Skip to content

feat: update CLI to add default tiles command#85

Merged
madclaws merged 3 commits intomainfrom
tiles-command
Feb 21, 2026
Merged

feat: update CLI to add default tiles command#85
madclaws merged 3 commits intomainfrom
tiles-command

Conversation

@feynon
Copy link
Copy Markdown
Member

@feynon feynon commented Feb 16, 2026

No description provided.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Feb 16, 2026

📝 Walkthrough

Walkthrough

The PR adds .DS_Store to .gitignore and updates tiles/src/main.rs to make the CLI subcommand optional: Cli.command changes from Commands to Option<Commands> and a flattened flags: RunFlags field is added. The main match now handles None as a default run path (building RunArgs from cli.flags) and updates all subcommand arms to match Some(Commands::...), extracting inner fields for Run, Health, Server, Memory, Optimize, and Account.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive No pull request description was provided by the author, making it impossible to evaluate whether the description relates to the changeset. Add a pull request description that explains the motivation and impact of making the CLI subcommand optional and adding default tiles command behavior.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: update CLI to add default tiles command' accurately describes the main change: making the CLI subcommand optional and adding default behavior when no subcommand is provided.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch tiles-command

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
tiles/src/main.rs (1)

101-120: Duplication between None and Run branches.

The None arm (lines 101–109) and the Some(Commands::Run { .. }) arm (lines 110–120) are nearly identical — both build a RunArgs and call commands::run. Consider extracting a small helper to reduce repetition.

♻️ Proposed refactor
-        None => {
-            // Running tiles without subcommand - launch default model with flags
-            let run_args = RunArgs {
-                modelfile_path: None,
-                relay_count: cli.flags.relay_count,
-                memory: cli.flags.memory,
-            };
-            commands::run(&runtime, run_args).await;
-        }
-        Some(Commands::Run {
-            modelfile_path,
-            flags,
-        }) => {
-            let run_args = RunArgs {
-                modelfile_path,
-                relay_count: flags.relay_count,
-                memory: flags.memory,
-            };
-            commands::run(&runtime, run_args).await;
-        }
+        None => {
+            let run_args = RunArgs::from_flags(None, cli.flags);
+            commands::run(&runtime, run_args).await;
+        }
+        Some(Commands::Run {
+            modelfile_path,
+            flags,
+        }) => {
+            let run_args = RunArgs::from_flags(modelfile_path, flags);
+            commands::run(&runtime, run_args).await;
+        }

With a small helper on RunArgs (or a free function):

impl RunArgs {
    fn from_flags(modelfile_path: Option<String>, flags: RunFlags) -> Self {
        Self {
            modelfile_path,
            relay_count: flags.relay_count,
            memory: flags.memory,
        }
    }
}

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 83bdec0f59

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread tiles/src/main.rs
@codecov
Copy link
Copy Markdown

codecov bot commented Feb 16, 2026

Codecov Report

❌ Patch coverage is 0% with 9 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
tiles/src/main.rs 0.00% 9 Missing ⚠️

📢 Thoughts on this report? Let us know!

@madclaws
Copy link
Copy Markdown
Member

madclaws commented Feb 18, 2026

@feynon Directing users to repl mode on tiles command means moving away from general CLI UX. Since usually when we run other cli binaries they show home page, which is a list of parent commands, which gives user's a birds-eye view on the tool. wdyt?

Also what was the notion behind this?

@feynon
Copy link
Copy Markdown
Member Author

feynon commented Feb 18, 2026

@feynon Directing users to repl mode on tiles command means moving away from general CLI UX. Since usually when we run other cli binaries they show home page, which is a list of parent commands, which gives user's a birds-eye view on the tool. wdyt?

Also what was the notion behind this?

It’s inspired by recent changes in Ollama’s CLI. On first run, without any arguments, a user will typically just run the tiles command. Instead of dropping them directly into REPL mode, we can show a concise overview that highlights both the Tiles REPL and Tilekit capabilities, like a launcher.

image

@madclaws
Copy link
Copy Markdown
Member

It’s inspired by recent changes in Ollama’s CLI. On first run, without any arguments, a user will typically just run the tiles command. Instead of dropping them directly into REPL mode, we can show a concise overview that highlights both the Tiles REPL and Tilekit capabilities, like a launcher.

This functionally kind of does the same thing but with a different UI, but this means we need to invest some time in CLI TUI UX. And as per our plan the focus will more towards web UI.

@feynon
Copy link
Copy Markdown
Member Author

feynon commented Feb 18, 2026

