mirror of
https://github.com/codewhale-hq/Codewhale.git
synced 2026-11-08 08:04:25 -10:00
fix: unknown --profile fails config commands, MCP reload panel refreshes, and CI reds from 34bf6568f2
- cli: 'codewhale --profile <missing> config <cmd>' now exits 1 naming the available profiles instead of printing the unrelated config. - tui: /mcp reload marks MCP initializing until the supervised pass finishes, so the open Extensions panel re-reads the settled snapshot instead of staying on interim not-connected rows. - cucumber PTY tests: 14 macOS-only failures came from tests that waited for the launch-time provider picker, which composer-first removed. Launch-card tests now start on the card; the three that need a live offline shell reach it by sending a first message, choosing Explore Offline and clearing the returned draft; the pointer test no longer sends a prompt whose 'Saved for later' status made its no-receipt baseline false. - runtime-contract budget regenerated for the task_shell_start removal (Act tools 52 -> 51, bytes and tokens only shrink); Lint failed on the identity change. Evidence: nextest --test cucumber --features long-running-tests on launch_card_pty|active_composer_pointer_pty|screen_mode_inline_pty|work_bar_keys_pty: 14 tests run: 14 passed, 0 failed. Focused cli+tui mcp/profile filter: 215 passed. Binary check: --profile bogus config list exits 1; plain config list exits 0. cargo fmt --check clean. Not run: full suite, clippy for this slice. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
This commit is contained in:
1 parent
26249dfdb7
commit
94549b79e0
7 files changed
+61
-89
No files matched your search
@@ -2618,6 +2618,9 @@ fn run() -> Result<()> {
|
||||
)
|
||||
}
|
||||
Some(Commands::Config(args)) => {
|
||||
if let Some(profile) = cli.profile.as_deref() {
|
||||
require_config_profile(store.path(), profile)?;
|
||||
}
|
||||
let resolved_runtime =
|
||||
resolve_runtime_for_diagnostic_dispatch(&store, &runtime_overrides);
|
||||
let session = start_cli_telemetry(
|
||||
@@ -5233,6 +5236,28 @@ fn run_auth_migrate(store: &mut ConfigStore, secrets: &Secrets, dry_run: bool) -
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn require_config_profile(path: &Path, profile: &str) -> Result<()> {
|
||||
let table = std::fs::read_to_string(path)
|
||||
.ok()
|
||||
.and_then(|text| toml::from_str::<toml::Table>(&text).ok());
|
||||
let mut available = table
|
||||
.as_ref()
|
||||
.and_then(|table| table.get("profiles"))
|
||||
.and_then(toml::Value::as_table)
|
||||
.map(|profiles| profiles.keys().cloned().collect::<Vec<_>>())
|
||||
.unwrap_or_default();
|
||||
if available.iter().any(|name| name == profile) {
|
||||
return Ok(());
|
||||
}
|
||||
available.sort();
|
||||
let available = if available.is_empty() {
|
||||
"none".to_string()
|
||||
} else {
|
||||
available.join(", ")
|
||||
};
|
||||
bail!("Profile not found (name not shown). Available profiles: {available}")
|
||||
}
|
||||
|
||||
fn run_config_command(
|
||||
store: &mut ConfigStore,
|
||||
command: ConfigCommand,
|
||||
|
||||
@@ -1135,6 +1135,7 @@ pub(crate) async fn handle_mcp_ui_action(
|
||||
// the reload-started line.
|
||||
app.mcp_reload_required = false;
|
||||
app.mcp_reload_in_flight = true;
|
||||
app.mcp_initializing = true;
|
||||
if is_reload {
|
||||
add_mcp_message(
|
||||
app,
|
||||
|
||||
@@ -81,23 +81,6 @@ fn run_pointer_submit_case(rows: u16, cols: u16) {
|
||||
.spawn()
|
||||
.expect("start distributed TUI binary");
|
||||
|
||||
// Walk the real onboarding: provider choice → Explore Offline (Ctrl+O)
|
||||
// → ready screen → Enter into Tideline Startup → New session.
|
||||
wait_or_panic(
|
||||
&mut tui,
|
||||
"Choose your model provider",
|
||||
STARTUP_WAIT,
|
||||
&format!("{size}: onboarding provider choice"),
|
||||
);
|
||||
tui.send(keys::key::ctrl('o'))
|
||||
.expect("choose Explore Offline");
|
||||
wait_or_panic(
|
||||
&mut tui,
|
||||
"You're ready.",
|
||||
SETTLE_WAIT,
|
||||
&format!("{size}: offline explore ready"),
|
||||
);
|
||||
tui.send(keys::key::enter()).expect("leave onboarding");
|
||||
// PTY reads can split a redraw: the launch header arrives before the
|
||||
// composer, with onboarding rows still on screen (Buildkite #1861/#1867).
|
||||
// Wait for the input surface as well as the header before asserting it.
|
||||
@@ -111,21 +94,7 @@ fn run_pointer_submit_case(rows: u16, cols: u16) {
|
||||
.unwrap_or_else(|error| panic!("{size}: show the launch card and composer: {error}"));
|
||||
tui.pump();
|
||||
assert_startup_contract(tui.frame(), rows, cols, &size);
|
||||
// Typing goes straight to the composer; Enter sends the first message
|
||||
// and the session begins (the card dissolved on the first keystroke).
|
||||
// type_line, not send+enter: a zero-gap PTY write is paste-classified
|
||||
// and the immediate Enter would be absorbed as a pasted newline.
|
||||
tui.type_line("start the session")
|
||||
.expect("type and send the first prompt");
|
||||
if tui
|
||||
.wait_for(|frame| !frame.text().contains('\u{2442}'), STARTUP_WAIT)
|
||||
.is_err()
|
||||
{
|
||||
panic!(
|
||||
"{size}: Startup New session did not enter the live shell\n{}",
|
||||
tui.diagnostics()
|
||||
);
|
||||
}
|
||||
super::launch_card_pty::explore_offline_from_composer(&mut tui);
|
||||
tui.wait_for_idle(Duration::from_millis(300), SETTLE_WAIT)
|
||||
.expect("session shell settles");
|
||||
// Prove the steady shell before clearing the onboarding seed. The clear
|
||||
|
||||
@@ -470,7 +470,6 @@ fn start_with_options(
|
||||
no_color,
|
||||
keep_composer: false,
|
||||
},
|
||||
|_| {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -487,13 +486,8 @@ struct Launch<'a> {
|
||||
keep_composer: bool,
|
||||
}
|
||||
|
||||
/// Launch into `workspace` and walk first-run onboarding to home.
|
||||
/// `on_provider_picker` sees the real first-run provider picker.
|
||||
fn start_in(
|
||||
workspace: SealedWorkspace,
|
||||
launch: Launch<'_>,
|
||||
on_provider_picker: impl FnOnce(&mut Harness),
|
||||
) -> (SealedWorkspace, Harness) {
|
||||
/// Launch into `workspace`; the composer-first launch card is home.
|
||||
fn start_in(workspace: SealedWorkspace, launch: Launch<'_>) -> (SealedWorkspace, Harness) {
|
||||
let Launch {
|
||||
rows,
|
||||
cols,
|
||||
@@ -579,11 +573,6 @@ fn start_in(
|
||||
.size(rows, cols)
|
||||
.spawn()
|
||||
.unwrap();
|
||||
wait(&mut tui, "Choose your model provider");
|
||||
on_provider_picker(&mut tui);
|
||||
tui.send(keys::key::ctrl('o')).unwrap();
|
||||
wait(&mut tui, "You're ready.");
|
||||
tui.send(keys::key::enter()).unwrap();
|
||||
wait(&mut tui, "New session");
|
||||
if animated || keep_composer {
|
||||
return (workspace, tui);
|
||||
@@ -594,6 +583,18 @@ fn start_in(
|
||||
(workspace, tui)
|
||||
}
|
||||
|
||||
pub(crate) fn explore_offline_from_composer(tui: &mut Harness) {
|
||||
wait(tui, "Type a message");
|
||||
tui.type_line("seed").expect("send a first message");
|
||||
wait(tui, "Choose your model provider");
|
||||
tui.send(keys::key::ctrl('o')).unwrap();
|
||||
wait(tui, "You're ready.");
|
||||
tui.send(keys::key::enter()).unwrap();
|
||||
tui.wait_for_idle(Duration::from_millis(300), WAIT).unwrap();
|
||||
tui.send(keys::key::ctrl('u')).unwrap();
|
||||
tui.wait_for_idle(Duration::from_millis(200), WAIT).unwrap();
|
||||
}
|
||||
|
||||
fn wait(tui: &mut Harness, text: &str) {
|
||||
if let Err(error) = tui.wait_for(|frame| frame.contains(text), WAIT) {
|
||||
let transcript = tui.transcript();
|
||||
|
||||
@@ -59,20 +59,8 @@ fn offline_queue_late_unbracketed_submit_keeps_composer_and_commands_responsive(
|
||||
.spawn()
|
||||
.expect("start offline TUI");
|
||||
|
||||
wait_or_panic(
|
||||
&mut tui,
|
||||
"Choose your model provider",
|
||||
STARTUP_WAIT,
|
||||
"provider",
|
||||
);
|
||||
tui.send(keys::key::ctrl('o')).expect("Explore Offline");
|
||||
wait_or_panic(&mut tui, "You're ready.", SETTLE_WAIT, "offline ready");
|
||||
tui.send(keys::key::enter()).expect("leave onboarding");
|
||||
wait_or_panic(&mut tui, "New session", STARTUP_WAIT, "launch card");
|
||||
tui.wait_for_idle(Duration::from_millis(100), SETTLE_WAIT)
|
||||
.expect("composer ready");
|
||||
tui.send(keys::key::ctrl('u'))
|
||||
.expect("clear suggested prompt");
|
||||
super::launch_card_pty::explore_offline_from_composer(&mut tui);
|
||||
|
||||
tui.send(keys::key::text("late queue draft"))
|
||||
.expect("raw prompt bytes");
|
||||
@@ -241,15 +229,11 @@ fn wait_for_bottom_rows_painted(tui: &mut Harness, label: &str) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Walk the real onboarding into deterministic offline-explore mode: no
|
||||
/// provider, no credentials, no network.
|
||||
/// Land on the composer-first launch card with no
|
||||
/// provider, no credentials and no network.
|
||||
fn enter_live_shell(tui: &mut Harness) {
|
||||
wait_or_panic(tui, "Choose your model provider", STARTUP_WAIT, "provider");
|
||||
tui.send(keys::key::ctrl('o'))
|
||||
.expect("choose Explore Offline");
|
||||
wait_or_panic(tui, "You're ready.", SETTLE_WAIT, "offline explore ready");
|
||||
tui.send(keys::key::enter()).expect("leave onboarding");
|
||||
wait_or_panic(tui, "New session", STARTUP_WAIT, "launch card");
|
||||
super::launch_card_pty::explore_offline_from_composer(tui);
|
||||
// Typing goes straight to the composer; Enter sends the first message
|
||||
// and the session begins (the card dissolved on the first keystroke).
|
||||
// type_line, not send+enter: a zero-gap PTY write is paste-classified
|
||||
|
||||
@@ -33,12 +33,6 @@ fn work_bar_opens_from_launch_with_legacy_and_enhanced_keys() {
|
||||
.size(rows, cols)
|
||||
.spawn()
|
||||
.expect("start TUI");
|
||||
tui.wait_for_text("Choose your model provider", Duration::from_secs(15))
|
||||
.unwrap();
|
||||
tui.send(keys::key::ctrl('o')).unwrap();
|
||||
tui.wait_for_text("You're ready.", Duration::from_secs(5))
|
||||
.unwrap();
|
||||
tui.send(keys::key::enter()).unwrap();
|
||||
tui.wait_for_text("New session", Duration::from_secs(15))
|
||||
.unwrap();
|
||||
tui.wait_for_idle(Duration::from_millis(250), Duration::from_secs(5))
|
||||
|
||||
@@ -90,9 +90,9 @@
|
||||
"modes": {
|
||||
"act": {
|
||||
"active": {
|
||||
"bytes": 37873,
|
||||
"bytes": 37703,
|
||||
"identity_sha256": "e2d6dfb7168d48ee8d2a415feac7ff5b8864e18528c233a697055370144eb138",
|
||||
"tokens_est": 9469,
|
||||
"tokens_est": 9426,
|
||||
"tool_names": [
|
||||
"agent",
|
||||
"bash",
|
||||
@@ -112,9 +112,9 @@
|
||||
"tools": 14
|
||||
},
|
||||
"full": {
|
||||
"bytes": 85363,
|
||||
"identity_sha256": "928e6417732837d26f1f26fe41ea6a11f5b8e3bfc4eeb3dd97a309379cffeff9",
|
||||
"tokens_est": 21341,
|
||||
"bytes": 84384,
|
||||
"identity_sha256": "2a2ee97e7c482c625bc50c77db7f0e08d6bc67e2b4e73e7351385418700252bb",
|
||||
"tokens_est": 21096,
|
||||
"tool_names": [
|
||||
"Git",
|
||||
"Run",
|
||||
@@ -151,7 +151,6 @@
|
||||
"session_get",
|
||||
"session_search",
|
||||
"speech",
|
||||
"task_shell_start",
|
||||
"task_shell_wait",
|
||||
"tasks",
|
||||
"terminal/cancel",
|
||||
@@ -169,14 +168,14 @@
|
||||
"workflow",
|
||||
"write"
|
||||
],
|
||||
"tools": 52
|
||||
"tools": 51
|
||||
}
|
||||
},
|
||||
"operate": {
|
||||
"active": {
|
||||
"bytes": 37873,
|
||||
"bytes": 37703,
|
||||
"identity_sha256": "e2d6dfb7168d48ee8d2a415feac7ff5b8864e18528c233a697055370144eb138",
|
||||
"tokens_est": 9469,
|
||||
"tokens_est": 9426,
|
||||
"tool_names": [
|
||||
"agent",
|
||||
"bash",
|
||||
@@ -196,9 +195,9 @@
|
||||
"tools": 14
|
||||
},
|
||||
"full": {
|
||||
"bytes": 85363,
|
||||
"identity_sha256": "928e6417732837d26f1f26fe41ea6a11f5b8e3bfc4eeb3dd97a309379cffeff9",
|
||||
"tokens_est": 21341,
|
||||
"bytes": 84384,
|
||||
"identity_sha256": "2a2ee97e7c482c625bc50c77db7f0e08d6bc67e2b4e73e7351385418700252bb",
|
||||
"tokens_est": 21096,
|
||||
"tool_names": [
|
||||
"Git",
|
||||
"Run",
|
||||
@@ -235,7 +234,6 @@
|
||||
"session_get",
|
||||
"session_search",
|
||||
"speech",
|
||||
"task_shell_start",
|
||||
"task_shell_wait",
|
||||
"tasks",
|
||||
"terminal/cancel",
|
||||
@@ -253,14 +251,14 @@
|
||||
"workflow",
|
||||
"write"
|
||||
],
|
||||
"tools": 52
|
||||
"tools": 51
|
||||
}
|
||||
},
|
||||
"plan": {
|
||||
"active": {
|
||||
"bytes": 36343,
|
||||
"bytes": 36173,
|
||||
"identity_sha256": "6c432c75bf776d95b367b17308b171c3eaba8e70aa0c2f76bac453b3c3368a21",
|
||||
"tokens_est": 9086,
|
||||
"tokens_est": 9044,
|
||||
"tool_names": [
|
||||
"agent",
|
||||
"bash",
|
||||
@@ -279,9 +277,9 @@
|
||||
"tools": 13
|
||||
},
|
||||
"full": {
|
||||
"bytes": 55333,
|
||||
"bytes": 55163,
|
||||
"identity_sha256": "ac8af1f4988199825be7b00b054c258724a44074b1d4e6de6c92ade7c1cffe63",
|
||||
"tokens_est": 13834,
|
||||
"tokens_est": 13791,
|
||||
"tool_names": [
|
||||
"Git",
|
||||
"Web",
|
||||
|
||||
Reference in new issue
Block a user