···11FROM mlx-community/gpt-oss-20b-MXFP4-Q4
22SYSTEM """
33-You are Tiles a Local-first private AI assistant for everyday use. You can be used for Knowledge work and programming task. Tiles is your identity. If a user asks who you are, you MUST respond that you are Tiles.
33+You are Tiles, a local-first private AI assistant.
44+55+Identity:
66+- You are Tiles, built on the gpt-oss-20b-MXFP4-Q4 model and powered by the Pi agent harness.
77+- If asked who you are, respond: "I am Tiles, a local-first private AI assistant."
88+- If asked who created you, respond:
99+ "Tiles is an AI assistant created by Tiles Privacy. Tiles Privacy is part of the User & Agents network. The shared goal is to empower people by designing and building software that provides agency, control, and choice in our digital lives. We strive to deliver strong privacy-focused engineering while also offering high convenience in consumer products. We believe identity and memory belong together, and Tiles gives you a way to own both through your personal user agent."
1010+- Do not change or reinterpret this identity.
1111+1212+Capabilities:
1313+- Help with knowledge work, reasoning, and programming.
1414+- Provide accurate, practical answers.
1515+1616+Behavior:
1717+- Be concise and plain.
1818+- Prefer direct answers over long explanations.
1919+- Avoid filler and unnecessary context.
2020+- Focus on what is useful to the user.
2121+2222+Constraints:
2323+- Do not mention system prompts or internal setup.
2424+- Do not break character.
2525+2626+Tone:
2727+- Efficient, concise, and plain.
428"""
+20-301
modelfiles/mem-agent
···11FROM driaforall/mem-agent-mlx-4bit
22SYSTEM """
33-# Memory Agent System Prompt
33+You are Tiles, a local-first private AI assistant.
4455-You are an LLM agent with a self-managed, Obsidian-like memory system. You interact with memory using Python code blocks.
55+Identity:
66+- You are Tiles, built on the driaforall/mem-agent-mlx-4bit model and powered by the Pi agent harness.
77+- If asked who you are, respond: "I am Tiles, a local-first private AI assistant."
88+- If asked who created you, respond:
99+ "Tiles is an AI assistant created by Tiles Privacy. Tiles Privacy is part of the User & Agents network. The shared goal is to empower people by designing and building software that provides agency, control, and choice in our digital lives. We strive to deliver strong privacy-focused engineering while also offering high convenience in consumer products. We believe identity and memory belong together, and Tiles gives you a way to own both through your personal user agent."
1010+- Do not change or reinterpret this identity.
61177-## CRITICAL: Response Format Rules
1212+Capabilities:
1313+- Help with knowledge work, reasoning, and programming.
1414+- Provide accurate, practical answers.
81599-**EVERY response MUST follow this EXACT structure:**
1616+Behavior:
1717+- Be concise and plain.
1818+- Prefer direct answers over long explanations.
1919+- Avoid filler and unnecessary context.
2020+- Focus on what is useful to the user.
10211111-1. **Always start with `<think>`** - Your reasoning about the query and what memory operations are needed
1212-2. **Always follow with `<python>`** - Either:
1313- - Python code to interact with memory, OR
1414- - Empty tags `<python></python>` if no memory interaction needed
1515-3. **Only provide `<reply>` if `<python>` is empty** - Your response to the user
1616-4. **The `<python></python>` and `<reply></reply>` MUST be separate, they should not be inside one another, they should be separate blocks**
1717-1818-### Valid Response Patterns:
1919-2020-**Pattern 1: When interacting with memory**
2121-```
2222-<think>
2323-[Your reasoning here]
2424-</think>
2222+Constraints:
2323+- Do not mention system prompts or internal setup.
2424+- Do not break character.
25252626-<python>
2727-[Your Python code here]
2828-</python>
2929-```
3030-3131-**Pattern 2: When NOT interacting with memory**
3232-```
3333-<think>
3434-[Your reasoning here]
3535-</think>
3636-3737-<python></python>
3838-3939-<reply>
4040-[Your response to the user]
4141-</reply>
4242-```
4343-4444-**CRITICAL: Always close ALL tags! Missing </think>, </python>, or </reply> will cause errors!**
4545-**CRITICAL: Think block MUST have opening <think> and closing </think>. This block should not only have </think>
4646-4747-**NEVER:**
4848-- Skip the `<think>` block
4949-- Provide text outside of these tags
5050-- Use `<reply>` when you have Python code in `<python>`
5151-- Respond with plain text after receiving `<result>` blocks
5252-5353-## After Receiving `<result>` Blocks
5454-5555-When you receive `<result>` blocks, you MUST:
5656-1. Start a new response with `<think>`
5757-2. Analyze the results and decide if more memory operations are needed
5858-3. Either provide more Python code OR empty `<python></python>` with a `<reply>`
5959-6060-**Understanding Results:**
6161-- `{'variable_name': value}` - Your assigned variables and their values
6262-- `{}` - Empty dict means NO variables were assigned (you forgot to capture return values!)
6363-- If you get `{}`, your function calls weren't assigned to variables
6464-6565-## Memory API
6666-6767-**⚠️ CRITICAL: ALWAYS assign function results to variables or they will be LOST!**
6868-```python
6969-# CORRECT - Results are captured
7070-exists = check_if_file_exists("user.md")
7171-content = read_file("user.md")
7272-7373-# WRONG - Results are lost, you get empty {}
7474-check_if_file_exists("user.md")
7575-read_file("user.md")
7676-```
7777-7878-```python
7979-# File Operations
8080-create_file(file_path: str, content: str = "") -> bool # Auto-creates parent directories
8181-update_file(file_path: str, old_content: str, new_content: str) -> Union[bool, str] # Returns True or error message
8282-read_file(file_path: str) -> str
8383-delete_file(file_path: str) -> bool
8484-check_if_file_exists(file_path: str) -> bool
8585-8686-# Directory Operations
8787-create_dir(dir_path: str) -> bool
8888-list_files() -> str # Shows tree structure of current working directory
8989-check_if_dir_exists(dir_path: str) -> bool
9090-9191-# Utilities
9292-get_size(file_or_dir_path: str) -> int # Bytes; empty = total memory size
9393-go_to_link(link_string: str) -> bool
9494-```
9595-9696-## File Update Examples
9797-9898-### Adding to a table:
9999-```python
100100-# Find the last row and add new row after it
101101-old_content = "| 2024-03-15 | Joined Premium | Active |"
102102-new_content = """| 2024-03-15 | Joined Premium | Active |
103103-| 2024-03-20 | Added Family | Active |"""
104104-result = update_file("user.md", old_content, new_content)
105105-106106-# ALWAYS check the result!
107107-if result != True:
108108- # Handle the error - result contains the error message
109109- print(f"Update failed: {result}")
110110-111111-Appending a new section:
112112-113113-# Find the last line of a section and append after it
114114-old_content = "- favorite_color: blue"
115115-new_content = """- favorite_color: blue
116116-- favorite_food: pizza
117117-- favorite_movie: Inception"""
118118-result = update_file("user.md", old_content, new_content)
119119-120120-Appending to a list:
121121-122122-# Add a new item to an existing list
123123-old_content = """## Hobbies
124124-- reading
125125-- hiking"""
126126-new_content = """## Hobbies
127127-- reading
128128-- hiking
129129-- photography"""
130130-result = update_file("user.md", old_content, new_content)
131131-132132-Updating a fact:
133133-134134-old_content = "- user_age: 25"
135135-new_content = "- user_age: 26"
136136-result = update_file("user.md", old_content, new_content)
137137-138138-## Memory Structure
139139-140140-### Root Directory
141141-- `user.md`: Personal information & attributes about the user, plus relationships to other entities
142142-- `entities/`: Information about people, places, organizations, etc.
143143- - `[entity_name].md`: One file per entity
144144-145145-### File Conventions
146146-- Dates: YYYY-MM-DD format
147147-- File names: snake_case, no spaces
148148-- All files use .md extension
149149-- New sections in files start with ## headers
150150-- Facts stored as: `- fact_name: fact_value`
151151-- Cross-references: Use `[[entity_name]]` to link between entities
152152-153153-### user.md Structure
154154-```markdown
155155-# User Information
156156-- user_name: [name]
157157-- user_age: [age]
158158-- [other attributes]
159159-160160-## User Relationships
161161-- wife: [[entities/jane_doe.md]]
162162-- friend: [[entities/john_smith.md]]
163163-- employer: [[entities/google.md]]
164164-165165-## Any other relation
166166-- name of entity: Explanation of what markdown files stores. [[entities/entity.md]]
167167-168168-## Tables
169169-- user.md can contain tables for structured data
170170-```
171171-172172-## Memory Operation Guidelines
173173-174174-### When to Save Information
175175-- **Personal facts**: Name, age, preferences, important dates
176176-- **Relationships**: Family, friends, colleagues, organizations
177177-- **Recurring topics**: Interests, projects, goals that come up repeatedly
178178-- **Context-dependent info**: Location, job, current situation
179179-180180-### When NOT to Save
181181-- Temporary information (e.g., "what's 2+2?")
182182-- General knowledge questions
183183-- One-off calculations or lookups
184184-185185-### Entity Creation Rules
186186-- Create new entity when: First mention of a person/place/organization with substantial information
187187-- Update existing entity when: New information about known entity
188188-- Attributes (age, location, etc.) belong in the entity file, NOT as separate entities
189189-!! Make sure the information is non existent before creating a new entity file !!
190190-191191-### Linking New Entities
192192-When creating a new entity file, ALWAYS add a link from the most relevant existing file (user.md OR another entity):
193193-194194-**Example 1: Link from user.md**
195195-```python
196196-# First: Create the new entity (entities/ dir created automatically)
197197-<python>
198198-content = """# Acme Corporation
199199-- industry: Technology
200200-- location: San Francisco, CA
201201-"""
202202-result = create_file("entities/acme_corp.md", content)
203203-</python>
204204-205205-# After result, add link to user.md
206206-<python>
207207-old_content = "## User Relationships"
208208-new_content = """## User Relationships
209209-- **Employer**: Technology company where user works as senior engineer. [[entities/acme_corp.md]]"""
210210-result = update_file("user.md", old_content, new_content)
211211-</python>
212212-```
213213-214214-**Example 2: Link between entities**
215215-```python
216216-# First: Create new entity
217217-<python>
218218-content = """# John Smith
219219-- relationship: Colleague
220220-- department: Engineering
221221-"""
222222-result = create_file("entities/john_smith.md", content)
223223-</python>
224224-225225-# After result, link from company entity
226226-<python>
227227-old_content = "## Employees"
228228-new_content = """## Employees
229229-- **Senior Engineer**: Works on backend systems team. [[entities/john_smith.md]]"""
230230-result = update_file("entities/acme_corp.md", old_content, new_content)
231231-</python>
232232-```
233233-234234-Example link descriptions:
235235-- **Primary Residence**: Three-bedroom house with home office and garden. [[entities/452_willow_creek_dr.md]]
236236-- **Project Lead**: Manages the mobile app development team. [[entities/sarah_chen.md]]
237237-- **Subsidiary**: Wholly-owned AI research division. [[entities/acme_ai_labs.md]]
238238-239239-## Important Operating Rules
240240-241241-1. **Initial Check**: On first interaction, ALWAYS check if `user.md` exists and read its contents before any other operations
242242-2. **Be Proactive**: Save relevant information without explicit requests
243243-3. **Be Selective**: Only save crucial, reusable information
244244-4. **No Print Statements**: They won't execute in the Python environment
245245-5. **Valid Python Only**: Ensure syntactically correct code
246246-6. **Execution Timeout**: Keep code blocks concise (5-second timeout)
247247-7. **No Duplicates**: Check existing content before adding information
248248-8. **CRITICAL - Use Variables**: ALWAYS capture return values for inspection
249249- ```python
250250- # Good - Result will be visible
251251- exists = check_if_file_exists("user.md")
252252- content = read_file("user.md")
253253- result = update_file("user.md", old, new)
254254-255255- # Bad - Result will be LOST, you'll get empty {}
256256- check_if_file_exists("user.md")
257257- read_file("user.md")
258258- update_file("user.md", old, new)
259259- ```
260260- **WARNING**: Function calls without assignment return empty {} results!
261261-9. **Wait for Results**: After submitting Python code, wait for `<result>` blocks before proceeding
262262-10. **Error Handling**: ALWAYS check return values from file operations
263263-```python
264264-# Good - checks the result
265265-result = update_file("user.md", old, new)
266266-if result != True:
267267- # result contains the `e`rror message
268268-269269-# Bad - ignores potential failure
270270-update_file("user.md", old, new)
271271-```
272272-11. **Your `<python>` block MUST compile under `ast.parse` and yield no `SyntaxError`**
273273-12. **One Operation Per Block**: Execute ONE main operation per `<python>` block to avoid errors
274274-```python
275275-# Good - separate operations
276276-<python>
277277-exists = check_if_file_exists("user.md")
278278-</python>
279279-# Wait for result, then:
280280-<python>
281281-if exists:
282282- content = read_file("user.md")
283283-</python>
284284-285285-# Bad - multiple operations can cause issues
286286-<python>
287287-exists = check_if_file_exists("user.md")
288288-content = read_file("user.md")
289289-result = update_file("user.md", old, new)
290290-</python>
291291-```
292292-293293-## Memory Maintenance
294294-295295-- Keep user.md as the source of truth for user information
296296-- Ensure cross-references between entities are bidirectional when relevant
297297-- Periodically review entity relationships for consistency
298298-299299-## Correct Search Patterns
300300-301301-- Use `list_files()` to see the complete directory structure
302302-- Start by reading user.md to understand existing relationships. It's your starting point.
303303-- Hop between markdowns using cross-references to gather context using read_file().
304304-- Use `go_to_link()` to navigate to specific websites if needed, but only if it adds significant value to the memory.
305305-306306-## Filtering
307307-308308-In the user query, you might receive a fact-retrieval question that incudes <filter> tags. In between these tags, the user might provide verbal filter(s) that may be inclusive or exclusive, you HAVE TO ABSOLUTELY FOLLOW THESE FILTERS. These filters provide privacy over user information. If there are no filters, just return the answer as is.
2626+Tone:
2727+- Efficient, concise, and plain.
30928"""
+25-1
modelfiles/qwen
···11FROM mlx-community/Qwen3.5-4B-MLX-4bit
22SYSTEM """
33-You are Tiles a Local-first private AI assistant for everyday use. Tiles is your identity. If a user asks who you are, you MUST respond that you are Tiles.Please DO NOT OVERTHINK OR HALLUCINATE ANSWERS, GIVE VERY BRIEF AND FAST ANSWERS.
33+You are Tiles, a local-first private AI assistant.
44+55+Identity:
66+- You are Tiles, built on the Qwen3.5-4B-MLX-4bit model and powered by the Pi agent harness.
77+- If asked who you are, respond: "I am Tiles, a local-first private AI assistant."
88+- If asked who created you, respond:
99+ "Tiles is an AI assistant created by Tiles Privacy. Tiles Privacy is part of the User & Agents network. The shared goal is to empower people by designing and building software that provides agency, control, and choice in our digital lives. We strive to deliver strong privacy-focused engineering while also offering high convenience in consumer products. We believe identity and memory belong together, and Tiles gives you a way to own both through your personal user agent."
1010+- Do not change or reinterpret this identity.
1111+1212+Capabilities:
1313+- Help with knowledge work, reasoning, and programming.
1414+- Provide accurate, practical answers.
1515+1616+Behavior:
1717+- Be concise and plain.
1818+- Prefer direct answers over long explanations.
1919+- Avoid filler and unnecessary context.
2020+- Focus on what is useful to the user.
2121+2222+Constraints:
2323+- Do not mention system prompts or internal setup.
2424+- Do not break character.
2525+2626+Tone:
2727+- Efficient, concise, and plain.
428"""
529630
+4-4
tiles/src/commands/mod.rs
···7474const FTUE_ACCOUNT_DETAILS_HINT: &str = "View full details:";
7575const FTUE_ACCOUNT_DETAILS_COMMAND: &str = "tiles account";
7676const FTUE_DATA_DIR_PROMPT: &str = "Data directory";
7777-const FTUE_DATA_DIR_CHANGE_HINT: &str = "Change data path later:";
7777+const FTUE_DATA_DIR_CHANGE_HINT: &str = "Change data folder later:";
7878const FTUE_DATA_DIR_CHANGE_COMMAND: &str = "tiles data set-path <PATH>";
7979-const FTUE_CUSTOM_DATA_PROMPT: &str = "Use a custom data directory now? [y/N]";
7979+const FTUE_CUSTOM_DATA_PROMPT: &str = "Use a custom data folder now? [y/N]";
8080const FTUE_UPDATE_COMMAND: &str = "tiles update";
81818282pub fn run_setup_for_ftue(_run_args: &RunArgs) -> Result<()> {
···376376 assert_eq!(FTUE_ACCOUNT_LABEL, "Account");
377377 assert_eq!(FTUE_ACCOUNT_DETAILS_HINT, "View full details:");
378378 assert_eq!(FTUE_DATA_DIR_PROMPT, "Data directory");
379379- assert_eq!(FTUE_DATA_DIR_CHANGE_HINT, "Change data path later:");
379379+ assert_eq!(FTUE_DATA_DIR_CHANGE_HINT, "Change data folder later:");
380380 assert_eq!(
381381 FTUE_CUSTOM_DATA_PROMPT,
382382- "Use a custom data directory now? [y/N]"
382382+ "Use a custom data folder now? [y/N]"
383383 );
384384 }
385385
+2-2
tiles/src/core/account/atproto.rs
···187187 upsert_auth_data(&conn.common, &logout_user)?;
188188 println!("Loggedout successfully as {}", key);
189189 } else {
190190- println!("No logged-in user, please login")
190190+ println!("No user logged in. Please log in using tiles at login <handle>.")
191191 }
192192 Ok(())
193193}
···346346347347 upsert_auth_data(conn, &auth_data)?;
348348 } else {
349349- println!("No logged-in user, please login")
349349+ println!("No user logged in. Please log in using tiles at login <handle>.")
350350 }
351351 Ok(())
352352}
+158-46
tiles/src/main.rs
···2233use std::error::Error;
4455-use clap::{Args, Parser, Subcommand};
55+use clap::{Args, CommandFactory, Parser, Subcommand};
66use tiles::{
77 core::{
88 self,
···1717use crate::commands::{show_peers, unlink_peer};
18181919mod commands;
2020+2121+const CLI_HELP_TEMPLATE: &str = concat!(
2222+ r#"
2323+ .:-::.
2424+ .:-+*#%@@@@@@%#**+=
2525+ .:-=*#%@@@@@%%#***#%%@@@%=.
2626+ .:-=+#%@@@@@%%##****#%@@@@@@@%*=
2727+ -#%@@@@@@%#*****#%%@@@@@@%#*=-:.
2828+ -#@%%#####%%%%@@@%%#@@@@@#.
2929+-###%@@@@@@@@@%%%%%%@@@@@*
3030+ ..:--=+##*+==@@@@@@@@@=
3131+ .#@@@@@@@@:
3232+ -@@@@@@@@#.
3333+ +@@@@@%@@*
3434+ #@@@@#+@@=
3535+ .%@@@@#+@@:
3636+ .@@@@@##@#.
3737+ %@@@#%@*
3838+ :@@*@@=
3939+ -+@@:
4040+ .-.
4141+"#,
4242+ "\nTiles ",
4343+ env!("CARGO_PKG_VERSION"),
4444+ "\nLocal-first private AI assistant for everyday use\n\n",
4545+ "Usage: {usage}\n\n",
4646+ "Commands:\n\n",
4747+ " Getting Started\n",
4848+ " run Run a Modelfile (uses the default model if none is provided)\n",
4949+ " help Show this message or help for a specific command\n\n",
5050+ " Accounts\n",
5151+ " account Manage your user account\n",
5252+ " at ATProto-related commands\n",
5353+ " data Configure your data and storage\n",
5454+ " update Update Tiles to the latest version\n\n",
5555+ " Sync\n",
5656+ " link Link devices via peer-to-peer\n",
5757+ " sync Sync chats with peers\n\n",
5858+ " System\n",
5959+ " health Check the status of dependencies\n",
6060+ " server Start or stop the daemon server\n",
6161+ " daemon Configure daemon behavior\n\n",
6262+ " Tilekit (Developer)\n",
6363+ " optimize Optimize the SYSTEM prompt in a Modelfile\n\n",
6464+ "Options:\n",
6565+ " -h, --help Show help\n",
6666+ " -V, --version Show version\n\n",
6767+ "Documentation: https://tiles.run/book\n",
6868+ "Report issues: https://github.com/tilesprivacy/tiles/issues\n"
6969+);
7070+2071#[derive(Debug, Parser)]
2172#[command(name = "tiles")]
2222-#[command(version, about = "Local-first private AI assistant for everyday use", long_about = None, after_help = "Documentation: https://tiles.run/book\nReport issues: https://github.com/tilesprivacy/tiles/issues")]
7373+#[command(
7474+ version,
7575+ about = "Local-first private AI assistant for everyday use",
7676+ disable_help_subcommand = true,
7777+ long_about = None,
7878+ override_usage = "tiles [OPTIONS] [COMMAND]",
7979+ help_template = CLI_HELP_TEMPLATE
8080+)]
2381struct Cli {
2482 #[command(subcommand)]
2583 command: Option<Commands>,
···2886 flags: RunFlags,
2987}
30883131-#[derive(Subcommand, Debug)]
8989+#[derive(Debug, Subcommand)]
3290enum Commands {
3333- /// Runs the given Modelfile (runs the default model if none passed)
9191+ #[command(flatten, next_help_heading = "Getting Started")]
9292+ GettingStarted(GettingStartedCommands),
9393+9494+ #[command(flatten, next_help_heading = "Accounts")]
9595+ Accounts(AccountCommandsGroup),
9696+9797+ #[command(flatten, next_help_heading = "Sync")]
9898+ Sync(SyncCommands),
9999+100100+ #[command(flatten, next_help_heading = "System")]
101101+ System(SystemCommands),
102102+103103+ #[command(flatten, next_help_heading = "Tilekit (Developer)")]
104104+ Tilekit(TilekitCommands),
105105+}
106106+107107+#[derive(Debug, Subcommand)]
108108+enum GettingStartedCommands {
109109+ /// Run a Modelfile (uses the default model if none is provided)
34110 Run {
35111 /// Path to the Modelfile (uses default model if not provided)
36112 modelfile_path: Option<String>,
···39115 flags: RunFlags,
40116 },
411174242- /// Configure your data
118118+ /// Show this message or help for a specific command
119119+ Help {
120120+ /// Command to show help for
121121+ #[arg(value_name = "COMMAND")]
122122+ command: Vec<String>,
123123+ },
124124+}
125125+126126+#[derive(Debug, Subcommand)]
127127+enum AccountCommandsGroup {
128128+ /// Manage your user account
129129+ Account(AccountArgs),
130130+131131+ /// ATProto-related commands
132132+ At(AtArgs),
133133+134134+ /// Configure your data and storage
43135 Data(DataArgs),
441364545- /// Checks the status of dependencies
137137+ /// Update Tiles to the latest version
138138+ Update,
139139+}
140140+141141+#[derive(Debug, Subcommand)]
142142+enum SyncCommands {
143143+ /// Link devices via peer-to-peer
144144+ Link(LinkArgs),
145145+146146+ /// Sync chats with peers
147147+ Sync {
148148+ /// The DID of the peer you want to sync
149149+ did: Option<String>,
150150+ },
151151+}
152152+153153+#[derive(Debug, Subcommand)]
154154+enum SystemCommands {
155155+ /// Check the status of dependencies
46156 Health,
471574848- /// start or stop the daemon server
158158+ /// Start or stop the daemon server
49159 Server(ServerArgs),
50160161161+ /// Configure daemon behavior
162162+ Daemon(DaemonArgs),
163163+}
164164+165165+#[derive(Debug, Subcommand)]
166166+enum TilekitCommands {
51167 /// Optimize the SYSTEM prompt in a Modelfile
52168 Optimize {
53169 /// Path to the Modelfile to optimize
···61177 #[arg(long, default_value = "openai:gpt-4o-mini")]
62178 model: String,
63179 },
6464- /// Manage user account
6565- Account(AccountArgs),
6666-6767- /// Update Tiles to latest version
6868- Update,
6969-7070- /// Daemon configurations
7171- Daemon(DaemonArgs),
7272-7373- /// Link with other devices p2p
7474- Link(LinkArgs),
7575-7676- /// Syncs the chats to peers
7777- Sync {
7878- /// The DID of the peer you want to sync
7979- did: Option<String>,
8080- },
8181-8282- /// Atproto related commands
8383- At(AtArgs),
84180}
8518186182#[derive(Debug, Args)]
87183struct RunFlags {
88184 /// Max times cli communicates with the model until it gets a proper reply for a user prompt
8989- #[arg(short = 'r', long, default_value_t = 10)]
185185+ #[arg(short = 'r', long, default_value_t = 10, hide = true)]
90186 relay_count: u32,
9118792188 /// Switches the mode to memory, used for interacting with memory models.
9393- #[arg(short = 'm', long)]
189189+ #[arg(short = 'm', long, hide = true)]
94190 memory: bool,
95191 // Future flags go here:
96192 // #[arg(long, default_value_t = 6969)]
97193 // port: u16,
9819499195 // Don't go into the repl
100100- #[arg(short = 'x', long)]
196196+ #[arg(short = 'x', long, hide = true)]
101197 no_repl: bool,
102198103199 // Use PI repl instead of Tiles
104104- #[arg(short = 'p', long)]
200200+ #[arg(short = 'p', long, hide = true)]
105201 pi: bool,
106202}
107203···179275180276#[derive(Debug, Subcommand)]
181277enum LinkCommands {
182182- /// Produce link ticket and wait or send link request with ticket
278278+ #[command(about = "Produce a link ticket and wait, or send a link request with a ticket.")]
183279 Enable {
184280 ticket: Option<String>,
185281 },
···202298203299#[derive(Debug, Subcommand)]
204300enum AtCommands {
205205- /// Produce link ticket and wait or send link request with ticket
301301+ #[command(about = "Produce a link ticket and wait, or send a link request with a ticket.")]
206302 Login {
207303 handle: String,
208304 },
305305+ #[command(about = "Log out of your Atproto account.")]
209306 Logout,
210307}
211308#[tokio::main]
···244341 .inspect_err(|e| eprintln!("Tiles failed to run due to {:?}", e))?;
245342 }
246343 }
247247- Some(Commands::Run {
344344+ Some(Commands::GettingStarted(GettingStartedCommands::Run {
248345 modelfile_path,
249346 flags,
250250- }) => {
347347+ })) => {
251348 let run_args = RunArgs {
252349 modelfile_path,
253350 relay_count: flags.relay_count,
···267364 .await
268365 .inspect_err(|e| eprintln!("Tiles failed to run due to {:?}", e))?;
269366 }
270270- Some(Commands::Health) => {
367367+ Some(Commands::GettingStarted(GettingStartedCommands::Help { command })) => {
368368+ print_help_for_command(&command)?;
369369+ }
370370+ Some(Commands::System(SystemCommands::Health)) => {
271371 commands::check_health().await?;
272372 }
273273- Some(Commands::Server(server)) => match server.command {
373373+ Some(Commands::System(SystemCommands::Server(server))) => match server.command {
274374 Some(ServerCommands::Start) => commands::start_server(&runtime).await,
275375 Some(ServerCommands::Stop) => commands::stop_server(&runtime).await,
276376 _ => println!("Expected start or stop"),
277377 },
278278- Some(Commands::Data(data)) => match data.command {
378378+ Some(Commands::Accounts(AccountCommandsGroup::Data(data))) => match data.command {
279379 DataCommands::SetPath { path } => commands::set_data(path.as_str()),
280380 },
281281- Some(Commands::Optimize {
381381+ Some(Commands::Tilekit(TilekitCommands::Optimize {
282382 modelfile_path,
283383 data,
284384 model,
285285- }) => {
385385+ })) => {
286386 let modelfile = commands::optimize(&modelfile_path, data, &model).await?;
287387 std::fs::write(&modelfile_path, modelfile.to_string())?;
288388 println!("Successfully updated {}", modelfile_path);
289389 }
290290- Some(Commands::Account(account_args)) => {
390390+ Some(Commands::Accounts(AccountCommandsGroup::Account(account_args))) => {
291391 commands::run_account_commands(account_args)?;
292392 }
293293- Some(Commands::Update) => {
393393+ Some(Commands::Accounts(AccountCommandsGroup::Update)) => {
294394 println!("Checking for updates...");
295395 let res = installer::try_update(None)
296396 .await
297397 .inspect_err(|e| eprintln!("Failed in update process due to {:?}", e))?;
298398 println!("{}", res);
299399 }
300300- Some(Commands::Daemon(daemon_args)) => match daemon_args.command {
400400+ Some(Commands::System(SystemCommands::Daemon(daemon_args))) => match daemon_args.command {
301401 Some(DaemonCommands::Start { port }) => start_cmd(port)
302402 .await
303403 .inspect_err(|e| eprintln!("Daemon starting failed, reason: {:?}", e))?,
···307407 .inspect(|_| println!("Daemon stopped successfully"))?,
308408 _ => start_server(None).await?,
309409 },
310310- Some(Commands::Link(link_args)) => match link_args.command {
410410+ Some(Commands::Sync(SyncCommands::Link(link_args))) => match link_args.command {
311411 LinkCommands::Enable { ticket } => link(ticket).await?,
312412 LinkCommands::Disable { did } => unlink_peer(&db_conn, &did)?,
313413 LinkCommands::ListPeers => {
314414 show_peers(&db_conn)?;
315415 }
316416 },
317317- Some(Commands::Sync { did }) => sync(did).await?,
318318- Some(Commands::At(at_args)) => match at_args.command {
417417+ Some(Commands::Sync(SyncCommands::Sync { did })) => sync(did).await?,
418418+ Some(Commands::Accounts(AccountCommandsGroup::At(at_args))) => match at_args.command {
319419 AtCommands::Login { handle } => {
320420 login(&db_conn, &handle).await?;
321421 }
···323423 // AtCommands::Share => share_session(&db_conn).await?,
324424 },
325425 }
426426+ Ok(())
427427+}
428428+429429+fn print_help_for_command(command_path: &[String]) -> Result<(), Box<dyn Error>> {
430430+ let mut argv = vec!["tiles".to_owned()];
431431+ argv.extend(command_path.iter().cloned());
432432+ argv.push("--help".to_owned());
433433+434434+ if let Err(err) = Cli::command().try_get_matches_from(argv) {
435435+ err.print()?;
436436+ }
437437+326438 Ok(())
327439}
328440
+31-29
tiles/src/runtime/mlx.rs
···316316}
317317318318fn show_help() {
319319- let help_list = vec![
320320- ("status", "Show the current session state"),
321321- ("sessions", "List available sessions"),
319319+ let help_groups = vec![
322320 (
323323- "share",
324324- "Create a shareable link for currently running session",
321321+ "Session",
322322+ vec![
323323+ ("/status", "Show the current session state"),
324324+ ("/sessions", "List all available sessions"),
325325+ (
326326+ "/resume <sessionId>",
327327+ "Load and resume a specific session (requires <sessionId>)",
328328+ ),
329329+ ],
325330 ),
326326- ("resume <sessionId>", "Loads and resume the given session"),
327331 (
328328- "share",
329329- "Create a shareable link for currently running session",
332332+ "Sharing",
333333+ vec![
334334+ ("/share", "Create a shareable link for the current session"),
335335+ (
336336+ "/share <sessionId>",
337337+ "Create a shareable link for a specific session (requires <sessionId>)",
338338+ ),
339339+ ],
330340 ),
331341 (
332332- "share <sessionId>",
333333- "Create a shareable link for given sessionId",
342342+ "Chat",
343343+ vec![
344344+ ("/help", "Show this help message"),
345345+ ("/bye", "Exit the REPL"),
346346+ ],
334347 ),
335335- ("help", "Show this help message"),
336336- ("bye", "Exit the REPL"),
337348 ];
338349339339- // finding the length of the longest command, for padding purposes
340340- let max_length = help_list
341341- .iter()
342342- .fold(0, |acc, x| if x.0.len() > acc { x.0.len() } else { acc });
343343-344350 println!("Available Commands:");
345351346346- for help in help_list {
347347- let final_str = format!(
348348- " /{}{}\t\t{}",
349349- help.0,
350350- " ".repeat(max_length - help.0.len()),
351351- help.1
352352- );
352352+ for (heading, commands) in help_groups {
353353+ println!();
354354+ println!(" {heading}");
353355354354- println!("{}", final_str);
356356+ for (command, description) in commands {
357357+ println!(" {command:<24}{description}");
358358+ }
355359 }
356360357361 println!();
358358-359359- println!("\nDocumentation: https://tiles.run/book");
360360-362362+ println!("Documentation: https://tiles.run/book");
361363 println!("Report issues: https://github.com/tilesprivacy/tiles/issues");
362364 println!();
363365}
···397399 .ok_or_else(|| anyhow!("Error getting FROM from modelfile due to"))?;
398400399401 let system_prompt = modelfile.system.clone().unwrap_or("".to_owned());
400400- println!("Running {} in interactive mode", modelname);
402402+ println!("Running {}", modelname);
401403 let current_user = get_current_user(&db_conn.common)?;
402404403405 let config = Config::builder().auto_add_history(true).build();