diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 135fa47..e1dafc3 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,14 +6,14 @@ }, "metadata": { "description": "Token Pilot \u2014 save 60-90% tokens when AI reads code", - "version": "0.42.2" + "version": "0.42.3" }, "plugins": [ { "name": "token-pilot", "source": "./", "description": "Reduces token consumption by 60-90% via AST-aware lazy file reading, structural symbol navigation, and cross-session tool-usage analytics. 22 MCP tools + 19 subagents + budget watchdog hooks.", - "version": "0.42.2", + "version": "0.42.3", "author": { "name": "Digital-Threads" }, diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 152f1a5..218375b 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "token-pilot", - "version": "0.42.2", + "version": "0.42.3", "description": "Saves 60-90% tokens on AI code reading. AST-aware lazy reads, symbol navigation, find_usages, structural git diff/log, edit-safety guard, Task-routing matcher, cross-session telemetry (errors + diagnostics), 25 tp-* subagents tiered to haiku/sonnet/opus with budget watchdog.", "author": { "name": "Digital-Threads", diff --git a/agents/tp-api-surface-tracker.md b/agents/tp-api-surface-tracker.md index e416366..5c95773 100644 --- a/agents/tp-api-surface-tracker.md +++ b/agents/tp-api-surface-tracker.md @@ -9,7 +9,7 @@ tools: - mcp__token-pilot__read_symbol - Bash model: haiku -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: dd184501203fa7f3c73f419c4ffbe33c4be75400cb64a7a51733a3fe23f6e085 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-audit-scanner.md b/agents/tp-audit-scanner.md index 66d7b08..487cd62 100644 --- a/agents/tp-audit-scanner.md +++ b/agents/tp-audit-scanner.md @@ -11,7 +11,7 @@ tools: - Grep - Read model: sonnet -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: d172f600bf32277ea6eb4cbbee4542ddd698a986dcd96997d33930561964569b requiredMcpServers: - "token-pilot" diff --git a/agents/tp-commit-writer.md b/agents/tp-commit-writer.md index eff959a..dddde73 100644 --- a/agents/tp-commit-writer.md +++ b/agents/tp-commit-writer.md @@ -8,7 +8,7 @@ tools: - mcp__token-pilot__test_summary - mcp__token-pilot__outline - Bash -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: de64a406b5176de19f7422619c7de7949b1f28865f225402c9cea9255f377428 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-context-engineer.md b/agents/tp-context-engineer.md index 1be4376..b0ed143 100644 --- a/agents/tp-context-engineer.md +++ b/agents/tp-context-engineer.md @@ -13,7 +13,7 @@ tools: - Edit - Glob model: sonnet -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: 68b32af2dacd82ebe52c4eec93edb903d452688274c3065218270627c564d8b0 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-dead-code-finder.md b/agents/tp-dead-code-finder.md index c7b63b2..25a9910 100644 --- a/agents/tp-dead-code-finder.md +++ b/agents/tp-dead-code-finder.md @@ -11,7 +11,7 @@ tools: - Grep - Read model: sonnet -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: d9b7f5b7ae6f4ae21305c775361bcab097cc774370a6d976c093571d46d55021 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-debugger.md b/agents/tp-debugger.md index f2cb2de..5edd156 100644 --- a/agents/tp-debugger.md +++ b/agents/tp-debugger.md @@ -12,7 +12,7 @@ tools: - Read - Bash model: sonnet -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: 052413de8d92377edcde6ae5c823f5378db304baccfa29e8866467f42553a500 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-dep-health.md b/agents/tp-dep-health.md index a085a5e..5510d78 100644 --- a/agents/tp-dep-health.md +++ b/agents/tp-dep-health.md @@ -9,7 +9,7 @@ tools: - Bash - Read model: haiku -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: e14dc57493d816f8c2e017963e2ef5f66bea50fd0b805a80e8a0d97c968427e7 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-doc-writer.md b/agents/tp-doc-writer.md index 8b85c3a..77ba5e6 100644 --- a/agents/tp-doc-writer.md +++ b/agents/tp-doc-writer.md @@ -13,7 +13,7 @@ tools: - Edit - Glob model: haiku -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: 57d741794ab40e31a7ac49c68ea39a9088f5827cdef866ce81bfca1b7c9180cf requiredMcpServers: - "token-pilot" diff --git a/agents/tp-history-explorer.md b/agents/tp-history-explorer.md index f2f991f..f06c460 100644 --- a/agents/tp-history-explorer.md +++ b/agents/tp-history-explorer.md @@ -10,7 +10,7 @@ tools: - Bash - Read model: haiku -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: 7b70fa76a60e3c58a1de4f56c32c0f166424137e203a0cf1c8654e7c9235d904 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-impact-analyzer.md b/agents/tp-impact-analyzer.md index 880dd25..de2f674 100644 --- a/agents/tp-impact-analyzer.md +++ b/agents/tp-impact-analyzer.md @@ -12,7 +12,7 @@ tools: - mcp__token-pilot__read_symbols - Read model: sonnet -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: 351a987e11eba63852f5431a16d8eb53104f4f689f82fdcc5a2bf4db948ba92f requiredMcpServers: - "token-pilot" diff --git a/agents/tp-incident-timeline.md b/agents/tp-incident-timeline.md index b91cfa4..fa04653 100644 --- a/agents/tp-incident-timeline.md +++ b/agents/tp-incident-timeline.md @@ -8,7 +8,7 @@ tools: - mcp__token-pilot__read_symbol - Bash model: inherit -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: de5722bfea374eaab096c1ae635c37879e7a91370ee3cd0532f4240be03c91eb requiredMcpServers: - "token-pilot" diff --git a/agents/tp-incremental-builder.md b/agents/tp-incremental-builder.md index f03a376..98c1a94 100644 --- a/agents/tp-incremental-builder.md +++ b/agents/tp-incremental-builder.md @@ -13,7 +13,7 @@ tools: - Edit - Bash model: sonnet -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: 375a824d0d847bb5453ec594c7a62ad566ee7e4d92717b0473f771f1a0477c60 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-migration-scout.md b/agents/tp-migration-scout.md index 6965d55..54bd2f4 100644 --- a/agents/tp-migration-scout.md +++ b/agents/tp-migration-scout.md @@ -11,7 +11,7 @@ tools: - Grep - Glob model: sonnet -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: 0334de1bf99b431b65359637d125cda7c44c6f780eb92c57cc538715b1939536 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-onboard.md b/agents/tp-onboard.md index 0bbc8cd..37a734e 100644 --- a/agents/tp-onboard.md +++ b/agents/tp-onboard.md @@ -10,7 +10,7 @@ tools: - mcp__token-pilot__smart_read - mcp__token-pilot__smart_read_many - mcp__token-pilot__read_section -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: 832e95633fbc8e9b0c10f3e540a327d4be062fb4b3f17a6cce6be13f414e2927 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-performance-profiler.md b/agents/tp-performance-profiler.md index 786ef6d..8977e1f 100644 --- a/agents/tp-performance-profiler.md +++ b/agents/tp-performance-profiler.md @@ -11,7 +11,7 @@ tools: - Bash - Read model: sonnet -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: b61f06380d80798fa2e49d37bcba0653495bee04dd6bdbc1feff9a75607b0508 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-pr-reviewer.md b/agents/tp-pr-reviewer.md index af0a73b..9dac64d 100644 --- a/agents/tp-pr-reviewer.md +++ b/agents/tp-pr-reviewer.md @@ -11,7 +11,7 @@ tools: - mcp__token-pilot__read_for_edit - Read model: sonnet -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: f83f50d05b4f70285ae7afed2b1a406fc436df56e61a0aedbfb31edc7f2b6e66 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-refactor-planner.md b/agents/tp-refactor-planner.md index 28dea03..6116a2c 100644 --- a/agents/tp-refactor-planner.md +++ b/agents/tp-refactor-planner.md @@ -8,7 +8,7 @@ tools: - mcp__token-pilot__outline - mcp__token-pilot__read_symbol model: sonnet -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: c5f6fc122c89e16e5cf774045f92169ee3468555320b898171ba13eca5323550 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-review-impact.md b/agents/tp-review-impact.md index 6bf9d53..02f7a9a 100644 --- a/agents/tp-review-impact.md +++ b/agents/tp-review-impact.md @@ -9,7 +9,7 @@ tools: - mcp__token-pilot__module_info - Bash model: sonnet -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: 8ef3c3341cbfed4eb8dd130126a9683edc57e378c92ff0ca764d584fd941c55c requiredMcpServers: - "token-pilot" diff --git a/agents/tp-run.md b/agents/tp-run.md index 378c264..7caa8ba 100644 --- a/agents/tp-run.md +++ b/agents/tp-run.md @@ -16,7 +16,7 @@ tools: - Glob - Bash model: haiku -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: 2b08618d34a61f00aafccbda9fed6d83243296dedb83440edbd2d5c28bb6dbc4 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-session-restorer.md b/agents/tp-session-restorer.md index 5e3e4ca..ba9a92d 100644 --- a/agents/tp-session-restorer.md +++ b/agents/tp-session-restorer.md @@ -9,7 +9,7 @@ tools: - mcp__token-pilot__session_budget - Bash - Read -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: 529374ed728f5eed5b758b3be3da65624783c0bf0c1a253d7d661a843eb5f767 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-ship-coordinator.md b/agents/tp-ship-coordinator.md index 8136834..727af54 100644 --- a/agents/tp-ship-coordinator.md +++ b/agents/tp-ship-coordinator.md @@ -11,7 +11,7 @@ tools: - Read - Grep model: sonnet -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: a60f6ae110eb3138064bce074e8ba26fa0ce5f4659df1624a9d9d3646803391b requiredMcpServers: - "token-pilot" diff --git a/agents/tp-spec-writer.md b/agents/tp-spec-writer.md index 512a3fb..91d7fd6 100644 --- a/agents/tp-spec-writer.md +++ b/agents/tp-spec-writer.md @@ -9,7 +9,7 @@ tools: - Read - Write model: sonnet -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: c7a4e8b39228fd5158528f389c924c5ff2d98c4b9b05ee0106d54a26c5dc1350 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-test-coverage-gapper.md b/agents/tp-test-coverage-gapper.md index a8c543e..3a1375c 100644 --- a/agents/tp-test-coverage-gapper.md +++ b/agents/tp-test-coverage-gapper.md @@ -10,7 +10,7 @@ tools: - mcp__token-pilot__test_summary - Glob - Grep -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: be81eed53a3720d146cf89e4a14a7a56577633f7c84c234c412ab70d64c05b11 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-test-triage.md b/agents/tp-test-triage.md index 3754e9a..417482e 100644 --- a/agents/tp-test-triage.md +++ b/agents/tp-test-triage.md @@ -8,7 +8,7 @@ tools: - mcp__token-pilot__find_usages - mcp__token-pilot__read_symbol model: sonnet -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: 362ecf4cb03b059421ea26933473700900073dc38b3a7fe271208dfb1ae14f90 requiredMcpServers: - "token-pilot" diff --git a/agents/tp-test-writer.md b/agents/tp-test-writer.md index 871608b..f24f8fb 100644 --- a/agents/tp-test-writer.md +++ b/agents/tp-test-writer.md @@ -13,7 +13,7 @@ tools: - Edit - Bash model: sonnet -token_pilot_version: "0.42.2" +token_pilot_version: "0.42.3" token_pilot_body_hash: 269f2fe22ff4517c277d3f56ca67d8a5527b93290ab21079a83ba7af22c1b5a9 requiredMcpServers: - "token-pilot" diff --git a/package.json b/package.json index 1271f05..8d84ba7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "token-pilot", - "version": "0.42.2", + "version": "0.42.3", "description": "Save up to 80% tokens when AI reads code — MCP server for token-efficient code navigation, AST-aware structural reading instead of dumping full files into context window", "type": "module", "main": "dist/index.js", diff --git a/src/cli/install-statusline.ts b/src/cli/install-statusline.ts index b753442..40f3abe 100644 --- a/src/cli/install-statusline.ts +++ b/src/cli/install-statusline.ts @@ -27,7 +27,14 @@ import { homedir } from "node:os"; import { dirname, join } from "node:path"; import type { StatuslineStatus } from "./ecosystem-check.js"; -/** The version-agnostic chain command (auto-picks the newest plugin dir). */ +/** TP-only badge command (DEFAULT — just token-pilot's [TP] badge). */ +export const TP_ONLY_COMMAND = + 'bash "$(ls -t ~/.claude/plugins/cache/token-pilot/token-pilot/*/hooks/tp-statusline.sh 2>/dev/null | head -1)"'; + +/** + * Chain command — renders token-pilot's badge AND any other ecosystem + * badge (caveman) side by side. Opt-in via `--chain`. + */ export const CHAIN_COMMAND = 'bash "$(ls -t ~/.claude/plugins/cache/token-pilot/token-pilot/*/hooks/statusline-chain.sh 2>/dev/null | head -1)"'; @@ -36,76 +43,108 @@ export interface InstallStatuslineResult { message: string; } +export interface DecideStatuslineOpts { + force: boolean; + /** `--chain`: render caveman + [TP] together. Default false → [TP] only. */ + chain: boolean; +} + /** - * Pure decision: given the current statusline status, what should the - * installer do? Separated from I/O for unit tests. + * Pure decision: given the current statusline status + options, what + * should the installer do and which command should it write? + * + * v0.42.3 — DEFAULT is now the TP-only badge. The previous default + * silently installed the chain wrapper, which also rendered caveman's + * badge — presumptuous (we install OUR badge, we don't decide that a + * third-party tool's badge belongs in your status bar). `--chain` + * opts into the combined view. token-pilot's own previous choices + * (tp-only ↔ chain) are switched freely; a non-token-pilot statusLine + * (caveman-only, custom) is never clobbered without `--force`. */ export function decideStatuslineAction( status: StatuslineStatus, - force: boolean, -): { write: boolean; result: InstallStatuslineResult } { + opts: DecideStatuslineOpts, +): { write: boolean; command: string; result: InstallStatuslineResult } { + const command = opts.chain ? CHAIN_COMMAND : TP_ONLY_COMMAND; + const badgeDesc = opts.chain ? "caveman + [TP] badges" : "[TP] badge"; + const noWrite = (action: InstallStatuslineResult["action"], message: string) => ({ + write: false, + command, + result: { action, message }, + }); + const doWrite = (action: InstallStatuslineResult["action"], message: string) => ({ + write: true, + command, + result: { action, message }, + }); + switch (status) { case "not-configured": - return { - write: true, - result: { - action: "installed", - message: - "statusLine configured — the [TP] badge will show in your status bar (restart Claude Code).", - }, - }; - case "configured-caveman-only": + return doWrite( + "installed", + `statusLine configured — the ${badgeDesc} will show in your status bar (restart Claude Code).`, + ); + case "configured-tp-only": - return { - write: true, - result: { - action: "upgraded", - message: - "statusLine upgraded to the chain wrapper — both caveman and [TP] badges now render side by side.", - }, - }; + // Already ours. Switch only if the user asked for the chain. + return opts.chain + ? doWrite( + "upgraded", + "statusLine upgraded to the chain wrapper — caveman + [TP] now render side by side.", + ) + : noWrite("noop", "statusLine already shows the [TP] badge. Nothing to do."); + case "configured-chain": - return { - write: false, - result: { - action: "noop", - message: "statusLine already uses the token-pilot chain wrapper. Nothing to do.", - }, - }; + // Also ours. Switch to tp-only unless the user wants the chain. + return opts.chain + ? noWrite("noop", "statusLine already uses the chain wrapper. Nothing to do.") + : doWrite( + "installed", + "statusLine switched to the [TP]-only badge (caveman badge removed from the status bar).", + ); + + case "configured-caveman-only": + // Caveman's own statusline — NOT ours. Don't replace it silently. + if (opts.chain) { + return doWrite( + "upgraded", + "statusLine upgraded to the chain wrapper — keeps caveman and adds [TP].", + ); + } + if (opts.force) { + return doWrite( + "installed", + "Replaced caveman's statusLine with the [TP]-only badge (--force).", + ); + } + return noWrite( + "skipped", + "Your statusLine is caveman's. Left untouched. Run with --chain to show " + + "both badges, or --force to replace it with [TP] only.", + ); + case "configured-other": - if (force) { - return { - write: true, - result: { - action: "installed", - message: - "Replaced your custom statusLine with the token-pilot chain wrapper (--force).", - }, - }; + if (opts.force) { + return doWrite( + "installed", + `Replaced your custom statusLine with the ${badgeDesc} (--force).`, + ); } - return { - write: false, - result: { - action: "skipped", - message: - "You already have a custom statusLine — left untouched. " + - "To show the [TP] badge too, set statusLine.command to:\n " + - CHAIN_COMMAND + - "\nor re-run with --force to replace it.", - }, - }; + return noWrite( + "skipped", + "You already have a custom statusLine — left untouched. To use the " + + `${badgeDesc}, re-run with --force, or set statusLine.command to:\n ` + + command, + ); + case "unknown": default: - return { - write: false, - result: { - action: "skipped", - message: - "Could not read ~/.claude/settings.json as JSON — not modifying it. " + - "Add this manually under \"statusLine\":\n " + - CHAIN_COMMAND, - }, - }; + return noWrite( + "skipped", + 'Could not read ~/.claude/settings.json as JSON — not modifying it. ' + + 'Add this manually under "statusLine":\n ' + + command, + ); } } @@ -145,11 +184,15 @@ export async function handleInstallStatusline( opts?: { settingsPath?: string }, ): Promise { const force = argv.includes("--force"); + const chain = argv.includes("--chain"); const settingsPath = opts?.settingsPath ?? join(homedir(), ".claude", "settings.json"); const status = await classifyStatuslineAt(settingsPath); - const { write, result } = decideStatuslineAction(status, force); + const { write, command, result } = decideStatuslineAction(status, { + force, + chain, + }); if (!write) { process.stdout.write(`[token-pilot] ${result.message}\n`); @@ -168,7 +211,7 @@ export async function handleInstallStatusline( /* fresh file — start clean (only reached when status was safe) */ } - settings.statusLine = { type: "command", command: CHAIN_COMMAND }; + settings.statusLine = { type: "command", command }; try { await mkdir(dirname(settingsPath), { recursive: true }); diff --git a/tests/cli/install-statusline.test.ts b/tests/cli/install-statusline.test.ts index 48e5bc0..509e298 100644 --- a/tests/cli/install-statusline.test.ts +++ b/tests/cli/install-statusline.test.ts @@ -13,39 +13,63 @@ import { handleInstallStatusline, classifyStatuslineAt, CHAIN_COMMAND, + TP_ONLY_COMMAND, } from "../../src/cli/install-statusline.ts"; -describe("decideStatuslineAction", () => { - it("installs when not configured", () => { - const d = decideStatuslineAction("not-configured", false); +const DEF = { force: false, chain: false }; + +describe("decideStatuslineAction (v0.42.3 — TP-only default)", () => { + it("installs the TP-only badge when not configured", () => { + const d = decideStatuslineAction("not-configured", DEF); expect(d.write).toBe(true); expect(d.result.action).toBe("installed"); + expect(d.command).toContain("tp-statusline.sh"); + expect(d.command).not.toContain("chain"); }); - it("upgrades caveman-only / tp-only to the chain", () => { - expect(decideStatuslineAction("configured-caveman-only", false).write).toBe( - true, - ); - expect(decideStatuslineAction("configured-tp-only", false).result.action).toBe( - "upgraded", - ); + it("installs the chain only with --chain", () => { + const d = decideStatuslineAction("not-configured", { force: false, chain: true }); + expect(d.write).toBe(true); + expect(d.command).toContain("statusline-chain.sh"); + }); + it("switches an existing chain to TP-only by default (drops caveman badge)", () => { + const d = decideStatuslineAction("configured-chain", DEF); + expect(d.write).toBe(true); + expect(d.command).toContain("tp-statusline.sh"); + expect(d.result.message).toMatch(/caveman badge removed/i); }); - it("no-ops when already chain", () => { - const d = decideStatuslineAction("configured-chain", false); + it("no-ops when already TP-only (default)", () => { + const d = decideStatuslineAction("configured-tp-only", DEF); expect(d.write).toBe(false); expect(d.result.action).toBe("noop"); }); + it("no-ops when already chain and --chain requested", () => { + const d = decideStatuslineAction("configured-chain", { force: false, chain: true }); + expect(d.write).toBe(false); + expect(d.result.action).toBe("noop"); + }); + it("does NOT replace caveman-only without --force/--chain", () => { + const d = decideStatuslineAction("configured-caveman-only", DEF); + expect(d.write).toBe(false); + expect(d.result.action).toBe("skipped"); + }); + it("upgrades caveman-only to chain with --chain (keeps both)", () => { + const d = decideStatuslineAction("configured-caveman-only", { force: false, chain: true }); + expect(d.write).toBe(true); + expect(d.command).toContain("statusline-chain.sh"); + }); it("leaves a third-party statusLine alone without --force", () => { - const d = decideStatuslineAction("configured-other", false); + const d = decideStatuslineAction("configured-other", DEF); expect(d.write).toBe(false); expect(d.result.action).toBe("skipped"); expect(d.result.message).toContain("--force"); }); - it("replaces a third-party statusLine with --force", () => { - const d = decideStatuslineAction("configured-other", true); + it("replaces a third-party statusLine with --force (TP-only)", () => { + const d = decideStatuslineAction("configured-other", { force: true, chain: false }); expect(d.write).toBe(true); + expect(d.command).toContain("tp-statusline.sh"); }); it("does not write on unknown (unparseable settings)", () => { - expect(decideStatuslineAction("unknown", false).write).toBe(false); + expect(decideStatuslineAction("unknown", DEF).write).toBe(false); }); }); @@ -102,14 +126,21 @@ describe("handleInstallStatusline (tmp settings)", () => { await rm(dir, { recursive: true, force: true }); }); - it("writes the chain command into a fresh settings file", async () => { + it("writes the TP-only command into a fresh settings file (default)", async () => { const code = await handleInstallStatusline([], { settingsPath: p }); expect(code).toBe(0); const saved = JSON.parse(await readFile(p, "utf-8")); - expect(saved.statusLine.command).toBe(CHAIN_COMMAND); + expect(saved.statusLine.command).toBe(TP_ONLY_COMMAND); + expect(saved.statusLine.command).not.toContain("chain"); expect(saved.statusLine.type).toBe("command"); }); + it("writes the chain command with --chain", async () => { + await handleInstallStatusline(["--chain"], { settingsPath: p }); + const saved = JSON.parse(await readFile(p, "utf-8")); + expect(saved.statusLine.command).toBe(CHAIN_COMMAND); + }); + it("preserves existing settings keys when writing", async () => { await mkdir(dir, { recursive: true }); await writeFile(p, JSON.stringify({ env: { FOO: "1" }, model: "opus" })); @@ -117,7 +148,7 @@ describe("handleInstallStatusline (tmp settings)", () => { const saved = JSON.parse(await readFile(p, "utf-8")); expect(saved.env.FOO).toBe("1"); expect(saved.model).toBe("opus"); - expect(saved.statusLine.command).toBe(CHAIN_COMMAND); + expect(saved.statusLine.command).toBe(TP_ONLY_COMMAND); }); it("does NOT clobber a third-party statusLine without --force", async () => { @@ -131,13 +162,13 @@ describe("handleInstallStatusline (tmp settings)", () => { expect(saved.statusLine.command).toBe("custom.sh"); // untouched }); - it("replaces a third-party statusLine with --force", async () => { + it("replaces a third-party statusLine with --force (TP-only)", async () => { await writeFile( p, JSON.stringify({ statusLine: { type: "command", command: "custom.sh" } }), ); await handleInstallStatusline(["--force"], { settingsPath: p }); const saved = JSON.parse(await readFile(p, "utf-8")); - expect(saved.statusLine.command).toBe(CHAIN_COMMAND); + expect(saved.statusLine.command).toBe(TP_ONLY_COMMAND); }); });