It’s inspired by recent changes in Ollama’s CLI. On first run, without any arguments, a user will typically just run the tiles command. Instead of dropping them directly into REPL mode, we can show a concise overview that highlights both the Tiles REPL and Tilekit capabilities, like a launcher.

This functionally kind of does the same thing but with a different UI, but this means we need to invest some time in CLI TUI UX. And as per our plan the focus will more towards web UI.

We should still invest in CLI/TUI I'd say.

@feynon
Copy link
Copy Markdown
Member Author

feynon commented Feb 19, 2026

It’s inspired by recent changes in Ollama’s CLI. On first run, without any arguments, a user will typically just run the tiles command. Instead of dropping them directly into REPL mode, we can show a concise overview that highlights both the Tiles REPL and Tilekit capabilities, like a launcher.

This functionally kind of does the same thing but with a different UI, but this means we need to invest some time in CLI TUI UX. And as per our plan the focus will more towards web UI.

The reasoning for working on TUI/CLI still is we should have at least one first party in house implemented end to end client as a reference.

@madclaws
Copy link
Copy Markdown
Member

The reasoning for working on TUI/CLI still is we should have at least one first party in house implemented end to end client as a reference.

Ok then closing this PR, as we will have a more focused TUI PR incoming..

@madclaws madclaws closed this Feb 19, 2026
@madclaws madclaws reopened this Feb 21, 2026
@madclaws
Copy link
Copy Markdown
Member

madclaws commented Feb 21, 2026

Merging this since, the change you made is what we want even in modern TUI. I didn't get it completely then until i played with codex CLI today.

And this kind of UX is good for doing our FTUE like account, nickname setup etc too. Like how codex asks to login to chatgpt

@madclaws madclaws merged commit b9944ad into main Feb 21, 2026
2 of 3 checks passed
@madclaws madclaws deleted the tiles-command branch February 21, 2026 12:04
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tiles/src/main.rs (1)

6-15: ⚠️ Potential issue | 🟠 Major

Missing args_conflicts_with_subcommands = true on root Cli — top-level RunFlags are silently accepted and discarded for all subcommands

RunFlags is flattened onto Cli (lines 13–14) and also exists inside Commands::Run (lines 24–26). Without #[command(args_conflicts_with_subcommands = true)] on Cli, clap will happily parse tiles -r 5 run or tiles -m health, populate cli.flags with those values, and then silently drop them — because all Some(Commands::…) arms never touch cli.flags. This is the exact pattern that ServerArgs, MemoryArgs, and AccountArgs (lines 69, 86, 99) all guard against by declaring args_conflicts_with_subcommands = true.

There are two ways to fix this:

Option A (preferred if the intent is that top-level flags are only for the no-subcommand path): Add args_conflicts_with_subcommands = true so clap rejects tiles -r 5 run with a clear error rather than silently ignoring the flag.

🛡️ Proposed fix — Option A
 #[derive(Debug, Parser)]
 #[command(name = "tiles")]
 #[command(version, about = "Your private and secure AI assistant for everyday use.", long_about = None, after_help = "Documentation: https://tiles.run/book\nReport issues: https://github.com/tilesprivacy/tiles/issues")]
+#[command(args_conflicts_with_subcommands = true)]
 struct Cli {
     #[command(subcommand)]
     command: Option<Commands>,

     #[command(flatten)]
     flags: RunFlags,
 }

Option B (if flags should be usable with any subcommand): Mark each RunFlags field with #[arg(global = true)] so they propagate down into subcommands and the Run arm reads from cli.flags rather than its own duplicate RunFlags.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tiles/src/main.rs` around lines 6 - 15, The root Cli struct flattens RunFlags
but lacks args_conflicts_with_subcommands, so top-level flags like RunFlags are
silently parsed and ignored when a subcommand (Commands::Run) is present; add
#[command(args_conflicts_with_subcommands = true)] to the Cli derive attributes
to make clap reject invocations like `tiles -r 5 run` (preferred), or
alternatively mark RunFlags fields with #[arg(global = true)] and remove the
duplicate RunFlags from Commands::Run so flags propagate into subcommands
(choose one approach and update the Cli and Commands::Run/RunFlags usages
accordingly).
🧹 Nitpick comments (1)
tiles/src/main.rs (1)

120-139: Inconsistent error handling: commands::run silently swallows errors while other branches propagate them

Both the None arm (line 127) and the Some(Commands::Run) arm (line 138) call commands::run, which discards the runtime error internally (let _ = runtime.run(run_args).await). The only user-visible signal is an eprintln! inside mlx.rs::run. By contrast, Some(Commands::Account) uses ? (line 161) to surface errors properly. Consider refactoring commands::run to return Result<()> and using ? here for consistency and to allow the process to exit with a non-zero status on failure.

