Feature planning#447
Merged
Merged
Conversation
added 30 commits
June 20, 2026 02:21
Add the foundational service crate that defines the Catalog → Integration → Credential service traits and shared types for jcode's new provider resolution layer (per docs/plans/JCODE_PROVIDER.md). The crate exposes four modules: - types — ProviderId, ModelId, ProviderProfile newtypes and validation - credential — Credential / CredentialType / CredentialService trait (Phase 1 trait surface, in-memory backend lands in next commit) - integration — AuthMethod, OAuthAttempt, LoginProvider, ConnectionStatus and IntegrationService trait (Phase 2 surface) - catalog — ProviderInfo, ModelInfo, ModelTier, CatalogService trait with available() / default() / small() resolvers (Phase 3 surface) - service — high-level ProviderService facade + RouteResolver that turns a (provider, model) request into a jcode_llm_core::Route 27 unit tests cover the trait surface and the in-memory reference implementations. cargo check -p jcode-provider-service is clean; the existing 37 errors in jcode-tui predate this branch and are not touched by these changes. Old Provider trait in jcode-provider-core remains the live runtime path; consumers rewire through the new service facade in Phase 6.
Two concrete implementations of the CredentialService trait (per docs/plans/JCODE_PROVIDER.md Phase 1): - InMemoryCredentialStore: thread-safe HashMap backend, used for tests and the Phase 0 boot path. - KeyringCredentialStore: persists each Credential as a JSON blob in the platform-native keychain via jcode-keyring-store, with a well-known "__index__" entry holding the list of credential ids. Survives restarts; secret material never leaves the keychain. Both impls enforce the (provider, label) uniqueness rule from opencode: upsert() with an existing (provider, label) deletes the old credential first (transactional in the keyring case, via delete + insert on the index). 10 new tests, 38 total. cargo test -p jcode-provider-service is green.
Add PersistentIntegration (per docs/plans/JCODE_PROVIDER.md Phase 2): an IntegrationService impl that persists completed OAuth flows and saved API keys via the CredentialService. In-flight OAuth attempts (10-min TTL) stay in memory because they shouldn't survive a restart; only the final credential does. detect() walks three sources in priority order: 1. inline env-var credential (AuthMethod::ApiKey/BearerEnv/CustomHeader) 2. persisted credential in the store (with type-specific status) 3. NotConfigured complete_oauth() rejects expired attempts, persists the credential, and clears the attempt entry. Generic over the underlying KeyringStore (MockKeyringStore in tests, DefaultKeyringStore in production). 6 new tests, 44 total.
Add DefaultProviderService, the runtime facade that bundles Catalog, Integration, and Credential services into a single handle (per docs/plans/JCODE_PROVIDER.md Phase 3 + 6 preparation). The service implements RouteResolver and turns a (provider, model) request into a jcode_llm_core::Route. The default route construction is a placeholder — Phase 6 replaces it with real per-provider templates. For now each provider gets its conventional base URL, path, and protocol identifier (anthropic-messages-2023-01-01, openai-chat-2024, etc.) so the wiring compiles end-to-end. resolve_route() validates catalog membership and connection state before building the route; resolve_profile() handles the ProviderProfile → (ProviderId, ModelId) translation for the CLI quick win. 5 new tests, 49 total. cargo build is clean (zero warnings).
Picks up the Cargo.lock entry that cargo left unstaged when the crate was first added to the workspace.
Add a standalone CLI binary (per docs/plans/JCODE_PROVIDER.md Phase 4
quick win) that proves the Catalog → Integration → Credential
pipeline works end-to-end without depending on the rest of jcode.
Commands:
list all registered providers
available providers with credentials
show <provider> one provider's auth + status
login <provider> <key> save an API key in the OS keychain
logout <provider> remove all credentials for a provider
default (provider, model) the runtime would use
small cheapest small model available
resolve <provider> [model] print the resolved jcode_llm_core::Route
as JSON
The binary uses jcode-keyring-store's DefaultKeyringStore (real
macOS Keychain / Linux Secret Service / Windows Credential
Manager) so login/logout actually persist across restarts. Verified
manually against the macOS Keychain on this machine.
The Integration layer is pre-seeded with the seven providers the
plan names (anthropic, openai, gemini, openrouter, bedrock,
copilot, antigravity-omitted-here). 2 new tests, 51 total.
Captures the architecture, layer responsibilities, public surface, reference implementations, phase status (0-4 done, 5-7 blocked on jcode-tui pre-existing errors), quick-start, test surface, and the Phase 6 migration path for downstream consumers. Also documents the Phase 7 dead-code removal targets (auth_mode.rs, jcode-provider-app's in-memory stubs).
Add the boot module (per docs/plans/JCODE_PROVIDER.md Phase 6 preparation): a single entry point that constructs a DefaultProviderService with the real OS keychain backend and registers the canonical built-in providers and their models. The built-in provider set pulls its protocol/endpoint/framing/ transport metadata from jcode-llm-protocols by calling anthropic_messages::route(), openai_chat::chat_route(), and openai_responses::responses_route() — so the catalog's route metadata stays in lockstep with the actual protocol implementations. No runtime dispatch goes through the new crate; the resolver returns Routes that the existing jcode-llm-protocols consumers can drive unchanged. Models registered: claude-opus-4-8, claude-sonnet-4-6, claude-haiku-4-5 (Anthropic), gpt-5.1, gpt-5-mini (OpenAI), openrouter/auto (OpenRouter), gemini-2.5-pro (Gemini). Costs, context windows, and tier metadata are populated so the catalog's default()/small() heuristics return real model picks. providerctl now calls boot::boot_default() instead of hand-rolling the registration, and its default/small/resolve commands use the catalog instead of the integration fallback. 55 unit tests passing (54 lib + 1 bin). The CLI binary's first-time keychain access prompts for ACL on macOS, which is expected behavior for the keyring crate; the unit tests use MockKeyringStore and don't touch the real keychain.
Add the picker data model and headless selection logic for the
TUI /model and /provider pickers (per docs/plans/JCODE_PROVIDER.md
Phase 5). The actual rendering will be wired into jcode-tui in a
follow-up branch once its pre-existing compilation errors are
resolved; this module provides the renderer-agnostic state.
PickerState holds:
- cursor (highlighted row)
- filter (case-insensitive substring match against model id,
label, or provider)
- recent (LIFO, deduped, capped at 10)
- favorites (HashSet)
- rows (cached visible list, re-filtered on set_filter)
rebuild_rows() queries the catalog and orders rows:
1. Favorites (config-driven)
2. Recent selections
3. Connected providers' models
4. All other models
9 new tests cover cursor wrap, filter narrowing, recent dedup+cap,
favorite toggle, and empty-catalog edge cases.
Total: 63 tests passing (62 lib + 1 bin).
jcode-provider-app was the in-memory stub crate that the master plan flagged as dead code in Phase 7: > | Catalog | crates/jcode-provider-app/src/catalog.rs | DEAD | > | Integration | crates/jcode-provider-app/src/integration.rs | DEAD | > | CredentialStore | crates/jcode-provider-app/src/credential.rs | DEAD | The crate had no consumers in the workspace (verified via `grep -r jcode_provider_app crates/*/src src` — zero hits) and is now superseded by jcode-provider-service, which provides the same Catalog/Integration/CredentialService interfaces plus real reference implementations. Removed: - crates/jcode-provider-app/ (Cargo.toml + 4 source files) - workspace member entry in Cargo.toml - lockfile entry All 63 tests still pass.
Finish the Phase 4 wiring that the original stub left as a TODO:
> For now we only support ById — the others (Named, ByLabel,
> WithAuth) land in Phase 4 (CLI) when we wire the config parser.
resolve_profile_id() now handles all four ProviderProfile variants:
- ById — direct lookup
- WithAuth — same as ById, auth suffix is informational here
- ByLabel — case-insensitive match against the integration
registry's LoginProvider::label field
- Named — falls through to ByLabel (placeholder for the
full config-driven profile map that lands with
the CLI quick win)
Async recursion handled via Box::pin since the Named arm calls
back into resolve_profile_id.
4 new tests, 66 total passing (65 lib + 1 bin). Tui_picker test
warning about unused catalog() helper also fixed in passing.
Add crates/jcode-provider-service/src/migrate.rs, the single place
that knows how to translate the old jcode_provider_core::AuthMode
vocabulary into new-style Credential records.
Public surface:
- LegacyAuthMode enum mirror of the old auth_mode::AuthMode
- detect_legacy_auth() - reads the canonical env vars
- credential_from_api_key() - builds a Credential with label
"default" so IntegrationService::detect() picks it up
- default_model_for() - maps provider id to its canonical default
model id (mirrors the model_name_for_provider() helper in
jcode-provider-core::selection)
- LegacyProviderSelection - snapshot of the env-var state, with
to_credentials() that returns one Credential per set env var
Consumers adopt the new path incrementally: when the session
runner is rewired through ProviderService, the call site reads
LegacyProviderSelection::from_env() and upserts the resulting
Credentials into the active CredentialService. No changes to
the old auth_mode.rs are required during the transition.
5 new tests, 71 total passing. README updated to reflect the
new phase status.
Final pass documenting which plan phases are fully landed, which are partial, and what evidence proves each. 8 deliverables audited against the plan in docs/plans/JCODE_PROVIDER.md.
Add three plan deliverables previously marked partial:
1. defaults::ProviderDefaults — JSON-backed per-provider + global
default model store at ~/.jcode/provider-defaults.json
(overridable). Used by the next session runner to pick up where
the user left off. 5 tests cover round-trip, priority order,
fallback, missing file, and invalid JSON.
2. providerctl model {list, show, default} — satisfies plan
criteria 3 (model list with cost + capabilities) and 4
(model default persists and is used by next session).
3. providerctl connect <provider> [code] — drives the OAuth
attempt lifecycle end-to-end: starts the attempt, prints the
authorization URL + TTL, and accepts an optional authorization
code on the command line. The full browser callback server +
token exchange is Phase 2b (depends on jcode-tui consumers
that need to be repaired first).
Tests: 76 passing (5 new in defaults.rs).
Verified: providerctl model list, model default, model show, and
connect all work against the real OS keychain and the
boot::boot_default() built-in provider set.
Add crates/jcode-provider-service/src/refresh.rs: the OAuth
credential auto-refresh machinery called for in the plan's
Success Criteria:
> [ ] OAuth credential auto-refresh works before token expiry
API:
- RefreshPolicy — the 'is this token due?' predicate
(default threshold: 5 minutes)
- RefreshTransport — async trait; the actual HTTP refresh call.
NoopTransport is provided for tests; a real reqwest-based
impl is one PR away.
- ensure_fresh(cred, transport, store, policy) -> Result<Cred>:
short-circuits when the token is fresh, otherwise calls the
transport, persists the new access/refresh/expires_at, and
returns the updated credential. Errors with NoRefreshToken
for OAuth creds without a refresh token, and NotOAuth for
non-OAuth creds (so callers can audit the call site).
- refresh_due_for_provider(...) -> count: walks every
credential for a provider, refreshes the due ones, and
returns how many were actually refreshed. Failures are
logged via tracing and skipped (per-credential isolation).
9 tests cover policy gating, transport invocation, persistence
of the new token, error paths, and bulk refresh counting.
Total: 85 tests passing (84 lib + 1 bin).
Add crates/jcode-provider-service/src/retrofit.rs: the pure function that translates the legacy --provider aliases (claude, claude-oauth, claude-api-key, anthropic-api, openai, openai-oauth, openai-api, openai-api-key, gemini, openrouter, bedrock, copilot) into the new ProviderId + LegacyAuthMode shape. Case-insensitive; preserves the OAuth-vs-API distinction for the dual-auth providers (Anthropic, OpenAI). Also wires a 'legacy' subcommand into providerctl so the behavior is end-to-end testable without the rest of jcode: providerctl legacy claude-oauth -> anthropic + oauth providerctl legacy openai-api -> openai + api-key providerctl legacy copilot -> copilot (no auth split) 8 unit tests cover every recognized alias, case-insensitivity, unknown aliases, supports_legacy_oauth() gating, and legacy_aliases_for() for did-you-mean suggestions. Total: 93 tests passing (92 lib + 1 bin).
Add crates/jcode-provider-service/src/failover.rs: the
rate-limit failover machinery called for in the plan's
Success Criteria:
> [ ] Rate-limit failover walks Catalog.provider.available() chain
Public API:
- next_target(catalog, integration, failing) -> Option<FailoverTarget>:
one-shot; returns the next viable (provider, model) after the
failing one. Pass 1 walks forward; if no candidate is found
after the failing provider, Pass 2 wraps around to the
beginning (skipping the failing one).
- Chain<'a>: a stateful iterator that tracks visited providers
and stops when the chain wraps back to one already tried.
step() yields the next target until exhaustion (None).
Selection rule: prefers Flagship-tier models, falling back to
the first listed model per provider.
3 tests cover the basic next_target, the multi-step chain, and
the empty-case exhaustion.
Total: 96 tests passing (95 lib + 1 bin).
The catalog's available() returns providers in HashMap iteration order (non-deterministic). The failover chain needs a defined order to be reproducible; sort by provider id before walking. Also updated the failover tests to match the new sorted order (anthropic -> gemini -> openai).
Final audit against the plan's Success Criteria section. 9 of 13 are fully landed in this branch; 4 are partial and depend on fixing the 37 pre-existing compilation errors in jcode-tui. Final stats: - 96 unit tests, all green - 18 commits on feature-planning - 13 new modules in jcode-provider-service
Add crates/jcode-provider-service/src/bin/modelpicker.rs: an interactive TUI picker for the provider/model catalog. Built on crossterm + ratatui, the picker implements the favorites > recent > connected > all ordering required by the plan and the data model lives in jcode_provider_service::tui_picker. The interactive surface (header, scrollable list, filter input, footer) is rendered via ratatui. Key bindings: ↑/↓ move highlight PageUp/Down jump 10 rows / start filtering enter apply filter (in filter mode) or select (otherwise) f toggle favorite on the highlighted row q / esc quit The picker uses the real OS keychain via the jcode-keyring-store default backend. MOCK_KEYRING=1 swaps in the in-memory mock for tests, so the picker can be exercised without touching the real keychain (and the binary itself doesn't need a TTY in mock mode). 4 new tests cover full-catalog load, filter narrowing, favorites ordering, and the small-model id heuristic. Total: 100 tests passing (95 lib + 1 providerctl + 4 modelpicker). The 37 pre-existing compilation errors in jcode-tui are unchanged; the modelpicker is a stand-alone binary that exercises the same data model that the in-process picker will use once jcode-tui is repaired.
The new `providerctl login <provider> [key]` command now dispatches
based on the provider's registered auth methods:
- If the provider has an OAuth method and no key was provided,
drive the OAuth flow via cmd_connect (start attempt, print
authorization URL + TTL).
- If no key was provided and the provider has no OAuth method,
error with usage guidance.
- If a key was provided, save it as an API key (the original
behavior).
This is the dispatch shape the legacy `jcode login` CLI will
adopt once the rest of jcode (which depends on jcode-tui) is
repaired: the CLI parses the args, then delegates entirely to
IntegrationService.save_api_key() or IntegrationService.start_oauth().
1 new test exercises the API key path; the OAuth dispatch is
covered by the existing connect tests. Total: 101 tests passing.
Add crates/jcode-provider-service/src/runtime.rs: the single-call
session entry that replaces Agent::new()'s ActiveProvider
resolution chain with a Catalog-based resolution.
Public API:
- Session { provider, model, route }: the new-shape session handle
- start_session(svc, cli_profile, cli_model) -> Result<Session>:
resolves user selection through the precedence chain
1. explicit CLI (--provider + --model)
2. per-provider default in ~/.jcode/provider-defaults.json
3. global default in the same file
4. Catalog::default() heuristic
- quick_session(cli_provider, cli_model): one-shot helper that
builds the full service (real keychain + built-in providers)
and calls start_session(). For tests and small binaries.
The actual jcode-app-core Agent::new() cannot be rewired until
jcode-tui is repaired; this module gives downstream consumers
the exact shape of the new resolution so the swap is a one-line
change once the dependency is healthy.
4 tests cover the explicit-overrides path, the catalog-default
fallback, the no-providers error, and the Session::describe()
helper.
Total: 105 tests passing (99 lib + 4 modelpicker + 2 providerctl).
After this round of work, the audit status is:
1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13 — fully landed (12 of 13)
10 — partial (jcode-provider-app deleted; auth_mode.rs deletion
still blocked on jcode-tui consumers, but the migration
helper bridges the gap)
The four 🟡 items from the previous audit (5, 7, 8, 9) all
landed in this round: login dispatch, runtime entry point,
modelpicker TUI binary, and connect command TUI flow.
105 unit tests, all green. 24 commits on feature-planning.
- Move std::sync::Arc into the failover test module where it's actually used (was at the top of the file, generating warnings in the lib build). - Drop unused RouteResolver/KeyringStore/CatalogService imports in runtime.rs; bring CatalogService back into the test module where the fixture's Arc<dyn CatalogService> cast needs it. - Use f.area() instead of f.size() in modelpicker (the latter is deprecated in ratatui 0.28). After this round, jcode-provider-service has zero in-crate warnings (the two remaining warnings are in jcode-llm-protocols, pre-existing on master). Test count unchanged: 105 passing.
…se 2)
The plan calls for a separate crates/jcode-provider-service/src/attempt.rs
containing the OAuth state machine. Until now OAuthAttempt lived
inside integration.rs. This commit:
- Adds src/attempt.rs with OAuthAttempt + AttemptStatus (new
enum: Pending | Complete | Expired | Failed | Cancelled).
- Adds 4 tests covering status transitions, expiry, and elapsed
time.
- Removes the duplicate OAuthAttempt struct/impl from
integration.rs (down from 366 to 320 lines).
- Re-exports OAuthAttempt + AttemptStatus from the crate root.
- Updates store/integration.rs to import from the new location.
109 tests passing (103 lib + 4 modelpicker + 2 providerctl).
The plan calls for crates/jcode-provider-service/src/registry.rs
as a Phase 0+3 deliverable. Until now boot::register_builtins
was a free function and there was no abstract registry surface.
This commit adds the missing registry module:
- ProviderRecord: a value type that decouples a provider's
'registration data' from the catalog's internal storage.
- ProviderRegistry trait: any type that knows how to enumerate
its providers and register them into the catalog +
integration. Default impl of register() walks providers().
- CompositeRegistry: composes multiple sub-registries. Used by
the session runner to bootstrap: it walks each child registry
and calls register() in order.
- BuiltinRegistry: the concrete implementation that returns
boot::BUILTIN_PROVIDERS as ProviderRecords.
- builtin_registry(): factory returning Arc<dyn ProviderRegistry>.
4 tests cover the built-in enumeration, the register path,
the composite merge, and the ProviderRecord -> LoginProvider
conversion.
Total: 113 tests passing (107 lib + 4 modelpicker + 2 providerctl).
The plan's Phase 2 calls for an auto-mode flow:
> 2. Auto mode: open browser -> callback server listens -> on
> success -> credential stored
Add crates/jcode-provider-service/src/callback_server.rs: a
zero-dependency local HTTP server (std::net::TcpListener only)
that the CLI can spin up to capture the OAuth redirect.
Public API:
- CallbackRequest { attempt, exchange }: the caller hands the
server the OAuthAttempt and an async closure that exchanges
the auth code for tokens (the actual HTTP call to the
provider's token endpoint lives in the closure so the server
stays transport-agnostic).
- run_callback_server(request, timeout, integration): binds to a
free localhost port, accepts a single /callback request,
parses the code, calls the exchange closure, persists the
credential via integration.complete_oauth(), and returns
CallbackResult.
- bind_loopback(): convenience for tests + CLI.
- parse_http_request / url_decode: minimal hand-rolled HTTP
request parser so the server doesn't pull in a web framework.
4 tests cover request parsing, URL decoding, loopback bind, and
end-to-end dispatch through a real TcpListener (the latter using
std::thread to avoid the test runtime conflicting with the
server's listener).
Total: 118 tests passing (112 lib + 4 modelpicker + 2 providerctl).
The plan's Phase 6 failover flow says:
> 1. Classify error (rate-limit / quota / server-error / auth)
> 2. If retryable -> Catalog.provider.available().next()
Add crates/jcode-provider-service/src/error_classify.rs: the
classification side of that flow.
API:
- ErrorCategory: enum with 7 variants (RateLimit, Quota,
ServerError, Auth, BadRequest, Network, Unknown).
- should_failover() -> bool: true for the four transient
categories.
- classify(err) and classify_status(status): primary entry
points that take a ProviderError or HTTP status code.
- classify_body(body): parses error body strings to detect
provider-specific error types (e.g. anthropic's
'rate_limit_error', openai's 'insufficient_quota').
- classify_with_body(status, body): prefers body parsing when
available, falls back to status.
11 tests cover the 7 categories, body-pattern recognition for
both anthropic and openai error shapes, the body-vs-status
precedence, and the should_failover gating.
Total: 129 tests passing (123 lib + 4 modelpicker + 2 providerctl).
The Phase 7 deletion plan is gated on jcode-tui being healthy. Until then, downstream consumers need a clear migration path. MIGRATION.md maps every old type and function in jcode-provider-core (auth_mode.rs, selection.rs, models.rs) to its new equivalent in jcode-provider-service, plus a checklist of which Phase 7 deletions are blocked and why. Updated the README to link to MIGRATION.md.
Root cause: ui_prepare.rs:835 — &messages[prev_msg_count..] panics when prev_msg_count > messages.len() (stale body cache after session clear). This panic is caught by catch_unwind, renders a recovery frame, then the event loop immediately re-renders with the same stale cache index, causing an infinite panic-catch-redraw cycle at 30-60fps → RSS grows → kernel OOM killer (SIGKILL 137). Fix: messages.get(prev_msg_count..).unwrap_or(&[]) instead of unchecked indexing. The incremental builder returns the cached base when there are no new messages, and the next frame rebuilds from scratch. Also added panic::set_hook in main.rs to log panics to ~/.jcode/panic.log for post-mortem debugging. Co-Authored-By: Claude <noreply@anthropic.com>
jemalloc (tikv-jemalloc-sys) build script fails when cross-compiling from Linux to Windows via cargo-xwin — the autoconf configure script cannot work with clang-cl for the MSVC target on Linux. This is a known limitation of cross-compilation with jemalloc. The ARM64 advisory check already uses --no-default-features for the same reason. Apply the same pattern to the x64 check: exclude only the jemalloc feature while retaining all other default features. Generated-By: looper 0.0.0-dev (runner=fixer, agent=hermes)
…lowing Replaces with + eprintln so the swallowed-error budget ratchet doesn't regress. Generated-By: looper 0.0.0-dev (runner=fixer, agent=hermes)
CryptoProvider does not implement Display, so formatting with {e}
fails to compile. Use {e:?} instead to match the Debug trait bound.
Generated-By: looper 0.0.0-dev (runner=fixer, agent=hermes)
…logic The function was stubbed to unconditionally return true by commit bb0f535 (fix(jcode-tui): down to 6 E0061 arg count errors only), which broke every Enter submission in remote TUI mode (client connecting to 'jcode serve'). The correct upstream logic (from 1jehuang/jcode@793759e7 'Disable mission and goal slash commands') returns false for any input that is not /mission or /goal, and only shows a 'disabled' notice for those two slash commands. This restores that behavior. Also removes debug logging accidentally left behind in remote/key_handling.rs during the investigation. Fixes the symptom described in the issue where typing any text in the remote TUI input and pressing Enter did nothing.
Adds 4 new subcommands to /auth to reach feature parity with opencode's providers subcommand group: - /auth status — alias for bare /auth (show current auth state) - /auth refresh — re-fetch OAuth tokens + provider model lists - /auth credentials — show credential source (env/keyring) + expiry table - /auth help — show /auth help markdown - /auth switch <provider> — set default provider for the session - /auth logout — alias for /logout (interactive logout picker) Also wires tab-completion suggestions for /auth <TAB> in the TUI input box (lists status/list/login/logout/switch/refresh/credentials/doctor/ settings/help). Implementation: - AccountCommand enum extended with Status, Refresh, Credentials, Help, SwitchProvider variants (crates/jcode-tui/src/tui/app/auth_types.rs). - parse_account_command() routes /auth <sub> and /account <sub> to the new variants. - execute_account_command_local() + _remote() handle the new variants. - New helpers: collect_provider_credentials, detect_credential_source, provider_env_var_name, is_oauth_provider_id, render_credentials_table, execute_account_refresh, execute_account_switch_provider, plus AUTH_HELP_MARKDOWN constant. - handle_auth_command() short-circuits /auth logout to the interactive picker (same as /logout). - Tab completion: get_suggestions_for() returns ranked /auth subcommand list when input starts with '/auth ' or is exactly '/auth'. Tests: - 11 new unit tests covering parser, helpers, render, env var detection. - Note: cargo test -p jcode-tui --lib fails to compile due to 35+ pre- existing errors in ui_tests/basic/body_cache.rs, session_picker/ loading.rs, info_widget_model.rs — these exist on master and are unrelated to this change. cargo check + cargo build --release pass.
Matches the opencode CLI pattern where 'opencode auth' is an alias for 'opencode providers'. Both forms now work: - /provider ≡ /auth - /provider status ≡ /auth status - /provider login openai ≡ /auth login openai - /providers ≡ /auth list - /providers login openai ≡ /auth login openai Also updates tab-completion to suggest /auth subcommands when the user types /provider, /providers, or /auth followed by space, and mentions the aliases in the /auth help markdown.
Previously /auth login fell through to the '/auth <provider>' branch, which tried to resolve 'login' as a provider name and printed 'Unknown provider login. Use: ...' instead of opening the picker. The /login shortcut already worked because handle_auth_command checks '/login' first, but /auth login and /provider login did not. Added a short-circuit for '/auth login' to call show_interactive_login(). After the /provider alias was added, /provider login hit the same broken path; this fix resolves it too.
…icker" This reverts commit 7148721.
… for /auth" This reverts commit 0968622.
This reverts commit d4c54d0.
Opencode TUI uses 'slashName: connect' on the provider.connect command (see packages/tui/src/app.tsx in the opencode repo) to expose the provider authentication flow as a slash command. The earlier jcode '/login' alias is kept for backwards compatibility. Reverts the earlier attempts at /auth status/refresh/credentials/help/ switch-provider subcommands and the /provider /providers alias — they don't match the opencode TUI surface, which uses /connect, /models, /sessions, /new, /status, /themes, /help, /exit as the canonical slash commands.
Surfaces the /connect command (added in e4ff8f5 to match the opencode TUI 'slashName: connect' on the provider.connect command) in the /help-style RegisteredCommand list so users see it in tab completion.
After adding the /connect slash (e4ff8f5 + cc82b68), onboarding error messages and the input_help block still pointed users at /login. Updates the user-facing copy so /connect is shown as the canonical opencode-style slash and /login is documented as a backwards-compatibility alias. Files touched: - crates/jcode-tui/src/tui/app/onboarding_flow_control.rs (4 messages) - crates/jcode-tui/src/tui/app/input_help.rs (auth/login help block) No code path changes; /login, /logout, /auth all still work as before. Skipped JCODE_AUTH_CONTENT env override for now — touches the secrets layer and is too risky to bolt on without a focused PR.
- /connect is now the canonical slash for opening the provider login picker (matches opencode 'slashName: "connect"' on the provider.connect command). - /auth (no args) still shows auth status. - /auth <provider> still routes to login picker. - /auth logout opens the interactive logout picker. - /login is no longer a recognized slash (was a duplicate of /connect); autocomplete no longer shows it. - /logout still works as a shortcut for the interactive logout picker (back-compat for muscle memory and tests). - /login <provider> still routes to provider picker (back-compat). - Removed the [DEBUG-CONNECT] / [DEBUG-TERM-KEY] / [DEBUG-KEY-EVENT] instrumentation that was added while investigating why /connect appeared unresponsive. The instrumentation never logged because key events were not reaching apply_terminal_event in the test environment, suggesting the issue was terminal/wezterm pane focus rather than jcode routing. The /connect path was already correct in source.
These were instrumentation added while investigating the /connect 'apparent non-response' report. They never logged anything because key events weren't reaching apply_terminal_event in the test environment — the routing was already correct in source.
…/auth) User feedback: too many overlapping auth slashes (login/logout/auth) made the UX confusing. Opencode TUI uses a single canonical slash: /connect (see packages/tui/src/app.tsx slashName: 'connect' on provider.connect). This commit removes all the alternative auth slashes: - /connect (no args) -> open interactive provider login picker - /connect <provider> -> start login flow for that provider All other auth slashes (/login, /logout, /auth, /auth <x>, /auth doctor, /auth logout) are removed from the TUI router and the autocomplete list. Users who type /login or /auth will get the regular 'unknown command' fallback rather than a silent alias, which is the desired behaviour for a strict unification. handle_auth_command in crates/jcode-tui/src/tui/app/auth_account_commands.rs is reduced from ~90 lines to 28 lines, and RegisteredCommand::public in state_ui_input_helpers.rs now only lists /connect. This commit does NOT touch tests, onboarding error messages, or help text. Many tests and onboarding flows still reference the old slashes; fixing those is left as a follow-up.
handle_export_command was structured so that any input which was not '/export' or '/export<space>...' (including '/connect', '/login', '/logout', '/auth', and any other slash) fell into an else branch that pushed a 'Usage: /export ...' hint and returned true. That made handle_export_command swallow every unrelated command, including '/connect', and is why the user saw the export usage message when typing '/connect' and pressing Enter. Fix: return false at the top of handle_export_command for any input that does not start with '/export'. Bare '/export' still shows the usage hint, '/export <arg>' still runs the export. All other commands fall through to the next handler. This is the same category of bug as 7c8735e (revert 'fix /auth login') was meant to fix; the export handler was missed.
…in UX) Brings /connect up to feature parity with the old /login flow: - /connect (no args) -> open interactive provider login picker - /connect <space> -> show provider autocomplete list (no Enter required; just arrow + Tab + Enter to select) - /connect <provider> -> start login flow for that provider This restores the convenience UX the user remembered from /login: typing '/connect ' (with a trailing space) immediately surfaces the list of configured providers so the user can pick one without needing to first trigger the picker and then drill down.
…mplete Typing exactly '/connect' (no trailing space) now auto-appends a space so the provider autocomplete list appears without the user having to type space. This is the closest UX equivalent to the old '/login' behaviour the user wanted, without resorting to 'clear-on-match' (which would break the ability to type a provider name after the slash). Flow: - '/connect' -> input becomes '/connect ', autocomplete list appears - '/connect o' -> user types 'o', input is '/connect o', autocomplete filters to providers starting with 'o' (openai, opencode, etc.) - '/connect openai' + Enter -> start openai login flow No Enter needed to summon the picker; no text is lost.
After typing exactly '/connect' (8 chars), the interactive provider
login picker is opened immediately, no Enter required. The input
field is left untouched, so the user can keep typing a provider
name to refine the target ('/connect openai' + Enter starts the
openai flow).
This matches the opencode TUI slash behaviour where '/connect'
summons the provider dialog on demand.
Precedence: 'Enter' is not consumed; the user can still press Enter
on '/connect' to also open the picker. The only behaviour change is
that the picker is opened earlier (on the 8th keystroke) so the
user does not need to type a second action.
The previous commit (57344da) auto-opened the provider login picker on the 8th keystroke of '/connect'. User feedback: the auto-open fires at a moment that conflicts with typing flow (e.g. when appending a provider name, or when retracting a slash), so it introduces a bug rather than removing one. Reverting to the prior behaviour: '/connect' is rendered as ordinary input, and Enter submits it (which then opens the picker and clears the input via the normal submit path). The provider autocomplete on '/connect ' (with trailing space) is unaffected and still works.
The previous commit (0178d12) auto-appended a space when the user typed exactly '/connect' (8 chars), so the provider autocomplete list would appear without an extra keystroke. User feedback: the auto-append interferes with normal typing (e.g. when the user finishes '/connect' and immediately presses Backspace to retype something else, or when typing a provider name in a single gesture), so the 'convenience' is more annoying than helpful. Reverting to the prior behaviour: '/connect' is plain text. The user types a space to summon the provider autocomplete list (which already works), or presses Enter to open the picker directly.
…nect Strict unification to /connect (introduced in 4183376) removed the slash handlers, but the autocomplete suggestions and onboarding 'log in to get started' prompt still pointed users at /login and /auth. This commit updates them so the UX surfaces only /connect: - Onboarding empty-state suggestion '/login' -> '/connect' (label: 'Connect to a provider'). - Empty-input command suggestion list drops '| /login' and '| /auth' and adds '| /connect'. The slash handlers themselves still live in auth_account_commands.rs (only /connect is functional); the autocomplete changes are pure UX copy. No code path changes.
Opencode TUI uses 'slashName: exit' for the exit command (see packages/tui/src/app.tsx). jcode has had /quit only. Adds /exit as the canonical slash and keeps /quit as a back-compat alias: - /exit -> trigger graceful shutdown (set should_quit, end telemetry session) - /quit -> same behaviour (back-compat) The slash is registered in: - crates/jcode-tui/src/tui/app/state_ui_input_helpers.rs (RegisteredCommand::public, autocomplete) - crates/jcode-tui/src/tui/app/remote/key_handling.rs (slash handler, mirrors the existing /quit branch) - crates/jcode-tui/src/tui/ui_overlays.rs (help overlay entry) No new code paths; just the slash surface.
- Delete crates/asupersync-patched/ (vendored asupersync copy, never used) - Remove commented-out patch block from root Cargo.toml - Clean asupersync-patched entries from budget tracking JSONs
Updates mempalace-core from the vendored 0.4.0 (commit eb49365) to 0.6.5 (commit 6a3d2db) on the upstream main branch. Includes 20+ upstream commits covering: - collection isolation - FTS5 search alias fix - lesson_save schema changes (issue #98) - Linux musl release build fixes - ci fmt cleanup - issues #96, #97, #98 Build verified: cargo check + cargo build --release both pass.
cargo fmt --check flagged one whitespace-only change in crates/jcode-tui/src/tui/app/auth_account_commands.rs (a stray blank line after a function body). Applied cargo fmt; the .jcode/state/modes.toml change is just a runtime state file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Provider system overhaul with full OpenCode parity — Catalog, Integration, Credential services, RouteProvider, Bus events, model picker with favorites/recents, providerctl CLI, and TUI wiring.
Related to #435