fix(cli, workspace): Allow init and config commands to run without valid config#450
fix(cli, workspace): Allow init and config commands to run without valid config#450
Conversation
`jp config show` went through the full startup pipeline including workspace load and config validation, causing it to fail when no config file sets `assistant.model.id`. This prevented users from inspecting defaults or listing themes before completing setup. `config show` is now dispatched before the full pipeline, like `init`. A `try_run_standalone` method on `Config` lets subcommands opt into early dispatch without workspace or validated config. Bare `config show` (no flags) now prints defaults instead of producing no output. Fixes: #404
`jp config show` previously serialized `PartialAppConfig::default()`, which is all `None` values, producing blank output. Users had no way to discover available configuration keys without reading docs. The command now generates a commented TOML skeleton from `AppConfig::fields()`, grouping keys by section with `[section]` headers and `# key =` entries. Both bare `config show` and `config show --defaults` display this skeleton.
`jp q help` was parsed as a query with the literal string "help", sending it through the full startup pipeline where it failed on missing workspace/config. Commands with subcommands (config, attachment, conversation) already get `help` handled by clap at parse time, but Query takes positional args so "help" fell through. Detect bare `help` as the sole query argument and print clap help for the subcommand without loading the workspace or validating config.
`jp init` wrote config using the struct field name `[conversation.tools.defaults]` but the field is `#[setting(rename = "*")]`, so the TOML key must be `'*'`. The mismatch caused the value to be silently captured as a tool config instead of populating the required defaults, making every subsequent `jp q` fail with a missing `conversation.tools.defaults.run` error. Additionally, `Workspace::load` treated "no conversations on disk" as a hard error, which is the expected state right after `jp init`. It now returns `Ok(())` with default state so that `jp q --new` can create the first conversation without a spurious ERROR log line.
`Workspace::load` no longer errors when no conversations exist on disk, so the test now asserts `Ok(())` instead of expecting `NotFound`. Also satisfy clippy.
48cdf16 to
44e4128
Compare
JeanMertz
left a comment
There was a problem hiding this comment.
As mentioned offline; there are some changes I want to make here, but the main fixes to unblock using jp init are worth getting into main as soon as possible. I will follow-up with further tweaks.
| } | ||
|
|
||
| /// Build a commented TOML skeleton showing all available config keys. | ||
| fn config_skeleton() -> String { |
There was a problem hiding this comment.
I like this as a first stab at this. I wrote an RFD that goes into more detail of the final version I want to work towards. But that end-state won't be here for a while, so this is a good starting point to build off of!
https://jp.computer/rfd/044-workspace-initialization
This RFD redesigns
jp initto produce a working workspace with a schema-driven, auto-generatedconfig.toml. The generated config includes documentation comments derived fromAppConfig's Rust doc comments, a curated whitelist of the most useful fields, and user-selected values for the model ID and tool run mode. The command supports both interactive and non-interactive use.
| pub(crate) fn run(self, ctx: &mut Ctx) -> Output { | ||
| match self.command { | ||
| Commands::Show(args) => args.run(ctx), | ||
| Commands::Show(_) => unreachable!("handled in standalone dispatch"), |
There was a problem hiding this comment.
This command will (eventually) require the full context, since it also wants to be able to show conversation specific configurations.
However, before that can happen, we have to have a "self-healing" system for our on-disk state, to avoid errors from aborting the run and not being able to run this command. Until then, this seems like a good trade-off.
| } | ||
|
|
||
| impl Query { | ||
| pub(crate) fn is_help_request(&self) -> bool { |
There was a problem hiding this comment.
This feels unintuitive to me.
I expect jp query help to send the query help to the assistant, not to stop the run and show the help text, since we already have --help and -h for that.
Having said that, I think we should be consistent, so what we should do is set Clap's disable_help_subcommand so that no subcommand responds to help by printing the help text.
I'll tweak this in a follow-up PR.
| return Err(Error::NotFound("Conversation", String::new())); | ||
| // Fresh workspace with no conversations yet (e.g. right after | ||
| // `jp init`). This is a valid state — keep defaults and let the | ||
| // first `jp q --new` create the initial conversation. |
There was a problem hiding this comment.
Ideally, after jp init, you aren't forced to use jp query --new, but can run jp query instead (in other words, an initial conversation should always be created after init, using the base config that you just configured when initializing the workspace.
I'll look into this in a follow-up PR, but for now, this does solve an actual bug in the init-flow that is worth patching.
Restructure the CLI startup pipeline so that commands like
jp init,jp config show, andjp q helpcan execute without requiring a fully loaded workspace or validated configuration. Previously these commands would fail in a freshly initialized project because loading a workspace with no conversations was treated as an error.Key changes:
hit config or conversation errors.
instead of Error::NotFound.
AppConfig::fields(), giving users a discoverable template of all available keys.
the leftover schema-generation scaffolding.
Fixes #404.