@jun133/kitty 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -40,7 +40,7 @@ var init_package = __esm({
40
40
  "package.json"() {
41
41
  package_default = {
42
42
  name: "@jun133/kitty",
43
- version: "0.0.7",
43
+ version: "0.0.9",
44
44
  description: "Agent",
45
45
  license: "MIT",
46
46
  keywords: [
@@ -64,7 +64,7 @@ var init_package = __esm({
64
64
  node: ">=20.0.0"
65
65
  },
66
66
  scripts: {
67
- build: "tsup src/cli.ts --format cjs --platform node --target node20 --outDir dist --clean --sourcemap",
67
+ build: "tsup src/cli.ts --format cjs --platform node --target node20 --outDir dist --clean --sourcemap && tsup src/tui.ts --format esm --platform node --target node20 --outDir dist --clean=false --external typescript",
68
68
  check: "npm run typecheck && npm run build",
69
69
  dev: "tsx src/cli.ts",
70
70
  "test:build": `node -e "require('node:fs').rmSync('.test-build', { recursive: true, force: true })" && tsc -p tsconfig.tests.json`,
@@ -83,12 +83,18 @@ var init_package = __esm({
83
83
  execa: "^9.6.0",
84
84
  "fast-glob": "^3.3.3",
85
85
  figlet: "^1.11.0",
86
+ ink: "^7.1.0",
87
+ marked: "^15.0.7",
86
88
  openai: "^6.4.0",
89
+ react: "^19.2.7",
90
+ "string-width": "^8.2.1",
91
+ "wrap-ansi": "^10.0.0",
87
92
  ws: "^8.21.0"
88
93
  },
89
94
  devDependencies: {
90
95
  "@types/better-sqlite3": "^7.6.13",
91
96
  "@types/node": "^24.5.2",
97
+ "@types/react": "^19.2.17",
92
98
  "@types/ws": "^8.18.1",
93
99
  tsup: "^8.5.0",
94
100
  tsx: "^4.20.5",
@@ -1128,6 +1134,13 @@ var init_wakeSignals = __esm({
1128
1134
  });
1129
1135
 
1130
1136
  // src/control/ledger.ts
1137
+ var ledger_exports = {};
1138
+ __export(ledger_exports, {
1139
+ ControlPlaneLedger: () => ControlPlaneLedger,
1140
+ ExecutionLedgerRepo: () => ExecutionLedgerRepo,
1141
+ TaskLifecycleLedgerRepo: () => TaskLifecycleLedgerRepo,
1142
+ WakeSignalLedgerRepo: () => WakeSignalLedgerRepo
1143
+ });
1131
1144
  var import_better_sqlite3, import_node_fs2, ControlPlaneLedger;
1132
1145
  var init_ledger = __esm({
1133
1146
  "src/control/ledger.ts"() {
@@ -5911,6 +5924,12 @@ var init_envKeys = __esm({
5911
5924
  });
5912
5925
 
5913
5926
  // src/agent/prompt/format.ts
5927
+ var format_exports = {};
5928
+ __export(format_exports, {
5929
+ formatPromptBlock: () => formatPromptBlock,
5930
+ joinBlocks: () => joinBlocks,
5931
+ renderPromptLayers: () => renderPromptLayers
5932
+ });
5914
5933
  function formatPromptBlock(title, content) {
5915
5934
  return `${title}:
5916
5935
  ${content}`;
@@ -8682,7 +8701,22 @@ function readContextBudget(value, sessionPath) {
8682
8701
  compressionMode,
8683
8702
  compressionReason: readRequiredString(record, "compressionReason", sessionPath, "contextBudget"),
8684
8703
  sources: readContextBudgetSources(record.sources, sessionPath),
8685
- promptHotspots: readContextBudgetHotspots(record.promptHotspots, sessionPath)
8704
+ promptHotspots: readContextBudgetHotspots(record.promptHotspots, sessionPath),
8705
+ cacheLayout: readContextCacheLayout(record.cacheLayout, sessionPath)
8706
+ };
8707
+ }
8708
+ function readContextCacheLayout(value, sessionPath) {
8709
+ if (value === void 0) {
8710
+ return void 0;
8711
+ }
8712
+ const record = expectRecord(value, sessionPath, "contextBudget.cacheLayout");
8713
+ return {
8714
+ stablePrefixFingerprint: readRequiredString(record, "stablePrefixFingerprint", sessionPath, "contextBudget.cacheLayout"),
8715
+ volatileTailFingerprint: readRequiredString(record, "volatileTailFingerprint", sessionPath, "contextBudget.cacheLayout"),
8716
+ stablePrefixChars: readRequiredNumber(record, "stablePrefixChars", sessionPath, "contextBudget.cacheLayout"),
8717
+ volatileTailChars: readRequiredNumber(record, "volatileTailChars", sessionPath, "contextBudget.cacheLayout"),
8718
+ stableSources: readStringArray(record.stableSources, sessionPath, "contextBudget.cacheLayout.stableSources"),
8719
+ volatileSources: readStringArray(record.volatileSources, sessionPath, "contextBudget.cacheLayout.volatileSources")
8686
8720
  };
8687
8721
  }
8688
8722
  function readContextBudgetSources(value, sessionPath) {
@@ -8724,6 +8758,17 @@ function readContextBudgetHotspots(value, sessionPath) {
8724
8758
  };
8725
8759
  });
8726
8760
  }
8761
+ function readStringArray(value, sessionPath, scope) {
8762
+ if (!Array.isArray(value)) {
8763
+ throw createSessionCorruptError(sessionPath, `${scope} must be an array`);
8764
+ }
8765
+ return value.map((entry, index) => {
8766
+ if (typeof entry !== "string") {
8767
+ throw createSessionCorruptError(sessionPath, `${scope}[${index}] must be a string`);
8768
+ }
8769
+ return entry;
8770
+ });
8771
+ }
8727
8772
  function readMessage(value, index, sessionPath) {
8728
8773
  const record = expectRecord(value, sessionPath, `messages[${index}]`);
8729
8774
  const role = readRequiredString(record, "role", sessionPath, `messages[${index}]`);
@@ -9881,6 +9926,222 @@ var init_projectContext = __esm({
9881
9926
  }
9882
9927
  });
9883
9928
 
9929
+ // src/runtime/scene.ts
9930
+ function buildRuntimeScene(status) {
9931
+ const executions = status.executions.active.map(buildExecutionScene);
9932
+ const blockedExecutions = executions.filter((execution) => execution.risk === "blocked");
9933
+ const watchExecutions = executions.filter((execution) => execution.risk === "watch");
9934
+ const activeBackground = executions.filter((execution) => execution.kind === "background");
9935
+ const blockedBackground = activeBackground.filter((execution) => execution.risk !== "none");
9936
+ return {
9937
+ headline: buildHeadline(status, blockedExecutions, watchExecutions),
9938
+ focus: readFocus(status),
9939
+ nextAction: readNextAction(status, blockedExecutions, watchExecutions),
9940
+ blocked: readBlocked(blockedExecutions),
9941
+ cost: readCost(status),
9942
+ recovery: readRecovery(status, executions),
9943
+ skills: {
9944
+ ready: status.skills.ready,
9945
+ total: status.skills.total,
9946
+ nextAction: readSkillsNextAction(status)
9947
+ },
9948
+ memory: {
9949
+ assets: status.memory.assets.length,
9950
+ latestSessionMemory: Boolean(status.sessions.latest?.hasMemory),
9951
+ nextAction: readMemoryNextAction(status)
9952
+ },
9953
+ background: {
9954
+ active: activeBackground.length,
9955
+ blocked: blockedBackground.length,
9956
+ nextAction: readBackgroundNextAction(activeBackground)
9957
+ },
9958
+ executions
9959
+ };
9960
+ }
9961
+ function buildExecutionScene(execution) {
9962
+ const risk = readExecutionRisk(execution);
9963
+ return {
9964
+ id: execution.id,
9965
+ kind: execution.kind,
9966
+ status: execution.status,
9967
+ health: execution.health?.message ?? `Execution is ${execution.status}.`,
9968
+ risk,
9969
+ summary: readExecutionSummary(execution),
9970
+ nextAction: readExecutionNextAction(execution, risk),
9971
+ lastOutput: execution.outputPreview ? truncateText2(execution.outputPreview, 160) : void 0
9972
+ };
9973
+ }
9974
+ function buildHeadline(status, blockedExecutions, watchExecutions) {
9975
+ if (!status.sessions.latest) {
9976
+ return "No active session yet.";
9977
+ }
9978
+ if (blockedExecutions.length > 0) {
9979
+ return `${blockedExecutions.length} execution(s) need attention.`;
9980
+ }
9981
+ if (watchExecutions.length > 0) {
9982
+ return `${watchExecutions.length} execution(s) should be watched.`;
9983
+ }
9984
+ if (status.executions.active.length > 0) {
9985
+ return `${status.executions.active.length} execution(s) are running.`;
9986
+ }
9987
+ return "Ready to continue the latest session.";
9988
+ }
9989
+ function readFocus(status) {
9990
+ const focus = status.sessions.latest?.focus;
9991
+ if (focus) {
9992
+ return truncateText2(focus, 120);
9993
+ }
9994
+ const title = status.sessions.latest?.title;
9995
+ if (title) {
9996
+ return truncateText2(title, 120);
9997
+ }
9998
+ return "none";
9999
+ }
10000
+ function readNextAction(status, blockedExecutions, watchExecutions) {
10001
+ const urgent = blockedExecutions[0] ?? watchExecutions[0];
10002
+ if (urgent) {
10003
+ return urgent.nextAction;
10004
+ }
10005
+ if (status.executions.active.length > 0) {
10006
+ return "Let active work finish, or inspect it with `kitty status` / `kitty background`.";
10007
+ }
10008
+ if (!status.sessions.latest) {
10009
+ return "Start a session with `kitty`.";
10010
+ }
10011
+ return "Continue from the current session focus.";
10012
+ }
10013
+ function readBlocked(blockedExecutions) {
10014
+ if (blockedExecutions.length === 0) {
10015
+ return "no";
10016
+ }
10017
+ return blockedExecutions.slice(0, 3).map((execution) => `${execution.kind} ${execution.id}: ${execution.health}`).join(" | ");
10018
+ }
10019
+ function readCost(status) {
10020
+ const budget = status.sessions.latest?.contextBudget;
10021
+ const latest = status.modelRequests.recent[0];
10022
+ const budgetText = budget ? `${Math.round(budget.usageRatio * 100)}% context${budget.compressed ? ", compressed" : ""}` : "context unknown";
10023
+ const layout = budget?.cacheLayout;
10024
+ const layoutText = layout ? `stable ${readStableRatio(layout.stablePrefixChars, layout.volatileTailChars)}` : "cache layout unknown";
10025
+ const usageText = latest?.usage ? readUsageCost(latest.usage) : latest ? "provider usage unavailable" : "no model request yet";
10026
+ return `${budgetText}; ${layoutText}; ${usageText}`;
10027
+ }
10028
+ function readStableRatio(stableChars, volatileChars) {
10029
+ const total = stableChars + volatileChars;
10030
+ return total > 0 ? `${Math.round(stableChars / total * 100)}%` : "unknown";
10031
+ }
10032
+ function readUsageCost(usage) {
10033
+ const cached = usage.cacheHitTokens ?? usage.cacheReadTokens;
10034
+ const hitRate = usage.cacheHitRate === void 0 ? void 0 : `${Math.round(usage.cacheHitRate * 100)}% hit`;
10035
+ return [
10036
+ usage.totalTokens === void 0 ? void 0 : `${usage.totalTokens} tokens`,
10037
+ cached === void 0 ? "cache unknown" : `${cached} cached`,
10038
+ hitRate
10039
+ ].filter(Boolean).join(", ");
10040
+ }
10041
+ function readRecovery(status, executions) {
10042
+ const risky = executions.filter((execution) => execution.risk !== "none").length;
10043
+ if (risky > 0) {
10044
+ return `${risky} execution(s) need recovery attention.`;
10045
+ }
10046
+ if (status.wakeSignals.recent.length > 0) {
10047
+ return `${status.wakeSignals.recent.length} wake signal(s) recorded.`;
10048
+ }
10049
+ return "no recovery action needed";
10050
+ }
10051
+ function readSkillsNextAction(status) {
10052
+ if (status.skills.total === 0) {
10053
+ return "No runtime skills discovered.";
10054
+ }
10055
+ if (status.skills.needsAttention.length > 0) {
10056
+ return "Inspect skill issues before relying on those skills.";
10057
+ }
10058
+ return "Skills are ready; load full skill content only when needed.";
10059
+ }
10060
+ function readMemoryNextAction(status) {
10061
+ if (!status.sessions.latest) {
10062
+ return "No session memory yet.";
10063
+ }
10064
+ if (!status.sessions.latest.hasMemory && status.memory.assets.length === 0) {
10065
+ return "Continue the session until useful memory is saved.";
10066
+ }
10067
+ if (!status.sessions.latest.hasMemory && status.memory.assets.length > 0) {
10068
+ return "Review memory assets when prior evidence is needed.";
10069
+ }
10070
+ return "Session memory is available; use assets only when needed.";
10071
+ }
10072
+ function readBackgroundNextAction(backgrounds) {
10073
+ if (backgrounds.length === 0) {
10074
+ return "No active background work.";
10075
+ }
10076
+ const blocked = backgrounds.find((execution) => execution.risk === "blocked");
10077
+ if (blocked) {
10078
+ return blocked.nextAction;
10079
+ }
10080
+ const watch = backgrounds.find((execution) => execution.risk === "watch");
10081
+ if (watch) {
10082
+ return watch.nextAction;
10083
+ }
10084
+ return "Background work is running; wait or inspect latest output.";
10085
+ }
10086
+ function readExecutionRisk(execution) {
10087
+ switch (execution.health?.state) {
10088
+ case "deadline_passed":
10089
+ case "stale":
10090
+ return "blocked";
10091
+ case "no_output":
10092
+ return "watch";
10093
+ case "running":
10094
+ case "settled":
10095
+ case void 0:
10096
+ return "none";
10097
+ }
10098
+ }
10099
+ function readExecutionSummary(execution) {
10100
+ if (execution.assignment?.objective) {
10101
+ return truncateText2(execution.assignment.objective, 120);
10102
+ }
10103
+ if (execution.summary) {
10104
+ return truncateText2(execution.summary, 120);
10105
+ }
10106
+ if (execution.command) {
10107
+ return truncateText2(execution.command, 120);
10108
+ }
10109
+ return `${execution.kind} execution`;
10110
+ }
10111
+ function readExecutionNextAction(execution, risk) {
10112
+ if (execution.kind === "background") {
10113
+ if (risk === "blocked") {
10114
+ return `Inspect or stop with \`kitty background stop ${execution.id}\`.`;
10115
+ }
10116
+ if (risk === "watch") {
10117
+ return `Wait for first output or inspect with \`kitty background wait ${execution.id}\`.`;
10118
+ }
10119
+ return `Inspect with \`kitty background wait ${execution.id}\` if you need the result now.`;
10120
+ }
10121
+ if (risk === "blocked") {
10122
+ return `Inspect execution ${execution.id} in status before continuing.`;
10123
+ }
10124
+ if (risk === "watch") {
10125
+ return `Watch execution ${execution.id} for output or deadline.`;
10126
+ }
10127
+ if (execution.waitPolicy === "block_lead_until_complete") {
10128
+ return "Lead should wait for this execution to finish.";
10129
+ }
10130
+ return "Execution is active.";
10131
+ }
10132
+ function truncateText2(value, maxChars) {
10133
+ const normalized = value.replace(/\s+/g, " ").trim();
10134
+ if (normalized.length <= maxChars) {
10135
+ return normalized;
10136
+ }
10137
+ return `${normalized.slice(0, Math.max(0, maxChars - 3))}...`;
10138
+ }
10139
+ var init_scene = __esm({
10140
+ "src/runtime/scene.ts"() {
10141
+ "use strict";
10142
+ }
10143
+ });
10144
+
9884
10145
  // src/runtime/status.ts
9885
10146
  var status_exports = {};
9886
10147
  __export(status_exports, {
@@ -9901,7 +10162,7 @@ async function buildRuntimeStatus(rootDir) {
9901
10162
  ]);
9902
10163
  const sessions = sessionRead.sessions.map(summarizeSession);
9903
10164
  const taskLifecycle = sessions[0] ? readTaskLifecycleStatus(paths.rootDir, sessions[0].id) : void 0;
9904
- return {
10165
+ const statusWithoutScene = {
9905
10166
  rootDir: paths.rootDir,
9906
10167
  stateDir: paths.kittyDir,
9907
10168
  sessions: {
@@ -9922,6 +10183,10 @@ async function buildRuntimeStatus(rootDir) {
9922
10183
  executions: control.executions,
9923
10184
  wakeSignals: control.wakeSignals
9924
10185
  };
10186
+ return {
10187
+ ...statusWithoutScene,
10188
+ scene: buildRuntimeScene(statusWithoutScene)
10189
+ };
9925
10190
  }
9926
10191
  function summarizeProjectMap2(projectMap) {
9927
10192
  return {
@@ -10101,6 +10366,7 @@ var init_status = __esm({
10101
10366
  init_memory2();
10102
10367
  init_projectContext();
10103
10368
  init_executionSummary();
10369
+ init_scene();
10104
10370
  init_store3();
10105
10371
  DEFAULT_RECENT_LIMIT = 10;
10106
10372
  }
@@ -12157,6 +12423,252 @@ var init_initialConfig = __esm({
12157
12423
  }
12158
12424
  });
12159
12425
 
12426
+ // src/cli/commands/runtimeStatusPresenter.ts
12427
+ var runtimeStatusPresenter_exports = {};
12428
+ __export(runtimeStatusPresenter_exports, {
12429
+ formatRuntimeStatusText: () => formatRuntimeStatusText
12430
+ });
12431
+ function formatRuntimeStatusText(status) {
12432
+ const lines = [];
12433
+ lines.push(`Project: ${status.rootDir}`);
12434
+ lines.push(`State: ${status.stateDir}`);
12435
+ lines.push("");
12436
+ lines.push("Scene:");
12437
+ lines.push(`- Now: ${status.scene.headline}`);
12438
+ lines.push(`- Focus: ${status.scene.focus}`);
12439
+ lines.push(`- Next: ${status.scene.nextAction}`);
12440
+ lines.push(`- Blocked: ${status.scene.blocked}`);
12441
+ lines.push(`- Background: ${status.scene.background.active} active / ${status.scene.background.blocked} need attention`);
12442
+ lines.push(`- Background next: ${status.scene.background.nextAction}`);
12443
+ lines.push(`- Skills: ${status.scene.skills.ready}/${status.scene.skills.total} ready; ${status.scene.skills.nextAction}`);
12444
+ lines.push(`- Memory: ${status.scene.memory.assets} asset(s), session=${status.scene.memory.latestSessionMemory ? "yes" : "no"}; ${status.scene.memory.nextAction}`);
12445
+ lines.push(`- Cost: ${status.scene.cost}`);
12446
+ lines.push(`- Recovery: ${status.scene.recovery}`);
12447
+ lines.push("");
12448
+ lines.push("Current workspace:");
12449
+ lines.push(`- Focus: ${status.scene.focus}`);
12450
+ lines.push(`- Session: ${readSessionLine(status)}`);
12451
+ lines.push(`- Next: ${status.scene.nextAction}`);
12452
+ lines.push(`- Blocked: ${status.scene.blocked}`);
12453
+ lines.push(`- Context budget: ${readContextBudgetLine(status)}`);
12454
+ lines.push(`- Workset: ${status.sessions.latest?.workset ? `${status.sessions.latest.workset.total} file(s)` : "none"}`);
12455
+ lines.push(`- Memory: ${status.memory.assets.length > 0 ? `${status.memory.assets.length} asset(s)` : "none"}`);
12456
+ lines.push(`- Skills: ${status.skills.ready}/${status.skills.total} ready`);
12457
+ lines.push(`- Model cache: ${readModelCacheLine(status)}`);
12458
+ lines.push(`- Project map: ${status.projectMap ? "ready" : "missing"}`);
12459
+ lines.push(`- Executions: ${status.executions.active.length} active / ${status.executions.total} total`);
12460
+ lines.push(`- Wake signals: ${status.wakeSignals.recent.length}`);
12461
+ if (status.taskLifecycle) {
12462
+ lines.push("");
12463
+ lines.push("Task lifecycle:");
12464
+ lines.push([
12465
+ status.taskLifecycle.stage,
12466
+ status.taskLifecycle.reason ? `reason=${status.taskLifecycle.reason}` : void 0,
12467
+ status.taskLifecycle.updatedAt
12468
+ ].filter(Boolean).join(" "));
12469
+ }
12470
+ if (status.sessions.latest) {
12471
+ lines.push("");
12472
+ lines.push("Latest session:");
12473
+ lines.push([
12474
+ status.sessions.latest.id,
12475
+ status.sessions.latest.title ?? "(untitled)",
12476
+ `messages=${status.sessions.latest.messageCount}`,
12477
+ status.sessions.latest.hasMemory ? "memory=yes" : "memory=no"
12478
+ ].join(" "));
12479
+ }
12480
+ if (status.projectMap) {
12481
+ lines.push("");
12482
+ lines.push("Project map:");
12483
+ lines.push([
12484
+ `dirs=${status.projectMap.topLevelDirectories.slice(0, 6).join(", ") || "none"}`,
12485
+ `scripts=${status.projectMap.packageScripts.slice(0, 6).join(", ") || "none"}`,
12486
+ `tests=${status.projectMap.testDirectories.slice(0, 4).join(", ") || "none"}`,
12487
+ status.projectMap.git.available ? `git=${status.projectMap.git.hasChanges ? "changed" : "clean"}` : "git=unavailable"
12488
+ ].join(" "));
12489
+ }
12490
+ if (status.sessions.latest?.workset?.files.length) {
12491
+ lines.push("");
12492
+ lines.push("Workset:");
12493
+ for (const file of status.sessions.latest.workset.files) {
12494
+ lines.push([
12495
+ file.path,
12496
+ `read=${file.readCount}`,
12497
+ `changed=${file.changedCount}`,
12498
+ `last=${file.lastTool}`,
12499
+ file.lastChangeId ? `change=${file.lastChangeId}` : void 0,
12500
+ file.reason ? `reason=${file.reason}` : void 0
12501
+ ].filter(Boolean).join(" "));
12502
+ }
12503
+ }
12504
+ if (status.sessions.latest?.contextBudget?.promptHotspots.length) {
12505
+ lines.push("");
12506
+ lines.push("Context budget hotspots:");
12507
+ for (const hotspot of status.sessions.latest.contextBudget.promptHotspots.slice(0, 3)) {
12508
+ lines.push([
12509
+ hotspot.layer,
12510
+ hotspot.title,
12511
+ `chars=${hotspot.chars}`,
12512
+ `lines=${hotspot.lines}`
12513
+ ].join(" "));
12514
+ }
12515
+ }
12516
+ if (status.sessions.latest?.contextBudget?.sources.length) {
12517
+ lines.push("");
12518
+ lines.push("Context budget sources:");
12519
+ for (const source of status.sessions.latest.contextBudget.sources) {
12520
+ lines.push([
12521
+ source.name,
12522
+ `chars=${source.chars}`,
12523
+ source.messages === void 0 ? void 0 : `messages=${source.messages}`
12524
+ ].filter(Boolean).join(" "));
12525
+ }
12526
+ }
12527
+ if (status.sessions.latest?.contextBudget?.cacheLayout) {
12528
+ const layout = status.sessions.latest.contextBudget.cacheLayout;
12529
+ const totalChars = layout.stablePrefixChars + layout.volatileTailChars;
12530
+ const stableRatio = totalChars > 0 ? `${Math.round(layout.stablePrefixChars / totalChars * 100)}%` : "unknown";
12531
+ lines.push("");
12532
+ lines.push("Cache layout:");
12533
+ lines.push([
12534
+ `stable=${layout.stablePrefixFingerprint}`,
12535
+ `stableChars=${layout.stablePrefixChars}`,
12536
+ `tail=${layout.volatileTailFingerprint}`,
12537
+ `tailChars=${layout.volatileTailChars}`,
12538
+ `stableRatio=${stableRatio}`
12539
+ ].join(" "));
12540
+ lines.push([
12541
+ `stableSources=${layout.stableSources.join(",") || "none"}`,
12542
+ `volatileSources=${layout.volatileSources.join(",") || "none"}`
12543
+ ].join(" "));
12544
+ }
12545
+ if (status.modelRequests.recent.length > 0) {
12546
+ lines.push("");
12547
+ lines.push("Recent model requests:");
12548
+ for (const request of status.modelRequests.recent.slice(0, 5)) {
12549
+ lines.push([
12550
+ request.model ?? "unknown-model",
12551
+ request.provider ? `provider=${request.provider}` : void 0,
12552
+ request.durationMs === void 0 ? void 0 : `duration=${request.durationMs}ms`,
12553
+ request.usage ? formatUsage(request.usage) : "usage=unavailable"
12554
+ ].filter(Boolean).join(" "));
12555
+ }
12556
+ }
12557
+ if (status.executions.active.length > 0) {
12558
+ lines.push("");
12559
+ lines.push("Active executions:");
12560
+ for (const execution of status.scene.executions) {
12561
+ lines.push([
12562
+ execution.id,
12563
+ execution.kind,
12564
+ execution.status,
12565
+ `risk=${execution.risk}`,
12566
+ `summary=${truncateCliValue(execution.summary, 80)}`,
12567
+ `next=${execution.nextAction}`
12568
+ ].filter(Boolean).join(" "));
12569
+ if (execution.lastOutput) {
12570
+ lines.push(` lastOutput=${execution.lastOutput}`);
12571
+ }
12572
+ }
12573
+ }
12574
+ if (status.executions.recent.length > 0) {
12575
+ lines.push("");
12576
+ lines.push("Recent executions:");
12577
+ for (const execution of status.executions.recent.slice(0, 5)) {
12578
+ lines.push([
12579
+ execution.id,
12580
+ execution.kind,
12581
+ execution.status,
12582
+ execution.actorName ? `actor=${execution.actorName}` : void 0,
12583
+ execution.summary ? truncateCliValue(execution.summary, 80) : void 0,
12584
+ execution.assignment?.expectedOutput ? `expected=${truncateCliValue(execution.assignment.expectedOutput, 60)}` : void 0
12585
+ ].filter(Boolean).join(" "));
12586
+ }
12587
+ }
12588
+ if (status.memory.assets.length > 0) {
12589
+ lines.push("");
12590
+ lines.push("Memory:");
12591
+ for (const memory of status.memory.assets.slice(0, 5)) {
12592
+ lines.push([
12593
+ memory.id,
12594
+ memory.kind,
12595
+ `bytes=${memory.size}`,
12596
+ memory.evidenceRefs.length > 0 ? `evidence=${memory.evidenceRefs.join(",")}` : void 0,
12597
+ memory.path
12598
+ ].filter(Boolean).join(" "));
12599
+ }
12600
+ }
12601
+ if (status.skills.needsAttention.length > 0) {
12602
+ lines.push("");
12603
+ lines.push("Skills needing attention:");
12604
+ for (const skill of status.skills.needsAttention) {
12605
+ lines.push([
12606
+ skill.name,
12607
+ skill.path,
12608
+ `resources=${skill.resources}`,
12609
+ `dependencies=${skill.dependencies}`,
12610
+ skill.issues.length > 0 ? `issues=${skill.issues.join("; ")}` : void 0
12611
+ ].filter(Boolean).join(" "));
12612
+ }
12613
+ }
12614
+ return `${lines.join("\n")}
12615
+ `;
12616
+ }
12617
+ function readSessionLine(status) {
12618
+ if (!status.sessions.latest) {
12619
+ return "none";
12620
+ }
12621
+ return `${status.sessions.latest.id} (${status.sessions.latest.messageCount} message(s))`;
12622
+ }
12623
+ function readContextBudgetLine(status) {
12624
+ const budget = status.sessions.latest?.contextBudget;
12625
+ if (!budget) {
12626
+ return "none";
12627
+ }
12628
+ const percent = Math.round(budget.usageRatio * 100);
12629
+ return [
12630
+ `${budget.estimatedChars}/${budget.limitChars} chars`,
12631
+ `${percent}%`,
12632
+ budget.compressed ? `compressed=${budget.compressionMode}` : "compressed=no",
12633
+ `reason=${budget.compressionReason}`
12634
+ ].join(" ");
12635
+ }
12636
+ function readModelCacheLine(status) {
12637
+ const latest = status.modelRequests.recent[0];
12638
+ if (!latest) {
12639
+ return "none";
12640
+ }
12641
+ if (!latest.usage) {
12642
+ return latest.usageAvailable ? "usage unavailable" : "usage unavailable from provider";
12643
+ }
12644
+ const cacheTokens = latest.usage.cacheHitTokens ?? latest.usage.cacheReadTokens;
12645
+ const missTokens = latest.usage.cacheMissTokens;
12646
+ const rate = latest.usage.cacheHitRate === void 0 ? void 0 : `${Math.round(latest.usage.cacheHitRate * 100)}%`;
12647
+ return [
12648
+ cacheTokens === void 0 ? "cached=unknown" : `cached=${cacheTokens}`,
12649
+ missTokens === void 0 ? void 0 : `miss=${missTokens}`,
12650
+ rate ? `hit=${rate}` : void 0
12651
+ ].filter(Boolean).join(" ");
12652
+ }
12653
+ function formatUsage(usage) {
12654
+ return [
12655
+ usage.inputTokens === void 0 ? void 0 : `input=${usage.inputTokens}`,
12656
+ usage.outputTokens === void 0 ? void 0 : `output=${usage.outputTokens}`,
12657
+ usage.reasoningTokens === void 0 ? void 0 : `reasoning=${usage.reasoningTokens}`,
12658
+ usage.cacheHitTokens === void 0 ? void 0 : `cacheHit=${usage.cacheHitTokens}`,
12659
+ usage.cacheReadTokens === void 0 ? void 0 : `cacheRead=${usage.cacheReadTokens}`,
12660
+ usage.cacheCreationTokens === void 0 ? void 0 : `cacheWrite=${usage.cacheCreationTokens}`,
12661
+ usage.cacheMissTokens === void 0 ? void 0 : `cacheMiss=${usage.cacheMissTokens}`,
12662
+ usage.cacheHitRate === void 0 ? void 0 : `hit=${Math.round(usage.cacheHitRate * 100)}%`
12663
+ ].filter(Boolean).join(" ");
12664
+ }
12665
+ var init_runtimeStatusPresenter = __esm({
12666
+ "src/cli/commands/runtimeStatusPresenter.ts"() {
12667
+ "use strict";
12668
+ init_cliValues();
12669
+ }
12670
+ });
12671
+
12160
12672
  // src/agent/turn/persistence.ts
12161
12673
  async function initializeTurnSession(session, input, sessionStore, source = "external") {
12162
12674
  const appended = await sessionStore.appendMessages(session, [
@@ -226726,14 +227238,6 @@ function createWebInputPort(wss) {
226726
227238
  pendingResolve = resolve;
226727
227239
  });
226728
227240
  },
226729
- readMultiline(_promptLabel) {
226730
- return this.readInput(_promptLabel).then((result) => {
226731
- if (result.kind === "submit") {
226732
- return { kind: "submit", value: result.value };
226733
- }
226734
- return { kind: "cancel" };
226735
- });
226736
- },
226737
227241
  bindInterrupt(handler) {
226738
227242
  interruptHandlers.add(handler);
226739
227243
  return () => {
@@ -227231,6 +227735,9 @@ var init_lifecycle2 = __esm({
227231
227735
  function listEvaluationChecks() {
227232
227736
  return [...EVALUATION_CHECK_IDS];
227233
227737
  }
227738
+ function listEvaluationScenarios() {
227739
+ return [...EVALUATION_SCENARIOS];
227740
+ }
227234
227741
  async function runEvaluationCheck(id, rootDir) {
227235
227742
  try {
227236
227743
  switch (id) {
@@ -227267,6 +227774,9 @@ async function runEvaluationCheck(id, rootDir) {
227267
227774
  case "cache-economy-ready": {
227268
227775
  return await runCacheEconomyCheck(id);
227269
227776
  }
227777
+ case "production-scene-ready": {
227778
+ return await runProductionSceneCheck(id, rootDir);
227779
+ }
227270
227780
  case "host-turn-boundary-runs": {
227271
227781
  return await runHostTurnBoundaryCheck(id, rootDir);
227272
227782
  }
@@ -227291,6 +227801,7 @@ async function runCacheEconomyCheck(id) {
227291
227801
  const { resolveProviderCachePolicy: resolveProviderCachePolicy2 } = await Promise.resolve().then(() => (init_cachePolicy(), cachePolicy_exports));
227292
227802
  const { buildCompressedContextRequest: buildCompressedContextRequest2 } = await Promise.resolve().then(() => (init_builder(), builder_exports));
227293
227803
  const { buildContextRuntimePromptLayers: buildContextRuntimePromptLayers2 } = await Promise.resolve().then(() => (init_prompt3(), prompt_exports));
227804
+ const { renderPromptLayers: renderPromptLayers2 } = await Promise.resolve().then(() => (init_format(), format_exports));
227294
227805
  const { getInitialRuntimeConfig: getInitialRuntimeConfig2 } = await Promise.resolve().then(() => (init_initialConfig(), initialConfig_exports));
227295
227806
  const { getAppPaths: getAppPaths2 } = await Promise.resolve().then(() => (init_paths(), paths_exports));
227296
227807
  const { resolveTelegramRuntimeConfig: resolveTelegramRuntimeConfig2 } = await Promise.resolve().then(() => (init_hosts(), hosts_exports));
@@ -227326,7 +227837,7 @@ async function runCacheEconomyCheck(id) {
227326
227837
  instructionText: "",
227327
227838
  instructionTruncated: false,
227328
227839
  ignoreRules: [],
227329
- skills: []
227840
+ skills: [buildCostSkillFixture()]
227330
227841
  };
227331
227842
  const firstPrompt = buildContextRuntimePromptLayers2({
227332
227843
  cwd: process.cwd(),
@@ -227382,18 +227893,37 @@ async function runCacheEconomyCheck(id) {
227382
227893
  };
227383
227894
  const first = buildCompressedContextRequest2(
227384
227895
  firstPrompt,
227385
- [{ role: "user", content: "first", createdAt: "2026-06-16T00:00:00.000Z" }],
227896
+ [
227897
+ { role: "user", content: "first", createdAt: "2026-06-16T00:00:00.000Z" },
227898
+ { role: "tool", name: "bash", content: `large output ${"x".repeat(2e4)}`, createdAt: "2026-06-16T00:00:01.000Z" }
227899
+ ],
227386
227900
  requestConfig
227387
227901
  );
227388
227902
  const second = buildCompressedContextRequest2(
227389
227903
  secondPrompt,
227390
227904
  [
227391
227905
  { role: "user", content: "first", createdAt: "2026-06-16T00:00:00.000Z" },
227906
+ { role: "tool", name: "bash", content: `large output ${"x".repeat(2e4)}`, createdAt: "2026-06-16T00:00:01.000Z" },
227392
227907
  { role: "user", content: "second", createdAt: "2026-06-16T00:01:00.000Z" }
227393
227908
  ],
227394
227909
  requestConfig
227395
227910
  );
227396
- if (deepSeek?.cacheHitRate !== 0.8 || openai?.cacheReadTokens !== 960 || !policy.promptCacheKey || first.cacheLayout?.stablePrefixFingerprint !== second.cacheLayout?.stablePrefixFingerprint || first.cacheLayout?.volatileTailFingerprint === second.cacheLayout?.volatileTailFingerprint) {
227911
+ const compactedLargeOutput = buildCompressedContextRequest2(
227912
+ firstPrompt,
227913
+ [
227914
+ { role: "user", content: "large output", createdAt: "2026-06-16T00:00:00.000Z" },
227915
+ { role: "tool", name: "bash", content: `large output ${"x".repeat(2e4)}`, createdAt: "2026-06-16T00:00:01.000Z" },
227916
+ { role: "user", content: "continue", createdAt: "2026-06-16T00:00:02.000Z" }
227917
+ ],
227918
+ {
227919
+ contextWindowMessages: 3,
227920
+ model: "gpt-5.5",
227921
+ maxContextChars: 8e3,
227922
+ contextSummaryChars: 600
227923
+ }
227924
+ );
227925
+ const renderedPrompt = renderPromptLayers2(firstPrompt);
227926
+ if (deepSeek?.cacheHitRate !== 0.8 || openai?.cacheReadTokens !== 960 || !policy.promptCacheKey || first.cacheLayout?.stablePrefixFingerprint !== second.cacheLayout?.stablePrefixFingerprint || first.cacheLayout?.volatileTailFingerprint === second.cacheLayout?.volatileTailFingerprint || renderedPrompt.includes("FULL_SKILL_BODY_MUST_NOT_ENTER_DEFAULT_CONTEXT") || !renderedPrompt.includes("cost-skill") || (compactedLargeOutput.cacheLayout?.volatileTailChars ?? Number.POSITIVE_INFINITY) >= 2e4) {
227397
227927
  return {
227398
227928
  id,
227399
227929
  status: "failed",
@@ -227402,9 +227932,141 @@ async function runCacheEconomyCheck(id) {
227402
227932
  }
227403
227933
  return passed(
227404
227934
  id,
227405
- `cache economy ready: deepseekHit=${deepSeek?.cacheHitRate}, openaiCached=${openai?.cacheReadTokens}, stablePrefix=${first.cacheLayout?.stablePrefixFingerprint ?? "unknown"}`
227935
+ `cache economy ready: deepseekHit=${deepSeek?.cacheHitRate}, openaiCached=${openai?.cacheReadTokens}, stablePrefix=${first.cacheLayout?.stablePrefixFingerprint ?? "unknown"}, stableChars=${first.cacheLayout?.stablePrefixChars ?? 0}, compactedTailChars=${compactedLargeOutput.cacheLayout?.volatileTailChars ?? 0}, skillIndex=only`
227406
227936
  );
227407
227937
  }
227938
+ async function runProductionSceneCheck(id, rootDir) {
227939
+ const { buildRuntimeStatus: buildRuntimeStatus2 } = await Promise.resolve().then(() => (init_status(), status_exports));
227940
+ const { formatRuntimeStatusText: formatRuntimeStatusText2 } = await Promise.resolve().then(() => (init_runtimeStatusPresenter(), runtimeStatusPresenter_exports));
227941
+ const { ControlPlaneLedger: ControlPlaneLedger2 } = await Promise.resolve().then(() => (init_ledger(), ledger_exports));
227942
+ const { SessionStore: SessionStore2 } = await Promise.resolve().then(() => (init_store3(), store_exports2));
227943
+ const workspace = await prepareCheckWorkspace(rootDir, "production-scene");
227944
+ await import_promises33.default.mkdir(import_node_path46.default.join(workspace, "skills", "scene-skill"), { recursive: true });
227945
+ await import_promises33.default.writeFile(
227946
+ import_node_path46.default.join(workspace, "skills", "scene-skill", "SKILL.md"),
227947
+ [
227948
+ "---",
227949
+ "name: scene-skill",
227950
+ "description: Checks production scene readiness.",
227951
+ "requires: node",
227952
+ "---",
227953
+ "Use when validating production runtime scene."
227954
+ ].join("\n"),
227955
+ "utf8"
227956
+ );
227957
+ const sessionStore = new SessionStore2(import_node_path46.default.join(workspace, ".kitty", "sessions"), {
227958
+ memorySessionsDir: import_node_path46.default.join(workspace, ".kitty", "memory", "sessions")
227959
+ });
227960
+ const session = await sessionStore.save({
227961
+ ...await sessionStore.create(workspace),
227962
+ title: "Production scene check",
227963
+ sessionMemory: {
227964
+ version: 1,
227965
+ summary: "User needs production runtime visibility.",
227966
+ updatedAt: "2026-06-18T00:00:00.000Z"
227967
+ },
227968
+ contextBudget: {
227969
+ version: 1,
227970
+ limitChars: 1e5,
227971
+ estimatedChars: 5e4,
227972
+ remainingChars: 5e4,
227973
+ usageRatio: 0.5,
227974
+ compressed: false,
227975
+ compressionMode: "none",
227976
+ compressionReason: "within_budget",
227977
+ sources: [{ name: "nearFieldConversation", chars: 2e4, messages: 4 }],
227978
+ promptHotspots: [],
227979
+ cacheLayout: {
227980
+ stablePrefixFingerprint: "scene-stable",
227981
+ volatileTailFingerprint: "scene-tail",
227982
+ stablePrefixChars: 3e4,
227983
+ volatileTailChars: 2e4,
227984
+ stableSources: ["staticPrompt"],
227985
+ volatileSources: ["runtimeFacts", "nearFieldConversation"]
227986
+ }
227987
+ }
227988
+ });
227989
+ const eventsDir = import_node_path46.default.join(workspace, ".kitty", "observability", "events");
227990
+ await import_promises33.default.mkdir(eventsDir, { recursive: true });
227991
+ await import_promises33.default.writeFile(
227992
+ import_node_path46.default.join(eventsDir, "2026-06-18.jsonl"),
227993
+ JSON.stringify({
227994
+ version: 1,
227995
+ timestamp: "2026-06-18T00:00:00.000Z",
227996
+ event: "model.request",
227997
+ status: "completed",
227998
+ model: "gpt-5.5",
227999
+ durationMs: 123,
228000
+ details: {
228001
+ provider: "openai",
228002
+ usageAvailable: true,
228003
+ usage: {
228004
+ totalTokens: 1e3,
228005
+ cacheReadTokens: 700,
228006
+ cacheMissTokens: 300,
228007
+ cacheHitRate: 0.7
228008
+ }
228009
+ }
228010
+ }) + "\n",
228011
+ "utf8"
228012
+ );
228013
+ const ledger = new ControlPlaneLedger2(workspace);
228014
+ try {
228015
+ ledger.executions.create({
228016
+ kind: "background",
228017
+ status: "running",
228018
+ command: "long production task",
228019
+ cwd: workspace,
228020
+ requestedBy: "lead",
228021
+ pid: process.pid,
228022
+ sessionId: session.id
228023
+ });
228024
+ } finally {
228025
+ ledger.close();
228026
+ }
228027
+ const status = await buildRuntimeStatus2(workspace);
228028
+ const text = formatRuntimeStatusText2(status);
228029
+ if (status.scene.background.active !== 1 || status.scene.background.blocked !== 1 || !status.scene.cost.includes("1000 tokens") || !status.scene.cost.includes("700 cached") || status.scene.skills.ready < 1 || status.scene.memory.assets < 1 || !status.scene.memory.latestSessionMemory || status.skills.ready < 1 || status.memory.assets.length < 1 || !text.includes("Scene:") || !text.includes("Background next:") || !text.includes("Cost:")) {
228030
+ return {
228031
+ id,
228032
+ status: "failed",
228033
+ fact: `production scene incomplete: background=${status.scene.background.active}/${status.scene.background.blocked}, skills=${status.skills.ready}/${status.skills.total}, memory=${status.memory.assets.length}, cost=${status.scene.cost}`
228034
+ };
228035
+ }
228036
+ return passed(
228037
+ id,
228038
+ `production scene ready: background=${status.scene.background.active}/${status.scene.background.blocked}, skills=${status.skills.ready}/${status.skills.total}, memory=${status.memory.assets.length}, cost=${status.scene.cost}`
228039
+ );
228040
+ }
228041
+ function buildCostSkillFixture() {
228042
+ return {
228043
+ name: "cost-skill",
228044
+ description: "Loaded only when needed.",
228045
+ path: "skills/cost-skill/SKILL.md",
228046
+ absolutePath: "skills/cost-skill/SKILL.md",
228047
+ body: "FULL_SKILL_BODY_MUST_NOT_ENTER_DEFAULT_CONTEXT",
228048
+ dependencies: [],
228049
+ resources: [{
228050
+ path: "references/cost.md",
228051
+ size: 1e5,
228052
+ kind: "references"
228053
+ }],
228054
+ health: {
228055
+ status: "ready",
228056
+ bodyPresent: true,
228057
+ resourceCount: 1,
228058
+ dependencyCount: 0,
228059
+ resourceGroups: {
228060
+ references: 1,
228061
+ scripts: 0,
228062
+ examples: 0,
228063
+ assets: 0,
228064
+ other: 0
228065
+ },
228066
+ issues: []
228067
+ }
228068
+ };
228069
+ }
227408
228070
  async function runHostTurnBoundaryCheck(id, rootDir) {
227409
228071
  const { runHostTurn: runHostTurn2 } = await Promise.resolve().then(() => (init_turn2(), turn_exports));
227410
228072
  const { SessionEventStore: SessionEventStore2 } = await Promise.resolve().then(() => (init_events(), events_exports));
@@ -227602,7 +228264,7 @@ async function closeWebSocketServer(wss) {
227602
228264
  });
227603
228265
  });
227604
228266
  }
227605
- var import_promises33, import_node_path46, import_node_events, import_ws, EVALUATION_CHECK_IDS;
228267
+ var import_promises33, import_node_path46, import_node_events, import_ws, EVALUATION_CHECK_IDS, EVALUATION_SCENARIOS;
227606
228268
  var init_checks = __esm({
227607
228269
  "src/evaluation/checks.ts"() {
227608
228270
  "use strict";
@@ -227619,10 +228281,79 @@ var init_checks = __esm({
227619
228281
  "skill-packages-readable",
227620
228282
  "config-preflight-readable",
227621
228283
  "cache-economy-ready",
228284
+ "production-scene-ready",
227622
228285
  "host-turn-boundary-runs",
227623
228286
  "remote-entrypoints-available",
227624
228287
  "recovery-drills-pass"
227625
228288
  ];
228289
+ EVALUATION_SCENARIOS = [
228290
+ {
228291
+ id: "runtime-status-builds",
228292
+ title: "\u5F53\u524D\u73B0\u573A\u53EF\u5BA1\u9605",
228293
+ userPath: "\u7528\u6237\u8FD0\u884C `kitty status` \u65F6\uFF0C\u53EF\u4EE5\u770B\u5230 session\u3001context\u3001memory\u3001skills\u3001execution \u548C cache \u7684\u5F53\u524D\u4E8B\u5B9E\u3002",
228294
+ evidence: "\u6784\u5EFA runtime status\uFF0C\u5E76\u786E\u8BA4 sessions / executions \u7B49\u73B0\u573A\u6458\u8981\u53EF\u7528\u3002"
228295
+ },
228296
+ {
228297
+ id: "project-map-builds",
228298
+ title: "\u8FDB\u5165\u4ED3\u5E93\u80FD\u5FEB\u901F\u5B9A\u5411",
228299
+ userPath: "\u7528\u6237\u628A Kitty \u6253\u5F00\u5728\u4E00\u4E2A\u4ED3\u5E93\u91CC\uFF0C\u6A21\u578B\u80FD\u770B\u5230\u76EE\u5F55\u3001\u5165\u53E3\u3001\u811A\u672C\u3001\u6D4B\u8BD5\u3001\u9879\u76EE\u6587\u6863\u548C git \u4E8B\u5B9E\u3002",
228300
+ evidence: "\u6784\u5EFA project map\uFF0C\u5E76\u786E\u8BA4\u76EE\u5F55\u3001\u811A\u672C\u548C\u4ED3\u5E93\u4E8B\u5B9E\u53EF\u8BFB\u3002"
228301
+ },
228302
+ {
228303
+ id: "memory-assets-readable",
228304
+ title: "\u8BB0\u5FC6\u8D44\u4EA7\u53EF\u5BA1\u9605",
228305
+ userPath: "\u7528\u6237\u53EF\u4EE5\u67E5\u770B session/project/user/evidence memory\uFF0C\u4E0D\u9760\u9690\u85CF\u4E0A\u4E0B\u6587\u731C\u5386\u53F2\u3002",
228306
+ evidence: "\u679A\u4E3E runtime memory assets\uFF0C\u5E76\u786E\u8BA4\u8D44\u4EA7\u7D22\u5F15\u53EF\u8BFB\u3002"
228307
+ },
228308
+ {
228309
+ id: "extension-surface-current",
228310
+ title: "\u5DE5\u5177\u9762\u53EA\u66B4\u9732\u5F53\u524D\u80FD\u529B",
228311
+ userPath: "\u9ED8\u8BA4 agent \u6253\u5F00\u5F53\u524D\u771F\u5B9E extensions\uFF0C\u4E0D\u590D\u6D3B\u5DF2\u5220\u9664\u80FD\u529B\u3002",
228312
+ evidence: "\u8BFB\u53D6 extension registry\uFF0C\u5E76\u786E\u8BA4\u9ED8\u8BA4\u542F\u7528\u9762\u6765\u81EA\u5F53\u524D\u5B9A\u4E49\u3002"
228313
+ },
228314
+ {
228315
+ id: "skill-packages-readable",
228316
+ title: "\u65B9\u6CD5\u5305\u6309\u9700\u53EF\u7528",
228317
+ userPath: "\u6A21\u578B\u80FD\u5148\u770B\u5230 skill \u7D22\u5F15\uFF0C\u5FC5\u8981\u65F6\u518D\u52A0\u8F7D\u6B63\u6587\u3001\u8D44\u6E90\u6216\u811A\u672C\u3002",
228318
+ evidence: "\u52A0\u8F7D project context\uFF0C\u5E76\u786E\u8BA4 runtime skills \u53EF\u53D1\u73B0\u3002"
228319
+ },
228320
+ {
228321
+ id: "config-preflight-readable",
228322
+ title: "\u9996\u6B21\u914D\u7F6E\u8DEF\u5F84\u6E05\u695A",
228323
+ userPath: "\u7528\u6237\u8FD0\u884C `kitty init` / `kitty doctor` \u540E\uFF0C\u80FD\u77E5\u9053 `.kitty/.env` \u662F\u5426\u5B8C\u6574\u3001\u4E0B\u4E00\u6B65\u8865\u4EC0\u4E48\u3002",
228324
+ evidence: "\u6267\u884C config preflight\uFF0C\u5E76\u786E\u8BA4\u672C\u5730\u6A21\u677F\u548C env contract \u53EF\u68C0\u67E5\u3002"
228325
+ },
228326
+ {
228327
+ id: "cache-economy-ready",
228328
+ title: "\u6210\u672C\u4E8B\u5B9E\u53EF\u5BA1\u9605",
228329
+ userPath: "\u7528\u6237\u80FD\u770B\u5230 provider usage\u3001cache hit/miss\u3001\u7A33\u5B9A\u524D\u7F00\u548C\u6309\u9700 skill \u8FB9\u754C\uFF0C\u800C\u4E0D\u662F\u53EA\u770B\u5230 token \u603B\u6570\u3002",
228330
+ evidence: "\u9A8C\u8BC1 usage \u5F52\u4E00\u5316\u3001provider cache policy\u3001stable/volatile prompt fingerprint\u3001skill index boundary \u548C\u5927\u8F93\u51FA\u538B\u7F29\u3002"
228331
+ },
228332
+ {
228333
+ id: "production-scene-ready",
228334
+ title: "\u751F\u4EA7\u73B0\u573A\u4E00\u773C\u53EF\u8BFB",
228335
+ userPath: "\u7528\u6237\u8FD0\u884C `kitty status` \u6216 `kitty background` \u65F6\uFF0C\u80FD\u770B\u5230\u5F53\u524D\u73B0\u573A\u3001\u540E\u53F0\u98CE\u9669\u3001\u4E0B\u4E00\u6B65\u3001\u6062\u590D\u72B6\u6001\u3001\u6210\u672C\u3001skill \u548C memory \u53EF\u5BA1\u9605\u6027\u3002",
228336
+ evidence: "\u6784\u5EFA\u5E26 session\u3001memory\u3001cache\u3001skill\u3001background \u548C provider usage \u7684 runtime scene\uFF0C\u5E76\u786E\u8BA4 scene \u4E0E CLI \u6587\u672C\u90FD\u66B4\u9732\u5173\u952E\u4E8B\u5B9E\u3002"
228337
+ },
228338
+ {
228339
+ id: "host-turn-boundary-runs",
228340
+ title: "\u4E00\u6B21 agent turn \u6709\u660E\u786E\u8FB9\u754C",
228341
+ userPath: "\u7528\u6237\u53D1\u8D77\u4E00\u6B21\u4EFB\u52A1\u540E\uFF0Chost \u80FD\u8BB0\u5F55 turn \u5F00\u59CB\u3001\u5B8C\u6210\u3001\u5931\u8D25\u6216\u4E2D\u65AD\uFF0C\u4E0D\u628A\u5185\u90E8\u4E8B\u5B9E\u5199\u6210\u7528\u6237\u610F\u56FE\u3002",
228342
+ evidence: "\u7528\u5047 turn \u8DD1 host boundary\uFF0C\u5E76\u786E\u8BA4 session events \u95ED\u73AF\u3002"
228343
+ },
228344
+ {
228345
+ id: "remote-entrypoints-available",
228346
+ title: "\u8FDC\u7A0B\u5165\u53E3\u590D\u7528\u540C\u4E00\u4E3B\u5E72",
228347
+ userPath: "Web / Telegram \u5165\u53E3\u80FD\u63A5\u5165\u540C\u4E00 turn \u8F93\u5165\uFF0C\u4E0D\u5206\u88C2\u6210\u53E6\u4E00\u5957 agent\u3002",
228348
+ evidence: "\u9A8C\u8BC1 web input port\u3001HTML shell \u548C Telegram file turn input \u53EF\u7528\u3002"
228349
+ },
228350
+ {
228351
+ id: "recovery-drills-pass",
228352
+ title: "\u540E\u53F0\u548C\u5B50\u6267\u884C\u53EF\u6062\u590D",
228353
+ userPath: "background \u6216 subagent \u5361\u4F4F\u3001\u6D88\u5931\u3001\u8D85\u65F6\u540E\uFF0CKitty \u80FD reconcile\u3001\u6682\u505C\u7B49\u5F85\u6216\u7EC8\u6B62\u73B0\u573A\u3002",
228354
+ evidence: "\u6F14\u7EC3 stale background\u3001expired lead-wait subagent\u3001running process termination \u548C runtime status\u3002"
228355
+ }
228356
+ ];
227626
228357
  }
227627
228358
  });
227628
228359
 
@@ -227645,22 +228376,25 @@ var init_harness = __esm({
227645
228376
 
227646
228377
  // src/cli/commands/evaluation.ts
227647
228378
  function registerEvaluationCommand(program, options = {}) {
227648
- program.command("eval").description("List or run machine-verifiable evaluation checks.").option("--json", "Print structured JSON.").option("--run", "Run all local evaluation checks.").action(async (commandOptions) => {
227649
- const checks = listEvaluationChecks();
228379
+ program.command("eval").description("List or run product acceptance scenarios.").option("--json", "Print structured JSON.").option("--run", "Run all local evaluation checks.").action(async (commandOptions) => {
228380
+ const scenarios = listEvaluationScenarios();
227650
228381
  const result = commandOptions.run ? await runEvaluationChecks(options.getCwd?.() ?? process.cwd()) : void 0;
227651
228382
  if (commandOptions.json) {
227652
- writeStdoutLine(JSON.stringify({ checks, result }, null, 2));
228383
+ writeStdoutLine(JSON.stringify({ scenarios, result }, null, 2));
227653
228384
  return;
227654
228385
  }
227655
- writeStdoutLine(commandOptions.run ? "Evaluation checks run:" : "Evaluation checks:");
227656
- for (const check of checks) {
227657
- writeStdoutLine(`- ${check}`);
228386
+ writeStdoutLine(commandOptions.run ? "Evaluation scenarios run:" : "Evaluation scenarios:");
228387
+ for (const scenario of scenarios) {
228388
+ writeStdoutLine(`- ${scenario.id}: ${scenario.title}`);
228389
+ writeStdoutLine(` \u7528\u6237\u8DEF\u5F84: ${scenario.userPath}`);
228390
+ writeStdoutLine(` \u673A\u5668\u8BC1\u636E: ${scenario.evidence}`);
227658
228391
  }
227659
228392
  if (result) {
227660
228393
  writeStdoutLine("");
227661
228394
  writeStdoutLine(`Status: ${result.status}`);
227662
228395
  for (const check of result.checks) {
227663
- writeStdoutLine(`${check.status} ${check.id}: ${check.fact}${check.error ? ` (${check.error})` : ""}`);
228396
+ const scenario = scenarios.find((item) => item.id === check.id);
228397
+ writeStdoutLine(`${check.status} ${check.id}${scenario ? ` ${scenario.title}` : ""}: ${check.fact}${check.error ? ` (${check.error})` : ""}`);
227664
228398
  }
227665
228399
  }
227666
228400
  });
@@ -227734,57 +228468,7 @@ var init_session2 = __esm({
227734
228468
  }
227735
228469
  });
227736
228470
 
227737
- // src/cli/commands/sessionPicker.ts
227738
- async function selectCliSession(options) {
227739
- const sessions = await options.sessionStore.list(options.limit ?? DEFAULT_SESSION_PICKER_LIMIT);
227740
- if (sessions.length === 0) {
227741
- return {
227742
- session: await createHostSession(options.sessionStore, options.cwd),
227743
- cwd: options.cwd
227744
- };
227745
- }
227746
- const io = resolveSessionPickerIo(options.io);
227747
- renderSessionPicker({
227748
- sessions,
227749
- io,
227750
- now: io.now()
227751
- });
227752
- while (true) {
227753
- const answer = await io.readChoice("\u9009\u62E9\u4F1A\u8BDD\uFF08\u8F93\u5165\u7F16\u53F7\uFF0C0 \u65B0\u5EFA\uFF09: ");
227754
- if (answer === null) {
227755
- return null;
227756
- }
227757
- const selected = parseSessionPickerChoice(answer, sessions.length);
227758
- if (selected.kind === "new") {
227759
- return {
227760
- session: await createHostSession(options.sessionStore, options.cwd),
227761
- cwd: options.cwd
227762
- };
227763
- }
227764
- if (selected.kind === "existing") {
227765
- const session = sessions[selected.index];
227766
- if (!session) {
227767
- io.writeLine("\u65E0\u6548\u9009\u62E9\uFF0C\u8BF7\u91CD\u65B0\u8F93\u5165\u3002");
227768
- continue;
227769
- }
227770
- return {
227771
- session,
227772
- cwd: options.cwdOverridden ? options.cwd : session.cwd
227773
- };
227774
- }
227775
- io.writeLine("\u65E0\u6548\u9009\u62E9\uFF0C\u8BF7\u8F93\u5165\u5217\u8868\u7F16\u53F7\uFF0C\u6216\u8F93\u5165 0 \u65B0\u5EFA\u4F1A\u8BDD\u3002");
227776
- }
227777
- }
227778
- function renderSessionPicker(options) {
227779
- options.io.writeLine("\u6700\u8FD1\u4F1A\u8BDD");
227780
- options.io.writeLine("0. \u65B0\u5EFA\u4F1A\u8BDD");
227781
- options.sessions.forEach((session, index) => {
227782
- options.io.writeLine(
227783
- `${index + 1}. ${formatSessionPickerTitle(session)} ${formatRelativeSessionTime(session.updatedAt, options.now)}`
227784
- );
227785
- });
227786
- options.io.writeLine();
227787
- }
228471
+ // src/session/picker.ts
227788
228472
  function parseSessionPickerChoice(input, sessionCount) {
227789
228473
  const trimmed = input.trim();
227790
228474
  if (trimmed === "") {
@@ -227842,6 +228526,63 @@ function truncateDisplayTitle(title) {
227842
228526
  const maxChars = 36;
227843
228527
  return chars.length > maxChars ? `${chars.slice(0, maxChars).join("")}...` : title;
227844
228528
  }
228529
+ var init_picker = __esm({
228530
+ "src/session/picker.ts"() {
228531
+ "use strict";
228532
+ }
228533
+ });
228534
+
228535
+ // src/cli/commands/sessionPicker.ts
228536
+ async function selectCliSession(options) {
228537
+ const sessions = await options.sessionStore.list(options.limit ?? DEFAULT_SESSION_PICKER_LIMIT);
228538
+ if (sessions.length === 0) {
228539
+ return {
228540
+ session: await createHostSession(options.sessionStore, options.cwd),
228541
+ cwd: options.cwd
228542
+ };
228543
+ }
228544
+ const io = resolveSessionPickerIo(options.io);
228545
+ renderSessionPicker({
228546
+ sessions,
228547
+ io,
228548
+ now: io.now()
228549
+ });
228550
+ while (true) {
228551
+ const answer = await io.readChoice("\u9009\u62E9\u4F1A\u8BDD\uFF08\u8F93\u5165\u7F16\u53F7\uFF0C0 \u65B0\u5EFA\uFF09: ");
228552
+ if (answer === null) {
228553
+ return null;
228554
+ }
228555
+ const selected = parseSessionPickerChoice(answer, sessions.length);
228556
+ if (selected.kind === "new") {
228557
+ return {
228558
+ session: await createHostSession(options.sessionStore, options.cwd),
228559
+ cwd: options.cwd
228560
+ };
228561
+ }
228562
+ if (selected.kind === "existing") {
228563
+ const session = sessions[selected.index];
228564
+ if (!session) {
228565
+ io.writeLine("\u65E0\u6548\u9009\u62E9\uFF0C\u8BF7\u91CD\u65B0\u8F93\u5165\u3002");
228566
+ continue;
228567
+ }
228568
+ return {
228569
+ session,
228570
+ cwd: options.cwdOverridden ? options.cwd : session.cwd
228571
+ };
228572
+ }
228573
+ io.writeLine("\u65E0\u6548\u9009\u62E9\uFF0C\u8BF7\u8F93\u5165\u5217\u8868\u7F16\u53F7\uFF0C\u6216\u8F93\u5165 0 \u65B0\u5EFA\u4F1A\u8BDD\u3002");
228574
+ }
228575
+ }
228576
+ function renderSessionPicker(options) {
228577
+ options.io.writeLine("\u6700\u8FD1\u4F1A\u8BDD");
228578
+ options.io.writeLine("0. \u65B0\u5EFA\u4F1A\u8BDD");
228579
+ options.sessions.forEach((session, index) => {
228580
+ options.io.writeLine(
228581
+ `${index + 1}. ${formatSessionPickerTitle(session)} ${formatRelativeSessionTime(session.updatedAt, options.now)}`
228582
+ );
228583
+ });
228584
+ options.io.writeLine();
228585
+ }
227845
228586
  function resolveSessionPickerIo(io) {
227846
228587
  return {
227847
228588
  writeLine: io?.writeLine ?? ((text = "") => writeStdoutLine(text)),
@@ -227873,6 +228614,8 @@ var init_sessionPicker = __esm({
227873
228614
  import_node_process2 = __toESM(require("process"));
227874
228615
  init_stdio();
227875
228616
  init_session2();
228617
+ init_picker();
228618
+ init_picker();
227876
228619
  DEFAULT_SESSION_PICKER_LIMIT = 10;
227877
228620
  }
227878
228621
  });
@@ -227944,6 +228687,218 @@ var init_exitGuard = __esm({
227944
228687
  }
227945
228688
  });
227946
228689
 
228690
+ // src/cli/commands/background.ts
228691
+ function registerBackgroundCommand(program, options) {
228692
+ const command = program.command("background").description("Inspect, wait for, or stop background executions.");
228693
+ command.argument("[action]", "Optional action: list, wait, stop").argument("[id]", "Background execution id for wait or stop").option("--json", "Print structured JSON.").option("--timeout-ms <ms>", "Wait timeout in milliseconds.", (value) => Number.parseInt(value, 10), 6e4).action(async (action, id, commandOptions) => {
228694
+ const runtime = await options.resolveRuntime(options.getCliOverrides());
228695
+ const normalizedAction = action ?? "list";
228696
+ if (normalizedAction === "list") {
228697
+ const executions = new BackgroundExecutionStore(runtime.stateRootDir).listAll().map(summarizeExecution).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
228698
+ printBackgroundExecutions(executions, Boolean(commandOptions.json));
228699
+ return;
228700
+ }
228701
+ if (!id) {
228702
+ throw new Error(`background ${normalizedAction} requires an execution id.`);
228703
+ }
228704
+ if (normalizedAction === "wait") {
228705
+ const execution = summarizeExecution(await waitForBackgroundExecution({
228706
+ rootDir: runtime.stateRootDir,
228707
+ id,
228708
+ timeoutMs: commandOptions.timeoutMs
228709
+ }));
228710
+ printBackgroundExecutions([execution], Boolean(commandOptions.json));
228711
+ return;
228712
+ }
228713
+ if (normalizedAction === "stop") {
228714
+ const execution = terminateBackgroundExecution(runtime.stateRootDir, id);
228715
+ await waitForRegisteredBackgroundProcess(id);
228716
+ printBackgroundExecutions([summarizeExecution(execution)], Boolean(commandOptions.json));
228717
+ return;
228718
+ }
228719
+ throw new Error(`Unknown background action: ${normalizedAction}. Use list, wait, or stop.`);
228720
+ });
228721
+ }
228722
+ function printBackgroundExecutions(executions, json) {
228723
+ if (json) {
228724
+ writeStdoutLine(JSON.stringify({ executions }, null, 2));
228725
+ return;
228726
+ }
228727
+ if (executions.length === 0) {
228728
+ ui.info("No background executions recorded.");
228729
+ return;
228730
+ }
228731
+ for (const execution of executions) {
228732
+ writeStdoutLine(formatBackgroundExecution(execution));
228733
+ }
228734
+ }
228735
+ function formatBackgroundExecution(execution) {
228736
+ const scene = buildExecutionScene(execution);
228737
+ return [
228738
+ execution.id,
228739
+ execution.status,
228740
+ `risk=${scene.risk}`,
228741
+ execution.pid === void 0 ? void 0 : `pid=${execution.pid}`,
228742
+ `health=${truncateCliValue(scene.health, 90)}`,
228743
+ execution.deadlineAt ? `deadline=${execution.deadlineAt}` : void 0,
228744
+ `summary=${truncateCliValue(scene.summary, 90)}`,
228745
+ `next=${scene.nextAction}`,
228746
+ scene.lastOutput ? `lastOutput=${truncateCliValue(scene.lastOutput, 120)}` : void 0
228747
+ ].filter(Boolean).join(" ");
228748
+ }
228749
+ var init_background3 = __esm({
228750
+ "src/cli/commands/background.ts"() {
228751
+ "use strict";
228752
+ init_background();
228753
+ init_executionSummary();
228754
+ init_scene();
228755
+ init_console();
228756
+ init_stdio();
228757
+ init_cliValues();
228758
+ }
228759
+ });
228760
+
228761
+ // src/cli/commands/memory.ts
228762
+ function registerMemoryCommand(program, options) {
228763
+ program.command("memory").description("List readable runtime memory assets.").argument("[memoryId]", "Optional runtime memory asset id to read.").option("--delete", "Delete the selected runtime memory asset.").option("--append-to-skill <skillName>", "Append the selected memory asset to a runtime skill references/ file.").option("--create <kind>", "Create a project, user, or evidence memory asset.").option("--title <title>", "Title for --create.").option("--content <content>", "Content for --create.").option("--evidence <refs>", "Comma-separated evidence refs for --create.").option("--scope <scope>", "Scope metadata for --create.").option("--tags <tags>", "Comma-separated tags for --create.").option("--file <fileName>", "Target file name for --append-to-skill.").option("-q, --query <query>", "Search runtime memory assets.").option("--json", "Print structured JSON.").action(async (memoryId, commandOptions) => {
228764
+ const runtime = await options.resolveRuntime(options.getCliOverrides());
228765
+ if (commandOptions.create) {
228766
+ await handleMemoryCreate(runtime.cwd, commandOptions);
228767
+ return;
228768
+ }
228769
+ if (memoryId) {
228770
+ await handleSelectedMemory(runtime.cwd, memoryId, commandOptions);
228771
+ return;
228772
+ }
228773
+ if (commandOptions.query) {
228774
+ await handleMemorySearch(runtime.cwd, commandOptions.query, commandOptions.json === true);
228775
+ return;
228776
+ }
228777
+ await handleMemoryList(runtime.cwd, commandOptions.json === true);
228778
+ });
228779
+ }
228780
+ async function handleMemoryCreate(cwd, options) {
228781
+ const kind = parseWritableMemoryKind(options.create);
228782
+ if (!options.title?.trim()) {
228783
+ throw new Error("--title is required when creating a memory asset.");
228784
+ }
228785
+ if (!options.content?.trim()) {
228786
+ throw new Error("--content is required when creating a memory asset.");
228787
+ }
228788
+ const created = await createRuntimeMemoryAsset({
228789
+ rootDir: cwd,
228790
+ kind,
228791
+ title: options.title,
228792
+ content: options.content,
228793
+ evidenceRefs: parseCsvOption(options.evidence),
228794
+ scope: options.scope,
228795
+ tags: parseCsvOption(options.tags),
228796
+ fileName: options.file
228797
+ });
228798
+ if (options.json) {
228799
+ writeStdoutLine(JSON.stringify({ created }, null, 2));
228800
+ return;
228801
+ }
228802
+ ui.success(`Created memory asset ${created.id}`);
228803
+ writeStdoutLine(created.path);
228804
+ }
228805
+ async function handleSelectedMemory(cwd, memoryId, options) {
228806
+ if (options.appendToSkill) {
228807
+ const appended = await appendRuntimeMemoryAssetToSkillReference({
228808
+ rootDir: cwd,
228809
+ memoryId,
228810
+ skillName: options.appendToSkill,
228811
+ fileName: options.file
228812
+ });
228813
+ if (options.json) {
228814
+ writeStdoutLine(JSON.stringify({ appended }, null, 2));
228815
+ return;
228816
+ }
228817
+ ui.success(`Appended memory asset ${memoryId} to skill ${options.appendToSkill}`);
228818
+ writeStdoutLine(appended.path);
228819
+ return;
228820
+ }
228821
+ if (options.delete) {
228822
+ const deleted = await deleteRuntimeMemoryAsset(cwd, memoryId);
228823
+ if (options.json) {
228824
+ writeStdoutLine(JSON.stringify({ deleted }, null, 2));
228825
+ return;
228826
+ }
228827
+ ui.success(`Deleted memory asset ${deleted.id}`);
228828
+ writeStdoutLine(deleted.path);
228829
+ return;
228830
+ }
228831
+ const memory = await readRuntimeMemoryAsset(cwd, memoryId);
228832
+ if (options.json) {
228833
+ writeStdoutLine(JSON.stringify(memory, null, 2));
228834
+ return;
228835
+ }
228836
+ writeStdoutLine(memory.content.trimEnd());
228837
+ }
228838
+ async function handleMemorySearch(cwd, query, json) {
228839
+ const results = await searchRuntimeMemoryAssets(cwd, query);
228840
+ if (json) {
228841
+ writeStdoutLine(JSON.stringify({ query, results }, null, 2));
228842
+ return;
228843
+ }
228844
+ if (results.length === 0) {
228845
+ ui.info("No matching runtime memory assets.");
228846
+ return;
228847
+ }
228848
+ for (const result of results) {
228849
+ writeStdoutLine(`${result.id} score=${result.score} ${result.path}`);
228850
+ for (const match of result.matches) {
228851
+ writeStdoutLine(` ${match}`);
228852
+ }
228853
+ }
228854
+ }
228855
+ function parseWritableMemoryKind(value) {
228856
+ if (value === "project" || value === "user" || value === "evidence") {
228857
+ return value;
228858
+ }
228859
+ throw new Error("--create must be one of: project, user, evidence.");
228860
+ }
228861
+ function parseCsvOption(value) {
228862
+ return (value ?? "").split(",").map((item) => item.trim()).filter(Boolean);
228863
+ }
228864
+ async function readMemoryListForCli(cwd) {
228865
+ return (await buildRuntimeStatus(cwd)).memory;
228866
+ }
228867
+ function formatMemoryListForCli(memory) {
228868
+ if (memory.assets.length === 0) {
228869
+ return "No runtime memory assets yet.";
228870
+ }
228871
+ return memory.assets.map((asset) => [
228872
+ asset.id,
228873
+ asset.kind,
228874
+ asset.updatedAt ?? "",
228875
+ `bytes=${asset.size}`,
228876
+ asset.evidenceRefs.length > 0 ? `evidence=${asset.evidenceRefs.join(",")}` : void 0,
228877
+ asset.path
228878
+ ].filter(Boolean).join(" ")).join("\n");
228879
+ }
228880
+ async function handleMemoryList(cwd, json) {
228881
+ const memory = await readMemoryListForCli(cwd);
228882
+ if (json) {
228883
+ writeStdoutLine(JSON.stringify(memory, null, 2));
228884
+ return;
228885
+ }
228886
+ if (memory.assets.length === 0) {
228887
+ ui.info("No runtime memory assets yet.");
228888
+ return;
228889
+ }
228890
+ writeStdoutLine(formatMemoryListForCli(memory));
228891
+ }
228892
+ var init_memory3 = __esm({
228893
+ "src/cli/commands/memory.ts"() {
228894
+ "use strict";
228895
+ init_status();
228896
+ init_memory2();
228897
+ init_console();
228898
+ init_stdio();
228899
+ }
228900
+ });
228901
+
227947
228902
  // src/project/resetSupport.ts
227948
228903
  async function waitForRemovedPaths(paths, attempts = 20, delayMs = 50) {
227949
228904
  for (let attempt = 0; attempt < attempts; attempt += 1) {
@@ -228132,7 +229087,7 @@ function normalizeLocalCommand(input) {
228132
229087
  return LOCAL_COMMAND_BY_ALIAS.get(input.trim().toLowerCase());
228133
229088
  }
228134
229089
  function getLocalCommandDefinition(id) {
228135
- const definition = LOCAL_COMMAND_DEFINITIONS.find((item) => item.id === id);
229090
+ const definition = ALL_LOCAL_COMMAND_DEFINITIONS.find((item) => item.id === id);
228136
229091
  if (!definition) {
228137
229092
  throw new Error(`Unknown local command: ${id}`);
228138
229093
  }
@@ -228142,50 +229097,164 @@ function formatLocalCommandHelpLine(id) {
228142
229097
  const definition = getLocalCommandDefinition(id);
228143
229098
  return `${definition.helpLabel.padEnd(12)} ${definition.helpText}`;
228144
229099
  }
228145
- var LOCAL_COMMAND_DEFINITIONS, LOCAL_COMMAND_BY_ALIAS;
229100
+ function formatLocalCommandHelp() {
229101
+ return [
229102
+ "Slash commands:",
229103
+ ...ALL_LOCAL_COMMAND_DEFINITIONS.map((definition) => formatLocalCommandHelpLine(definition.id)),
229104
+ "",
229105
+ "Any other input is sent directly to kitty."
229106
+ ].join("\n");
229107
+ }
229108
+ function listIntroLocalCommands() {
229109
+ return ALL_LOCAL_COMMAND_DEFINITIONS.filter((definition) => definition.showInIntro);
229110
+ }
229111
+ var LOCAL_COMMAND_DEFINITIONS, ALL_LOCAL_COMMAND_DEFINITIONS, LOCAL_COMMAND_BY_ALIAS;
228146
229112
  var init_localCommandDefinitions = __esm({
228147
229113
  "src/interaction/localCommandDefinitions.ts"() {
228148
229114
  "use strict";
228149
229115
  LOCAL_COMMAND_DEFINITIONS = [
228150
229116
  {
228151
229117
  id: "exit",
229118
+ category: "system",
228152
229119
  aliases: ["q", "quit", "exit", "/q", "/quit", "/exit"],
229120
+ slashName: "exit",
229121
+ description: "Exit the session",
228153
229122
  helpLabel: "quit",
228154
- helpText: "Exit the session"
229123
+ helpText: "Exit the session",
229124
+ showInIntro: true
228155
229125
  },
228156
229126
  {
228157
229127
  id: "reset",
229128
+ category: "project",
228158
229129
  aliases: ["reset", "/reset"],
229130
+ slashName: "reset",
229131
+ description: "Clear current project runtime state and exit",
228159
229132
  helpLabel: "/reset",
228160
- helpText: "Clear current project runtime state and exit"
229133
+ helpText: "Clear current project runtime state and exit",
229134
+ showInIntro: true
228161
229135
  },
228162
229136
  {
228163
229137
  id: "help",
229138
+ category: "system",
228164
229139
  aliases: ["/help"],
229140
+ slashName: "help",
229141
+ description: "Show slash commands",
228165
229142
  helpLabel: "/help",
228166
- helpText: "Show help"
229143
+ helpText: "Show slash commands",
229144
+ showInIntro: true
228167
229145
  },
228168
229146
  {
228169
229147
  id: "session",
229148
+ category: "session",
228170
229149
  aliases: ["/session"],
229150
+ slashName: "session",
229151
+ description: "Show current session ID",
228171
229152
  helpLabel: "/session",
228172
229153
  helpText: "Show current session ID"
228173
229154
  },
229155
+ {
229156
+ id: "sessions",
229157
+ category: "session",
229158
+ aliases: ["/sessions", "/resume", "/continue"],
229159
+ slashName: "sessions",
229160
+ description: "List recent sessions",
229161
+ helpLabel: "/sessions",
229162
+ helpText: "List recent sessions"
229163
+ },
228174
229164
  {
228175
229165
  id: "config",
229166
+ category: "project",
228176
229167
  aliases: ["/config"],
229168
+ slashName: "config",
229169
+ description: "Show current runtime config",
228177
229170
  helpLabel: "/config",
228178
229171
  helpText: "Show current runtime config"
228179
229172
  },
228180
229173
  {
228181
- id: "multiline",
228182
- aliases: ["/multi"],
228183
- helpLabel: "/multi",
228184
- helpText: "Enter multiline input; use ::end to submit and ::cancel to cancel"
229174
+ id: "status",
229175
+ category: "runtime",
229176
+ aliases: ["/status"],
229177
+ slashName: "status",
229178
+ description: "Show current project scene",
229179
+ helpLabel: "/status",
229180
+ helpText: "Show current project scene"
229181
+ },
229182
+ {
229183
+ id: "background",
229184
+ category: "runtime",
229185
+ aliases: ["/background", "/bg"],
229186
+ slashName: "background",
229187
+ description: "Show background task scene",
229188
+ helpLabel: "/background",
229189
+ helpText: "Show background task scene"
229190
+ },
229191
+ {
229192
+ id: "events",
229193
+ category: "runtime",
229194
+ aliases: ["/events"],
229195
+ slashName: "events",
229196
+ description: "Show recent session events",
229197
+ helpLabel: "/events",
229198
+ helpText: "Show recent session events"
229199
+ },
229200
+ {
229201
+ id: "memory",
229202
+ category: "runtime",
229203
+ aliases: ["/memory"],
229204
+ slashName: "memory",
229205
+ description: "List runtime memory assets",
229206
+ helpLabel: "/memory",
229207
+ helpText: "List runtime memory assets"
229208
+ },
229209
+ {
229210
+ id: "skills",
229211
+ category: "runtime",
229212
+ aliases: ["/skills"],
229213
+ slashName: "skills",
229214
+ description: "List runtime skills",
229215
+ helpLabel: "/skills",
229216
+ helpText: "List runtime skills"
229217
+ },
229218
+ {
229219
+ id: "doctor",
229220
+ category: "project",
229221
+ aliases: ["/doctor"],
229222
+ slashName: "doctor",
229223
+ description: "Run local setup preflight",
229224
+ helpLabel: "/doctor",
229225
+ helpText: "Run local setup preflight"
229226
+ },
229227
+ {
229228
+ id: "copy",
229229
+ category: "session",
229230
+ aliases: ["/copy"],
229231
+ slashName: "copy",
229232
+ description: "Print current session transcript",
229233
+ helpLabel: "/copy",
229234
+ helpText: "Print current session transcript"
229235
+ },
229236
+ {
229237
+ id: "export",
229238
+ category: "session",
229239
+ aliases: ["/export"],
229240
+ slashName: "export",
229241
+ description: "Print current session snapshot JSON",
229242
+ helpLabel: "/export",
229243
+ helpText: "Print current session snapshot JSON"
229244
+ },
229245
+ {
229246
+ id: "clear",
229247
+ category: "session",
229248
+ aliases: ["/clear"],
229249
+ slashName: "clear",
229250
+ description: "Clear the current prompt in UI shells",
229251
+ helpLabel: "/clear",
229252
+ helpText: "Clear the current prompt in UI shells"
228185
229253
  }
228186
229254
  ];
229255
+ ALL_LOCAL_COMMAND_DEFINITIONS = LOCAL_COMMAND_DEFINITIONS;
228187
229256
  LOCAL_COMMAND_BY_ALIAS = new Map(
228188
- LOCAL_COMMAND_DEFINITIONS.flatMap(
229257
+ ALL_LOCAL_COMMAND_DEFINITIONS.flatMap(
228189
229258
  (definition) => definition.aliases.map((alias) => [alias, definition.id])
228190
229259
  )
228191
229260
  );
@@ -228211,23 +229280,9 @@ async function handleLocalCommand(input, context, output) {
228211
229280
  return "quit";
228212
229281
  }
228213
229282
  if (command === "help") {
228214
- output.plain(
228215
- [
228216
- formatLocalCommandHelpLine("help"),
228217
- formatLocalCommandHelpLine("session"),
228218
- formatLocalCommandHelpLine("config"),
228219
- formatLocalCommandHelpLine("multiline"),
228220
- formatLocalCommandHelpLine("reset"),
228221
- formatLocalCommandHelpLine("exit"),
228222
- "",
228223
- "Any other input is sent directly to kitty."
228224
- ].join("\n")
228225
- );
229283
+ output.plain(formatLocalCommandHelp());
228226
229284
  return "handled";
228227
229285
  }
228228
- if (command === "multiline") {
228229
- return "multiline";
228230
- }
228231
229286
  if (command === "session") {
228232
229287
  output.info(`Current session: ${context.session.id}`);
228233
229288
  return "handled";
@@ -228236,12 +229291,108 @@ async function handleLocalCommand(input, context, output) {
228236
229291
  output.info(`model=${context.config.model} baseUrl=${context.config.baseUrl}`);
228237
229292
  return "handled";
228238
229293
  }
229294
+ if (command === "status") {
229295
+ output.plain(formatRuntimeStatusText(await buildRuntimeStatus(await resolveLocalStateRootDir(context))).trimEnd());
229296
+ return "handled";
229297
+ }
229298
+ if (command === "background") {
229299
+ output.plain(await formatBackgroundExecutionsForLocalCommand(context));
229300
+ return "handled";
229301
+ }
229302
+ if (command === "memory") {
229303
+ output.plain(formatMemoryListForCli(await readMemoryListForCli(await resolveLocalStateRootDir(context))));
229304
+ return "handled";
229305
+ }
229306
+ if (command === "skills") {
229307
+ output.plain(formatSkillsForLocalCommand(await buildRuntimeStatus(await resolveLocalStateRootDir(context))));
229308
+ return "handled";
229309
+ }
229310
+ if (command === "events") {
229311
+ const stateRootDir = await resolveLocalStateRootDir(context);
229312
+ output.plain(formatSessionEventsForCli(await readSessionEventsForCli({
229313
+ cwd: stateRootDir,
229314
+ paths: getAppPaths(stateRootDir),
229315
+ sessionId: context.session.id,
229316
+ limit: 20
229317
+ })));
229318
+ return "handled";
229319
+ }
229320
+ if (command === "doctor") {
229321
+ output.plain(formatConfigPreflightReport(await inspectConfigPreflight(context.cwd)).join("\n"));
229322
+ return "handled";
229323
+ }
229324
+ if (command === "sessions") {
229325
+ output.plain(await formatSessionsForLocalCommand(context));
229326
+ return "handled";
229327
+ }
229328
+ if (command === "copy") {
229329
+ output.plain(formatSessionTranscript(context.session));
229330
+ return "handled";
229331
+ }
229332
+ if (command === "export") {
229333
+ output.plain(JSON.stringify(context.session, null, 2));
229334
+ return "handled";
229335
+ }
229336
+ if (command === "clear") {
229337
+ output.info("Current prompt cleared.");
229338
+ return "handled";
229339
+ }
228239
229340
  return "continue";
228240
229341
  }
229342
+ async function formatBackgroundExecutionsForLocalCommand(context) {
229343
+ const stateRootDir = await resolveLocalStateRootDir(context);
229344
+ const store = new BackgroundExecutionStore(stateRootDir);
229345
+ const executions = store.listAll().map(summarizeExecution).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
229346
+ if (executions.length === 0) {
229347
+ return "No background executions recorded.";
229348
+ }
229349
+ return executions.map(formatBackgroundExecution).join("\n");
229350
+ }
229351
+ async function resolveLocalStateRootDir(context) {
229352
+ return context.stateRootDir ?? (await resolveProjectRoots(context.cwd)).stateRootDir;
229353
+ }
229354
+ function formatSkillsForLocalCommand(status) {
229355
+ if (status.skills.total === 0) {
229356
+ return "No runtime skills discovered.";
229357
+ }
229358
+ return [
229359
+ `skills: ${status.skills.ready}/${status.skills.total} ready`,
229360
+ ...status.skills.needsAttention.map((skill) => `${skill.name} status=${skill.status} issues=${skill.issues.join("; ") || "none"}`)
229361
+ ].join("\n");
229362
+ }
229363
+ async function formatSessionsForLocalCommand(context) {
229364
+ const sessions = await (context.sessionStore?.list(10) ?? []);
229365
+ if (sessions.length === 0) {
229366
+ return "No saved sessions yet.";
229367
+ }
229368
+ return sessions.map((session) => [
229369
+ session.id === context.session.id ? "*" : " ",
229370
+ session.id,
229371
+ session.updatedAt,
229372
+ session.title ?? "(untitled)",
229373
+ `messages=${session.messageCount}`
229374
+ ].join(" ")).join("\n");
229375
+ }
229376
+ function formatSessionTranscript(session) {
229377
+ if (session.messages.length === 0) {
229378
+ return "Current session has no messages yet.";
229379
+ }
229380
+ return session.messages.map((message) => `${message.role}: ${message.content}`).join("\n\n");
229381
+ }
228241
229382
  var init_localCommands = __esm({
228242
229383
  "src/interaction/localCommands.ts"() {
228243
229384
  "use strict";
229385
+ init_background();
229386
+ init_background3();
229387
+ init_preflight();
229388
+ init_memory3();
229389
+ init_runtimeStatusPresenter();
229390
+ init_events3();
229391
+ init_paths();
229392
+ init_repoRoots();
228244
229393
  init_reset();
229394
+ init_status();
229395
+ init_executionSummary();
228245
229396
  init_localCommandDefinitions();
228246
229397
  }
228247
229398
  });
@@ -228311,7 +229462,8 @@ var init_sessionDriver = __esm({
228311
229462
  {
228312
229463
  cwd: this.options.cwd,
228313
229464
  session: this.session,
228314
- config: this.options.config
229465
+ config: this.options.config,
229466
+ sessionStore: this.options.sessionStore
228315
229467
  },
228316
229468
  this.options.shell.output
228317
229469
  );
@@ -228323,8 +229475,6 @@ var init_sessionDriver = __esm({
228323
229475
  await this.runTurn(input);
228324
229476
  } else if (localCommandResult === "quit") {
228325
229477
  return this.handleQuitRequest();
228326
- } else if (localCommandResult === "multiline") {
228327
- await this.handleMultilineInput();
228328
229478
  }
228329
229479
  return localCommandResult;
228330
229480
  }
@@ -228366,25 +229516,6 @@ var init_sessionDriver = __esm({
228366
229516
  return "handled";
228367
229517
  }
228368
229518
  }
228369
- async handleMultilineInput() {
228370
- this.options.shell.output.info("Entered multiline mode. Use ::end to submit or ::cancel to cancel.\n");
228371
- const multiline = await this.options.shell.input.readMultiline("\u2026 ");
228372
- if (multiline.kind === "cancel") {
228373
- this.options.shell.output.warn("Cancelled multiline input.\n");
228374
- return;
228375
- }
228376
- if (multiline.kind === "closed") {
228377
- await this.terminateRunningProcessesForForcedExit("Input closed during multiline mode. Stopping running processes before exit.");
228378
- this.exitRequested = true;
228379
- return;
228380
- }
228381
- const value = multiline.value.trim();
228382
- if (!value) {
228383
- this.options.shell.output.warn("Multiline input was empty, nothing was sent.\n");
228384
- return;
228385
- }
228386
- await this.runTurn(value);
228387
- }
228388
229519
  handleInterrupt() {
228389
229520
  if (this.turnInFlight && this.turnAbortController && !this.turnAbortController.signal.aborted) {
228390
229521
  this.turnAbortController.abort();
@@ -228477,6 +229608,7 @@ var init_sessionDriver = __esm({
228477
229608
  runTurn: this.options.runTurn
228478
229609
  });
228479
229610
  this.session = outcome.session;
229611
+ this.options.onSessionUpdated?.(this.session);
228480
229612
  if (outcome.status === "aborted") {
228481
229613
  turnDisplay.flush();
228482
229614
  this.options.shell.output.warn(outcome.errorMessage ?? "Turn interrupted. You can keep chatting.");
@@ -229334,17 +230466,6 @@ function mirrorInput(input, writer) {
229334
230466
  const result = await input.readInput(promptLabel);
229335
230467
  if (result.kind === "submit") {
229336
230468
  writer.write(`${promptLabel ?? "> "}${result.value}
229337
- `);
229338
- }
229339
- return result;
229340
- },
229341
- async readMultiline(promptLabel) {
229342
- const result = await input.readMultiline(promptLabel);
229343
- if (result.kind === "submit") {
229344
- writer.write(`${promptLabel ?? "... "}${result.value}
229345
- `);
229346
- } else if (result.kind === "cancel") {
229347
- writer.write(`${promptLabel ?? "... "}::cancel
229348
230469
  `);
229349
230470
  }
229350
230471
  return result;
@@ -229392,7 +230513,7 @@ var init_terminalLog = __esm({
229392
230513
  }
229393
230514
  });
229394
230515
 
229395
- // src/shell/cli/intro.ts
230516
+ // src/shell/banner.ts
229396
230517
  function renderKittyBanner() {
229397
230518
  return import_figlet.default.textSync("kitty agent", {
229398
230519
  font: KITTY_WORDMARK_FONT,
@@ -229402,6 +230523,16 @@ function renderKittyBanner() {
229402
230523
  whitespaceBreak: false
229403
230524
  }).trimEnd();
229404
230525
  }
230526
+ var import_figlet, KITTY_WORDMARK_FONT;
230527
+ var init_banner = __esm({
230528
+ "src/shell/banner.ts"() {
230529
+ "use strict";
230530
+ import_figlet = __toESM(require("figlet"));
230531
+ KITTY_WORDMARK_FONT = "ANSI Shadow";
230532
+ }
230533
+ });
230534
+
230535
+ // src/shell/cli/intro.ts
229405
230536
  function writeCliInteractiveIntro(options) {
229406
230537
  options.output.plain(import_chalk4.default.bold(import_chalk4.default.greenBright(renderKittyBanner())));
229407
230538
  options.output.dim(`session: ${options.session.id}`);
@@ -229410,21 +230541,18 @@ function writeCliInteractiveIntro(options) {
229410
230541
  options.output.dim(`Tools: ${options.toolsLabel}`);
229411
230542
  }
229412
230543
  options.output.dim("Commands:");
229413
- options.output.dim(formatLocalCommandHelpLine("help"));
229414
- options.output.dim(formatLocalCommandHelpLine("multiline"));
229415
- options.output.dim(formatLocalCommandHelpLine("reset"));
229416
- options.output.dim(formatLocalCommandHelpLine("exit"));
229417
- options.output.dim("::end Submit multiline input");
229418
- options.output.dim("::cancel Cancel multiline input\n");
229419
- }
229420
- var import_chalk4, import_figlet, KITTY_WORDMARK_FONT;
230544
+ for (const command of listIntroLocalCommands()) {
230545
+ options.output.dim(formatLocalCommandHelpLine(command.id));
230546
+ }
230547
+ options.output.dim("");
230548
+ }
230549
+ var import_chalk4;
229421
230550
  var init_intro = __esm({
229422
230551
  "src/shell/cli/intro.ts"() {
229423
230552
  "use strict";
229424
230553
  import_chalk4 = __toESM(require("chalk"));
229425
- import_figlet = __toESM(require("figlet"));
229426
230554
  init_localCommandDefinitions();
229427
- KITTY_WORDMARK_FONT = "ANSI Shadow";
230555
+ init_banner();
229428
230556
  }
229429
230557
  });
229430
230558
 
@@ -229499,58 +230627,6 @@ async function readPersistentInput(promptLabel, onInterrupt) {
229499
230627
  rl.prompt();
229500
230628
  });
229501
230629
  }
229502
- async function readMultilineInput(onInterrupt, promptLabel = "\u2026 ") {
229503
- return new Promise((resolve) => {
229504
- const rl = import_node_readline.default.createInterface({
229505
- input: import_node_process4.default.stdin,
229506
- output: import_node_process4.default.stdout,
229507
- terminal: true
229508
- });
229509
- const lines = [];
229510
- let settled = false;
229511
- const cleanup = () => {
229512
- rl.removeAllListeners("line");
229513
- rl.removeAllListeners("close");
229514
- rl.removeAllListeners("SIGINT");
229515
- };
229516
- const finish = (value) => {
229517
- if (settled) {
229518
- return;
229519
- }
229520
- settled = true;
229521
- cleanup();
229522
- rl.close();
229523
- resolve(value);
229524
- };
229525
- rl.on("line", (line) => {
229526
- const trimmed = line.trim();
229527
- if (trimmed === "::end") {
229528
- finish({ kind: "submit", value: lines.join("\n") });
229529
- return;
229530
- }
229531
- if (trimmed === "::cancel") {
229532
- finish({ kind: "cancel" });
229533
- return;
229534
- }
229535
- lines.push(line);
229536
- rl.prompt();
229537
- });
229538
- rl.on("SIGINT", () => {
229539
- onInterrupt();
229540
- rl.prompt();
229541
- });
229542
- rl.on("close", () => {
229543
- if (settled) {
229544
- return;
229545
- }
229546
- settled = true;
229547
- cleanup();
229548
- resolve({ kind: "eof" });
229549
- });
229550
- rl.setPrompt(promptLabel);
229551
- rl.prompt();
229552
- });
229553
- }
229554
230630
  function createReadlineInputPort() {
229555
230631
  const listeners = /* @__PURE__ */ new Set();
229556
230632
  let releaseProcessInterrupt = null;
@@ -229583,13 +230659,6 @@ function createReadlineInputPort() {
229583
230659
  const value = await readPersistentInput(promptLabel, notifyInterrupt);
229584
230660
  return value === null ? { kind: "closed" } : { kind: "submit", value };
229585
230661
  },
229586
- async readMultiline(promptLabel = "\u2026 ") {
229587
- const result = await readMultilineInput(notifyInterrupt, promptLabel);
229588
- if (result.kind === "eof") {
229589
- return { kind: "closed" };
229590
- }
229591
- return result;
229592
- },
229593
230662
  bindInterrupt(handler) {
229594
230663
  listeners.add(handler);
229595
230664
  ensureProcessInterruptBinding();
@@ -230081,7 +231150,7 @@ function registerEventsCommand(program, options) {
230081
231150
  return;
230082
231151
  }
230083
231152
  for (const event of result.events) {
230084
- writeStdoutLine(formatSessionEvent(event));
231153
+ writeStdoutLine(formatSessionEventForCli(event));
230085
231154
  }
230086
231155
  });
230087
231156
  }
@@ -230099,7 +231168,16 @@ async function readSessionEventsForCli(input) {
230099
231168
  events: await new SessionEventStore(input.paths.eventsDir).list(session.id, input.limit)
230100
231169
  };
230101
231170
  }
230102
- function formatSessionEvent(event) {
231171
+ function formatSessionEventsForCli(result) {
231172
+ if (!result.sessionId) {
231173
+ return "No saved sessions yet.";
231174
+ }
231175
+ if (result.events.length === 0) {
231176
+ return `No events recorded for session ${result.sessionId}.`;
231177
+ }
231178
+ return result.events.map(formatSessionEventForCli).join("\n");
231179
+ }
231180
+ function formatSessionEventForCli(event) {
230103
231181
  const parts = [
230104
231182
  event.createdAt,
230105
231183
  event.type,
@@ -230124,382 +231202,6 @@ var init_events3 = __esm({
230124
231202
  }
230125
231203
  });
230126
231204
 
230127
- // src/cli/commands/memory.ts
230128
- function registerMemoryCommand(program, options) {
230129
- program.command("memory").description("List readable runtime memory assets.").argument("[memoryId]", "Optional runtime memory asset id to read.").option("--delete", "Delete the selected runtime memory asset.").option("--append-to-skill <skillName>", "Append the selected memory asset to a runtime skill references/ file.").option("--create <kind>", "Create a project, user, or evidence memory asset.").option("--title <title>", "Title for --create.").option("--content <content>", "Content for --create.").option("--evidence <refs>", "Comma-separated evidence refs for --create.").option("--scope <scope>", "Scope metadata for --create.").option("--tags <tags>", "Comma-separated tags for --create.").option("--file <fileName>", "Target file name for --append-to-skill.").option("-q, --query <query>", "Search runtime memory assets.").option("--json", "Print structured JSON.").action(async (memoryId, commandOptions) => {
230130
- const runtime = await options.resolveRuntime(options.getCliOverrides());
230131
- if (commandOptions.create) {
230132
- await handleMemoryCreate(runtime.cwd, commandOptions);
230133
- return;
230134
- }
230135
- if (memoryId) {
230136
- await handleSelectedMemory(runtime.cwd, memoryId, commandOptions);
230137
- return;
230138
- }
230139
- if (commandOptions.query) {
230140
- await handleMemorySearch(runtime.cwd, commandOptions.query, commandOptions.json === true);
230141
- return;
230142
- }
230143
- await handleMemoryList(runtime.cwd, commandOptions.json === true);
230144
- });
230145
- }
230146
- async function handleMemoryCreate(cwd, options) {
230147
- const kind = parseWritableMemoryKind(options.create);
230148
- if (!options.title?.trim()) {
230149
- throw new Error("--title is required when creating a memory asset.");
230150
- }
230151
- if (!options.content?.trim()) {
230152
- throw new Error("--content is required when creating a memory asset.");
230153
- }
230154
- const created = await createRuntimeMemoryAsset({
230155
- rootDir: cwd,
230156
- kind,
230157
- title: options.title,
230158
- content: options.content,
230159
- evidenceRefs: parseCsvOption(options.evidence),
230160
- scope: options.scope,
230161
- tags: parseCsvOption(options.tags),
230162
- fileName: options.file
230163
- });
230164
- if (options.json) {
230165
- writeStdoutLine(JSON.stringify({ created }, null, 2));
230166
- return;
230167
- }
230168
- ui.success(`Created memory asset ${created.id}`);
230169
- writeStdoutLine(created.path);
230170
- }
230171
- async function handleSelectedMemory(cwd, memoryId, options) {
230172
- if (options.appendToSkill) {
230173
- const appended = await appendRuntimeMemoryAssetToSkillReference({
230174
- rootDir: cwd,
230175
- memoryId,
230176
- skillName: options.appendToSkill,
230177
- fileName: options.file
230178
- });
230179
- if (options.json) {
230180
- writeStdoutLine(JSON.stringify({ appended }, null, 2));
230181
- return;
230182
- }
230183
- ui.success(`Appended memory asset ${memoryId} to skill ${options.appendToSkill}`);
230184
- writeStdoutLine(appended.path);
230185
- return;
230186
- }
230187
- if (options.delete) {
230188
- const deleted = await deleteRuntimeMemoryAsset(cwd, memoryId);
230189
- if (options.json) {
230190
- writeStdoutLine(JSON.stringify({ deleted }, null, 2));
230191
- return;
230192
- }
230193
- ui.success(`Deleted memory asset ${deleted.id}`);
230194
- writeStdoutLine(deleted.path);
230195
- return;
230196
- }
230197
- const memory = await readRuntimeMemoryAsset(cwd, memoryId);
230198
- if (options.json) {
230199
- writeStdoutLine(JSON.stringify(memory, null, 2));
230200
- return;
230201
- }
230202
- writeStdoutLine(memory.content.trimEnd());
230203
- }
230204
- async function handleMemorySearch(cwd, query, json) {
230205
- const results = await searchRuntimeMemoryAssets(cwd, query);
230206
- if (json) {
230207
- writeStdoutLine(JSON.stringify({ query, results }, null, 2));
230208
- return;
230209
- }
230210
- if (results.length === 0) {
230211
- ui.info("No matching runtime memory assets.");
230212
- return;
230213
- }
230214
- for (const result of results) {
230215
- writeStdoutLine(`${result.id} score=${result.score} ${result.path}`);
230216
- for (const match of result.matches) {
230217
- writeStdoutLine(` ${match}`);
230218
- }
230219
- }
230220
- }
230221
- function parseWritableMemoryKind(value) {
230222
- if (value === "project" || value === "user" || value === "evidence") {
230223
- return value;
230224
- }
230225
- throw new Error("--create must be one of: project, user, evidence.");
230226
- }
230227
- function parseCsvOption(value) {
230228
- return (value ?? "").split(",").map((item) => item.trim()).filter(Boolean);
230229
- }
230230
- async function handleMemoryList(cwd, json) {
230231
- const status = await buildRuntimeStatus(cwd);
230232
- if (json) {
230233
- writeStdoutLine(JSON.stringify(status.memory, null, 2));
230234
- return;
230235
- }
230236
- if (status.memory.assets.length === 0) {
230237
- ui.info("No runtime memory assets yet.");
230238
- return;
230239
- }
230240
- for (const memory of status.memory.assets) {
230241
- writeStdoutLine([
230242
- memory.id,
230243
- memory.kind,
230244
- memory.updatedAt ?? "",
230245
- `bytes=${memory.size}`,
230246
- memory.evidenceRefs.length > 0 ? `evidence=${memory.evidenceRefs.join(",")}` : void 0,
230247
- memory.path
230248
- ].filter(Boolean).join(" "));
230249
- }
230250
- }
230251
- var init_memory3 = __esm({
230252
- "src/cli/commands/memory.ts"() {
230253
- "use strict";
230254
- init_status();
230255
- init_memory2();
230256
- init_console();
230257
- init_stdio();
230258
- }
230259
- });
230260
-
230261
- // src/cli/commands/runtimeStatusPresenter.ts
230262
- function formatRuntimeStatusText(status) {
230263
- const lines = [];
230264
- lines.push(`Project: ${status.rootDir}`);
230265
- lines.push(`State: ${status.stateDir}`);
230266
- lines.push("");
230267
- lines.push("Current workspace:");
230268
- lines.push(`- Focus: ${readFocus(status)}`);
230269
- lines.push(`- Session: ${readSessionLine(status)}`);
230270
- lines.push(`- Next: ${readNextStep(status)}`);
230271
- lines.push(`- Blocked: ${readBlockedLine(status)}`);
230272
- lines.push(`- Context budget: ${readContextBudgetLine(status)}`);
230273
- lines.push(`- Workset: ${status.sessions.latest?.workset ? `${status.sessions.latest.workset.total} file(s)` : "none"}`);
230274
- lines.push(`- Memory: ${status.memory.assets.length > 0 ? `${status.memory.assets.length} asset(s)` : "none"}`);
230275
- lines.push(`- Skills: ${status.skills.ready}/${status.skills.total} ready`);
230276
- lines.push(`- Model cache: ${readModelCacheLine(status)}`);
230277
- lines.push(`- Project map: ${status.projectMap ? "ready" : "missing"}`);
230278
- lines.push(`- Executions: ${status.executions.active.length} active / ${status.executions.total} total`);
230279
- lines.push(`- Wake signals: ${status.wakeSignals.recent.length}`);
230280
- if (status.taskLifecycle) {
230281
- lines.push("");
230282
- lines.push("Task lifecycle:");
230283
- lines.push([
230284
- status.taskLifecycle.stage,
230285
- status.taskLifecycle.reason ? `reason=${status.taskLifecycle.reason}` : void 0,
230286
- status.taskLifecycle.updatedAt
230287
- ].filter(Boolean).join(" "));
230288
- }
230289
- if (status.sessions.latest) {
230290
- lines.push("");
230291
- lines.push("Latest session:");
230292
- lines.push([
230293
- status.sessions.latest.id,
230294
- status.sessions.latest.title ?? "(untitled)",
230295
- `messages=${status.sessions.latest.messageCount}`,
230296
- status.sessions.latest.hasMemory ? "memory=yes" : "memory=no"
230297
- ].join(" "));
230298
- }
230299
- if (status.projectMap) {
230300
- lines.push("");
230301
- lines.push("Project map:");
230302
- lines.push([
230303
- `dirs=${status.projectMap.topLevelDirectories.slice(0, 6).join(", ") || "none"}`,
230304
- `scripts=${status.projectMap.packageScripts.slice(0, 6).join(", ") || "none"}`,
230305
- `tests=${status.projectMap.testDirectories.slice(0, 4).join(", ") || "none"}`,
230306
- status.projectMap.git.available ? `git=${status.projectMap.git.hasChanges ? "changed" : "clean"}` : "git=unavailable"
230307
- ].join(" "));
230308
- }
230309
- if (status.sessions.latest?.workset?.files.length) {
230310
- lines.push("");
230311
- lines.push("Workset:");
230312
- for (const file of status.sessions.latest.workset.files) {
230313
- lines.push([
230314
- file.path,
230315
- `read=${file.readCount}`,
230316
- `changed=${file.changedCount}`,
230317
- `last=${file.lastTool}`,
230318
- file.lastChangeId ? `change=${file.lastChangeId}` : void 0,
230319
- file.reason ? `reason=${file.reason}` : void 0
230320
- ].filter(Boolean).join(" "));
230321
- }
230322
- }
230323
- if (status.sessions.latest?.contextBudget?.promptHotspots.length) {
230324
- lines.push("");
230325
- lines.push("Context budget hotspots:");
230326
- for (const hotspot of status.sessions.latest.contextBudget.promptHotspots.slice(0, 3)) {
230327
- lines.push([
230328
- hotspot.layer,
230329
- hotspot.title,
230330
- `chars=${hotspot.chars}`,
230331
- `lines=${hotspot.lines}`
230332
- ].join(" "));
230333
- }
230334
- }
230335
- if (status.sessions.latest?.contextBudget?.sources.length) {
230336
- lines.push("");
230337
- lines.push("Context budget sources:");
230338
- for (const source of status.sessions.latest.contextBudget.sources) {
230339
- lines.push([
230340
- source.name,
230341
- `chars=${source.chars}`,
230342
- source.messages === void 0 ? void 0 : `messages=${source.messages}`
230343
- ].filter(Boolean).join(" "));
230344
- }
230345
- }
230346
- if (status.sessions.latest?.contextBudget?.cacheLayout) {
230347
- const layout = status.sessions.latest.contextBudget.cacheLayout;
230348
- lines.push("");
230349
- lines.push("Cache layout:");
230350
- lines.push([
230351
- `stable=${layout.stablePrefixFingerprint}`,
230352
- `stableChars=${layout.stablePrefixChars}`,
230353
- `tail=${layout.volatileTailFingerprint}`,
230354
- `tailChars=${layout.volatileTailChars}`
230355
- ].join(" "));
230356
- }
230357
- if (status.modelRequests.recent.length > 0) {
230358
- lines.push("");
230359
- lines.push("Recent model requests:");
230360
- for (const request of status.modelRequests.recent.slice(0, 5)) {
230361
- lines.push([
230362
- request.model ?? "unknown-model",
230363
- request.provider ? `provider=${request.provider}` : void 0,
230364
- request.durationMs === void 0 ? void 0 : `duration=${request.durationMs}ms`,
230365
- request.usage ? formatUsage(request.usage) : "usage=unavailable"
230366
- ].filter(Boolean).join(" "));
230367
- }
230368
- }
230369
- if (status.executions.active.length > 0) {
230370
- lines.push("");
230371
- lines.push("Active executions:");
230372
- for (const execution of status.executions.active) {
230373
- lines.push([
230374
- execution.id,
230375
- execution.kind,
230376
- execution.status,
230377
- execution.actorName ? `actor=${execution.actorName}` : void 0,
230378
- execution.waitPolicy ? `wait=${execution.waitPolicy}` : void 0,
230379
- execution.health ? `health=${execution.health.state}` : void 0,
230380
- execution.deadlineAt ? `deadline=${execution.deadlineAt}` : void 0,
230381
- execution.assignment?.objective ? `objective=${truncateCliValue(execution.assignment.objective, 60)}` : void 0
230382
- ].filter(Boolean).join(" "));
230383
- }
230384
- }
230385
- if (status.executions.recent.length > 0) {
230386
- lines.push("");
230387
- lines.push("Recent executions:");
230388
- for (const execution of status.executions.recent.slice(0, 5)) {
230389
- lines.push([
230390
- execution.id,
230391
- execution.kind,
230392
- execution.status,
230393
- execution.actorName ? `actor=${execution.actorName}` : void 0,
230394
- execution.summary ? truncateCliValue(execution.summary, 80) : void 0,
230395
- execution.assignment?.expectedOutput ? `expected=${truncateCliValue(execution.assignment.expectedOutput, 60)}` : void 0
230396
- ].filter(Boolean).join(" "));
230397
- }
230398
- }
230399
- if (status.memory.assets.length > 0) {
230400
- lines.push("");
230401
- lines.push("Memory:");
230402
- for (const memory of status.memory.assets.slice(0, 5)) {
230403
- lines.push([
230404
- memory.id,
230405
- memory.kind,
230406
- `bytes=${memory.size}`,
230407
- memory.evidenceRefs.length > 0 ? `evidence=${memory.evidenceRefs.join(",")}` : void 0,
230408
- memory.path
230409
- ].filter(Boolean).join(" "));
230410
- }
230411
- }
230412
- if (status.skills.needsAttention.length > 0) {
230413
- lines.push("");
230414
- lines.push("Skills needing attention:");
230415
- for (const skill of status.skills.needsAttention) {
230416
- lines.push([
230417
- skill.name,
230418
- skill.path,
230419
- `resources=${skill.resources}`,
230420
- `dependencies=${skill.dependencies}`,
230421
- skill.issues.length > 0 ? `issues=${skill.issues.join("; ")}` : void 0
230422
- ].filter(Boolean).join(" "));
230423
- }
230424
- }
230425
- return `${lines.join("\n")}
230426
- `;
230427
- }
230428
- function readFocus(status) {
230429
- const focus = status.sessions.latest?.focus;
230430
- return focus ? truncateCliValue(focus, 100) : "none";
230431
- }
230432
- function readSessionLine(status) {
230433
- if (!status.sessions.latest) {
230434
- return "none";
230435
- }
230436
- return `${status.sessions.latest.id} (${status.sessions.latest.messageCount} message(s))`;
230437
- }
230438
- function readNextStep(status) {
230439
- if (status.executions.active.length > 0) {
230440
- return "Wait for active execution results or inspect them with status/tools.";
230441
- }
230442
- if (!status.sessions.latest) {
230443
- return "Start a session with `kitty` or run a prompt.";
230444
- }
230445
- return "Continue from the current session focus.";
230446
- }
230447
- function readBlockedLine(status) {
230448
- const unhealthy = status.executions.active.find(
230449
- (execution) => execution.health?.state === "stale" || execution.health?.state === "deadline_passed"
230450
- );
230451
- if (unhealthy) {
230452
- return `${unhealthy.kind} ${unhealthy.id}: ${unhealthy.health?.message}`;
230453
- }
230454
- return "no";
230455
- }
230456
- function readContextBudgetLine(status) {
230457
- const budget = status.sessions.latest?.contextBudget;
230458
- if (!budget) {
230459
- return "none";
230460
- }
230461
- const percent = Math.round(budget.usageRatio * 100);
230462
- return [
230463
- `${budget.estimatedChars}/${budget.limitChars} chars`,
230464
- `${percent}%`,
230465
- budget.compressed ? `compressed=${budget.compressionMode}` : "compressed=no",
230466
- `reason=${budget.compressionReason}`
230467
- ].join(" ");
230468
- }
230469
- function readModelCacheLine(status) {
230470
- const latest = status.modelRequests.recent[0];
230471
- if (!latest) {
230472
- return "none";
230473
- }
230474
- if (!latest.usage) {
230475
- return "usage unavailable";
230476
- }
230477
- const cacheTokens = latest.usage.cacheHitTokens ?? latest.usage.cacheReadTokens;
230478
- const rate = latest.usage.cacheHitRate === void 0 ? void 0 : `${Math.round(latest.usage.cacheHitRate * 100)}%`;
230479
- return [
230480
- cacheTokens === void 0 ? "cached=unknown" : `cached=${cacheTokens}`,
230481
- rate ? `hit=${rate}` : void 0
230482
- ].filter(Boolean).join(" ");
230483
- }
230484
- function formatUsage(usage) {
230485
- return [
230486
- usage.inputTokens === void 0 ? void 0 : `input=${usage.inputTokens}`,
230487
- usage.outputTokens === void 0 ? void 0 : `output=${usage.outputTokens}`,
230488
- usage.reasoningTokens === void 0 ? void 0 : `reasoning=${usage.reasoningTokens}`,
230489
- usage.cacheHitTokens === void 0 ? void 0 : `cacheHit=${usage.cacheHitTokens}`,
230490
- usage.cacheReadTokens === void 0 ? void 0 : `cacheRead=${usage.cacheReadTokens}`,
230491
- usage.cacheCreationTokens === void 0 ? void 0 : `cacheWrite=${usage.cacheCreationTokens}`,
230492
- usage.cacheMissTokens === void 0 ? void 0 : `cacheMiss=${usage.cacheMissTokens}`,
230493
- usage.cacheHitRate === void 0 ? void 0 : `hit=${Math.round(usage.cacheHitRate * 100)}%`
230494
- ].filter(Boolean).join(" ");
230495
- }
230496
- var init_runtimeStatusPresenter = __esm({
230497
- "src/cli/commands/runtimeStatusPresenter.ts"() {
230498
- "use strict";
230499
- init_cliValues();
230500
- }
230501
- });
230502
-
230503
231205
  // src/cli/commands/runtimeStatus.ts
230504
231206
  function registerRuntimeStatusCommand(program, options) {
230505
231207
  program.command("status").description("Show the current project runtime status.").option("--json", "Print structured JSON.").action(async (commandOptions) => {
@@ -230814,74 +231516,6 @@ var init_agent = __esm({
230814
231516
  }
230815
231517
  });
230816
231518
 
230817
- // src/cli/commands/background.ts
230818
- function registerBackgroundCommand(program, options) {
230819
- const command = program.command("background").description("Inspect, wait for, or stop background executions.");
230820
- command.argument("[action]", "Optional action: list, wait, stop").argument("[id]", "Background execution id for wait or stop").option("--json", "Print structured JSON.").option("--timeout-ms <ms>", "Wait timeout in milliseconds.", (value) => Number.parseInt(value, 10), 6e4).action(async (action, id, commandOptions) => {
230821
- const runtime = await options.resolveRuntime(options.getCliOverrides());
230822
- const normalizedAction = action ?? "list";
230823
- if (normalizedAction === "list") {
230824
- const executions = new BackgroundExecutionStore(runtime.stateRootDir).listAll().map(summarizeExecution).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
230825
- printBackgroundExecutions(executions, Boolean(commandOptions.json));
230826
- return;
230827
- }
230828
- if (!id) {
230829
- throw new Error(`background ${normalizedAction} requires an execution id.`);
230830
- }
230831
- if (normalizedAction === "wait") {
230832
- const execution = summarizeExecution(await waitForBackgroundExecution({
230833
- rootDir: runtime.stateRootDir,
230834
- id,
230835
- timeoutMs: commandOptions.timeoutMs
230836
- }));
230837
- printBackgroundExecutions([execution], Boolean(commandOptions.json));
230838
- return;
230839
- }
230840
- if (normalizedAction === "stop") {
230841
- const execution = terminateBackgroundExecution(runtime.stateRootDir, id);
230842
- await waitForRegisteredBackgroundProcess(id);
230843
- printBackgroundExecutions([summarizeExecution(execution)], Boolean(commandOptions.json));
230844
- return;
230845
- }
230846
- throw new Error(`Unknown background action: ${normalizedAction}. Use list, wait, or stop.`);
230847
- });
230848
- }
230849
- function printBackgroundExecutions(executions, json) {
230850
- if (json) {
230851
- writeStdoutLine(JSON.stringify({ executions }, null, 2));
230852
- return;
230853
- }
230854
- if (executions.length === 0) {
230855
- ui.info("No background executions recorded.");
230856
- return;
230857
- }
230858
- for (const execution of executions) {
230859
- writeStdoutLine(formatBackgroundExecution(execution));
230860
- }
230861
- }
230862
- function formatBackgroundExecution(execution) {
230863
- return [
230864
- execution.id,
230865
- execution.status,
230866
- execution.pid === void 0 ? void 0 : `pid=${execution.pid}`,
230867
- execution.health ? `health=${execution.health.state}` : void 0,
230868
- execution.deadlineAt ? `deadline=${execution.deadlineAt}` : void 0,
230869
- execution.command ? `cmd=${truncateCliValue(execution.command, 70)}` : void 0,
230870
- execution.summary ? `summary=${truncateCliValue(execution.summary, 90)}` : void 0,
230871
- execution.outputPreview ? `output=${truncateCliValue(execution.outputPreview, 120)}` : void 0
230872
- ].filter(Boolean).join(" ");
230873
- }
230874
- var init_background3 = __esm({
230875
- "src/cli/commands/background.ts"() {
230876
- "use strict";
230877
- init_background();
230878
- init_executionSummary();
230879
- init_console();
230880
- init_stdio();
230881
- init_cliValues();
230882
- }
230883
- });
230884
-
230885
231519
  // src/cli/commands/session.ts
230886
231520
  function registerSessionCommands(program, options) {
230887
231521
  program.argument("[prompt...]", "Start a new session with a one-shot prompt. Without a prompt, opens interactive chat.").action(async (promptParts) => {
@@ -231839,7 +232473,6 @@ var init_helpText_zh = __esm({
231839
232473
  "/stop Stop the current Telegram task without shutting down the bot service",
231840
232474
  "/session Show the session ID bound to this Telegram chat",
231841
232475
  "/config Show current runtime configuration",
231842
- "/multi Telegram does not support interactive multiline mode; send the full message directly",
231843
232476
  "",
231844
232477
  "File usage:",
231845
232478
  "- Send files directly to the private chat. Kitty downloads them and attaches them to the current session.",
@@ -232286,11 +232919,6 @@ async function runTelegramTurn(options) {
232286
232919
  options.markQueuedTurnStarted(options.message.peerKey);
232287
232920
  return;
232288
232921
  }
232289
- if (localCommandResult === "multiline") {
232290
- options.markQueuedTurnStarted(options.message.peerKey);
232291
- output.warn("Telegram does not support interactive multiline mode. Send the full message directly.");
232292
- return;
232293
- }
232294
232922
  }
232295
232923
  const display = new TelegramTurnDisplay({
232296
232924
  chatId: options.message.chatId,
@@ -233417,6 +234045,39 @@ var init_web2 = __esm({
233417
234045
  }
233418
234046
  });
233419
234047
 
234048
+ // src/cli/commands/tui.ts
234049
+ function registerTuiCommand(program, dependencies) {
234050
+ program.command("tui").description("Start the Ink terminal UI.").action(async () => {
234051
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
234052
+ throw new Error("kitty tui requires an interactive TTY.");
234053
+ }
234054
+ const runtime = await dependencies.resolveRuntime(dependencies.getCliOverrides());
234055
+ const sessionStore = await createSessionStore(runtime.paths.sessionsDir);
234056
+ const { startTuiChat } = await loadTuiEntrypoint();
234057
+ await startTuiChat({
234058
+ cwd: runtime.cwd,
234059
+ cwdOverridden: Boolean(runtime.overrides.cwd),
234060
+ config: runtime.config,
234061
+ sessionStore
234062
+ });
234063
+ });
234064
+ }
234065
+ async function loadTuiEntrypoint() {
234066
+ try {
234067
+ return await import(new URL("./tui.mjs", (0, import_node_url.pathToFileURL)(__filename)).href);
234068
+ } catch (error) {
234069
+ throw new Error(`TUI entrypoint is unavailable. Run npm.cmd run build and try again. Cause: ${error instanceof Error ? error.message : String(error)}`);
234070
+ }
234071
+ }
234072
+ var import_node_url;
234073
+ var init_tui = __esm({
234074
+ "src/cli/commands/tui.ts"() {
234075
+ "use strict";
234076
+ import_node_url = require("url");
234077
+ init_sessionHelpers();
234078
+ }
234079
+ });
234080
+
233420
234081
  // src/cli/program.ts
233421
234082
  var program_exports = {};
233422
234083
  __export(program_exports, {
@@ -233491,6 +234152,10 @@ function buildCliProgram(dependencies = {}) {
233491
234152
  getCliOverrides,
233492
234153
  resolveRuntime
233493
234154
  });
234155
+ registerTuiCommand(program, {
234156
+ getCliOverrides,
234157
+ resolveRuntime
234158
+ });
233494
234159
  return program;
233495
234160
  }
233496
234161
  var import_commander;
@@ -233513,6 +234178,7 @@ var init_program = __esm({
233513
234178
  init_stdio();
233514
234179
  init_cli();
233515
234180
  init_web2();
234181
+ init_tui();
233516
234182
  }
233517
234183
  });
233518
234184