Skip to content

fix(cli, workspace): Allow init and config commands to run without valid config#450

Merged
JeanMertz merged 6 commits intomainfrom
404-init-config
Mar 18, 2026
Merged

fix(cli, workspace): Allow init and config commands to run without valid config#450
JeanMertz merged 6 commits intomainfrom
404-init-config

Conversation

@rxgrant
Copy link
Copy Markdown
Collaborator

@rxgrant rxgrant commented Mar 14, 2026

Restructure the CLI startup pipeline so that commands like jp init, jp config show, and jp q help can 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:

  • Dispatch init, config show, and jp q help before the workspace-load-and-validate pipeline, so they never
    hit config or conversation errors.
  • Accept a fresh workspace with no conversations as a valid state, returning Ok(()) from Workspace::load
    instead of Error::NotFound.
  • Replace the --defaults dump in config show with a commented TOML skeleton derived from
    AppConfig::fields(), giving users a discoverable template of all available keys.
  • Write a valid config.toml during jp init with the user's chosen provider, model, and run mode, removing
    the leftover schema-generation scaffolding.

Fixes #404.

rxgrant added 4 commits March 14, 2026 18:57
`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.
@rxgrant rxgrant requested review from JeanMertz and parasyte March 14, 2026 21:00
`Workspace::load` no longer errors when no conversations
exist on disk, so the test now asserts `Ok(())` instead of
expecting `NotFound`.

Also satisfy clippy.
@rxgrant rxgrant changed the title resolve issue #404 init did not write to config to disk, and related init improvements fix(cli, workspace): Allow init and config commands to run without valid config Mar 14, 2026
Copy link
Copy Markdown
Collaborator

@JeanMertz JeanMertz left a comment

Choose a reason for hiding this comment

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

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 {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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 init to produce a working workspace with a schema-driven, auto-generated config.toml. The generated config includes documentation comments derived from AppConfig'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"),
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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 {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

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.

@JeanMertz JeanMertz merged commit b3692cf into main Mar 18, 2026
12 checks passed
@JeanMertz JeanMertz deleted the 404-init-config branch April 10, 2026 11:31
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.

from clean install, jp q fails with workspace error

2 participants