♻️ Suggested refactor

In tiles/src/commands/mod.rs — change the signature to propagate the result:

-pub async fn run(runtime: &Runtime, run_args: RunArgs) {
-    let _ = runtime.run(run_args).await;
-}
+pub async fn run(runtime: &Runtime, run_args: RunArgs) -> Result<()> {
+    runtime.run(run_args).await
+}

Then in main.rs, both call sites become:

-            commands::run(&runtime, run_args).await;
+            commands::run(&runtime, run_args).await?;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tiles/src/main.rs` around lines 120 - 139, Change commands::run to return a
Result (e.g., Result<(), E> or anyhow::Result<()>) and stop swallowing the error
inside (propagate the error returned by runtime.run or mlx::run instead of let _
= ...). Update its signature in tiles/src/commands/mod.rs and propagate errors
from any internal awaits with ? so failures surface. Then update both call sites
in main.rs (the None arm and the Some(Commands::Run) arm that build a RunArgs
and call commands::run(&runtime, run_args)) to use ? on commands::run so errors
are propagated out of main (or convert main to return Result and use ?); ensure
top-level error handling exits with non-zero status if desired.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tiles/src/main.rs`:
- Around line 120-128: The None branch currently jumps straight into the default
model REPL by constructing RunArgs and calling commands::run(&runtime,
run_args); instead, update this branch to present the concise launcher/overview
UI first (or route to a dedicated launcher command) before entering interactive
mode: either call a new or existing launcher function (e.g.,
commands::launcher(&runtime, launcher_args)) or emit a structured overview
(title, available models, flags summary) and only after user selection call
commands::run with the constructed RunArgs; modify the None arm to invoke that
launcher/overview flow rather than directly calling commands::run.

---

Outside diff comments:
In `@tiles/src/main.rs`:
- Around line 6-15: The root Cli struct flattens RunFlags but lacks
args_conflicts_with_subcommands, so top-level flags like RunFlags are silently
parsed and ignored when a subcommand (Commands::Run) is present; add
#[command(args_conflicts_with_subcommands = true)] to the Cli derive attributes
to make clap reject invocations like `tiles -r 5 run` (preferred), or
alternatively mark RunFlags fields with #[arg(global = true)] and remove the
duplicate RunFlags from Commands::Run so flags propagate into subcommands
(choose one approach and update the Cli and Commands::Run/RunFlags usages
accordingly).

---

Nitpick comments:
In `@tiles/src/main.rs`:
- Around line 120-139: Change commands::run to return a Result (e.g., Result<(),
E> or anyhow::Result<()>) and stop swallowing the error inside (propagate the
error returned by runtime.run or mlx::run instead of let _ = ...). Update its
signature in tiles/src/commands/mod.rs and propagate errors from any internal
awaits with ? so failures surface. Then update both call sites in main.rs (the
None arm and the Some(Commands::Run) arm that build a RunArgs and call
commands::run(&runtime, run_args)) to use ? on commands::run so errors are
propagated out of main (or convert main to return Result and use ?); ensure
top-level error handling exits with non-zero status if desired.

Comment thread tiles/src/main.rs
Comment on lines +120 to +128
None => {
// Running tiles without subcommand - launch default model with flags
let run_args = RunArgs {
modelfile_path: None,
relay_count: cli.flags.relay_count,
memory: cli.flags.memory,
};
commands::run(&runtime, run_args).await;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

None branch runs the REPL directly — diverges from the stated launcher/overview goal

The PR discussion describes showing a concise overview ("launcher") screen when tiles is invoked with no arguments, similar to Ollama's CLI. The implementation instead drops directly into the default model REPL (commands::run), which is the UX concern madclaws raised. If this branch is revisited in the follow-up TUI PR, consider emitting a structured overview before entering interactive mode, or routing to a dedicated launcher command.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tiles/src/main.rs` around lines 120 - 128, The None branch currently jumps
straight into the default model REPL by constructing RunArgs and calling
commands::run(&runtime, run_args); instead, update this branch to present the
concise launcher/overview UI first (or route to a dedicated launcher command)
before entering interactive mode: either call a new or existing launcher
function (e.g., commands::launcher(&runtime, launcher_args)) or emit a
structured overview (title, available models, flags summary) and only after user
selection call commands::run with the constructed RunArgs; modify the None arm
to invoke that launcher/overview flow rather than directly calling
commands::run.

@coderabbitai coderabbitai bot mentioned this pull request Feb 25, 2026
This was referenced Mar 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants