@orquentra/codebuddy 2.9.0 → 2.14.0

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.
Files changed (70) hide show
  1. package/CLAUDE_CODEX.md +33 -0
  2. package/README.md +27 -3
  3. package/dist/{adapters-S4HHTWJW.js → adapters-OJRSVFV3.js} +2 -2
  4. package/dist/{chunk-GKNYMNFM.js → chunk-4DKYKDHI.js} +6 -1
  5. package/dist/chunk-4DKYKDHI.js.map +1 -0
  6. package/dist/{chunk-SRA4MDVG.js → chunk-7QMHIJBA.js} +30 -3
  7. package/dist/chunk-7QMHIJBA.js.map +1 -0
  8. package/dist/{chunk-6NZEZJYD.js → chunk-ESC4Y3L7.js} +3 -5
  9. package/dist/{chunk-6NZEZJYD.js.map → chunk-ESC4Y3L7.js.map} +1 -1
  10. package/dist/chunk-GQKWUHMA.js +20 -0
  11. package/dist/chunk-GQKWUHMA.js.map +1 -0
  12. package/dist/{chunk-J4T6HHI4.js → chunk-L6BRYZ6G.js} +537 -316
  13. package/dist/chunk-L6BRYZ6G.js.map +1 -0
  14. package/dist/{chunk-UK6XTS2Y.js → chunk-LRQNPYCK.js} +13 -19
  15. package/dist/chunk-LRQNPYCK.js.map +1 -0
  16. package/dist/{chunk-GXOML4UZ.js → chunk-LW75SKZR.js} +2 -2
  17. package/dist/{chunk-ENKDFLOO.js → chunk-UOTKHSQ5.js} +25 -8
  18. package/dist/chunk-UOTKHSQ5.js.map +1 -0
  19. package/dist/{chunk-W7GAXL6C.js → chunk-V7PT6ER5.js} +6 -5
  20. package/dist/chunk-V7PT6ER5.js.map +1 -0
  21. package/dist/{chunk-F4BJ3DXW.js → chunk-XSNW6Y3X.js} +1253 -315
  22. package/dist/chunk-XSNW6Y3X.js.map +1 -0
  23. package/dist/{chunk-2WH2Q4TA.js → chunk-YJPCS2GP.js} +256 -68
  24. package/dist/chunk-YJPCS2GP.js.map +1 -0
  25. package/dist/{chunk-TLWGZVPC.js → chunk-YNSTIMCF.js} +746 -66
  26. package/dist/chunk-YNSTIMCF.js.map +1 -0
  27. package/dist/{chunk-QN76UJP7.js → chunk-YWDH6DQ6.js} +70 -4
  28. package/dist/chunk-YWDH6DQ6.js.map +1 -0
  29. package/dist/cli/index.js +23 -9
  30. package/dist/cli/index.js.map +1 -1
  31. package/dist/{codebuddy-DO7ZhoWn.d.ts → codebuddy-CHqMptZB.d.ts} +35 -0
  32. package/dist/index.d.ts +6 -4
  33. package/dist/index.js +8 -8
  34. package/dist/install-EU35ERK5.js +30 -0
  35. package/dist/langgraph/index.d.ts +1 -1
  36. package/dist/lean-43ADYTBW.js +28 -0
  37. package/dist/{main-YJKSYFPT.js → main-SFC5LV4E.js} +704 -242
  38. package/dist/main-SFC5LV4E.js.map +1 -0
  39. package/dist/{operations-OG4ISWIM.js → operations-6Z6ZDVBP.js} +3 -3
  40. package/dist/{pretool-command-PRFOIHMW.js → pretool-command-B5GY5T6E.js} +2 -2
  41. package/dist/prompt-gate-TDTFFQW2.js +8 -0
  42. package/dist/{runtime-GCSEIAVD.js → runtime-W5AW32SD.js} +14 -6
  43. package/dist/{summaries-ML53R7EL.js → summaries-32PHOB6L.js} +3 -4
  44. package/dist/{zvec-XDZNZ2OL.js → zvec-24HLAFGN.js} +3 -4
  45. package/dist/zvec-24HLAFGN.js.map +1 -0
  46. package/package.json +11 -2
  47. package/dist/chunk-2WH2Q4TA.js.map +0 -1
  48. package/dist/chunk-ENKDFLOO.js.map +0 -1
  49. package/dist/chunk-F4BJ3DXW.js.map +0 -1
  50. package/dist/chunk-GKNYMNFM.js.map +0 -1
  51. package/dist/chunk-ITQO632W.js +0 -673
  52. package/dist/chunk-ITQO632W.js.map +0 -1
  53. package/dist/chunk-J4T6HHI4.js.map +0 -1
  54. package/dist/chunk-QN76UJP7.js.map +0 -1
  55. package/dist/chunk-SRA4MDVG.js.map +0 -1
  56. package/dist/chunk-TLWGZVPC.js.map +0 -1
  57. package/dist/chunk-UK6XTS2Y.js.map +0 -1
  58. package/dist/chunk-W7GAXL6C.js.map +0 -1
  59. package/dist/install-BXM56QCD.js +0 -19
  60. package/dist/lean-command-7DFW32MA.js +0 -19
  61. package/dist/main-YJKSYFPT.js.map +0 -1
  62. /package/dist/{adapters-S4HHTWJW.js.map → adapters-OJRSVFV3.js.map} +0 -0
  63. /package/dist/{chunk-GXOML4UZ.js.map → chunk-LW75SKZR.js.map} +0 -0
  64. /package/dist/{install-BXM56QCD.js.map → install-EU35ERK5.js.map} +0 -0
  65. /package/dist/{lean-command-7DFW32MA.js.map → lean-43ADYTBW.js.map} +0 -0
  66. /package/dist/{operations-OG4ISWIM.js.map → operations-6Z6ZDVBP.js.map} +0 -0
  67. /package/dist/{pretool-command-PRFOIHMW.js.map → pretool-command-B5GY5T6E.js.map} +0 -0
  68. /package/dist/{runtime-GCSEIAVD.js.map → prompt-gate-TDTFFQW2.js.map} +0 -0
  69. /package/dist/{summaries-ML53R7EL.js.map → runtime-W5AW32SD.js.map} +0 -0
  70. /package/dist/{zvec-XDZNZ2OL.js.map → summaries-32PHOB6L.js.map} +0 -0
@@ -1,10 +1,16 @@
1
1
  import {
2
- renderLeanBlock,
3
- runLeanHookCommand
4
- } from "./chunk-UK6XTS2Y.js";
2
+ isDockerDaemonRunning,
3
+ tryStartDockerDesktop,
4
+ waitForDockerDaemon
5
+ } from "./chunk-5UH3BEQR.js";
6
+ import {
7
+ postgresDown,
8
+ postgresStatus,
9
+ postgresUp
10
+ } from "./chunk-CSRD5OQA.js";
5
11
  import {
6
12
  runPreToolHookCommand
7
- } from "./chunk-ENKDFLOO.js";
13
+ } from "./chunk-UOTKHSQ5.js";
8
14
  import {
9
15
  failSoft,
10
16
  readHookInput,
@@ -13,6 +19,7 @@ import {
13
19
  import {
14
20
  WORKFLOW_CLIENTS,
15
21
  clientDisplayName,
22
+ clientHookCapabilities,
16
23
  clientHooksInstalled,
17
24
  defaultRulesMode,
18
25
  detectClient,
@@ -29,10 +36,12 @@ import {
29
36
  removeCodexEntry,
30
37
  renderManagedBlock,
31
38
  renderWorkflowTemplate,
39
+ runBridgeHook,
40
+ runCodexHook,
32
41
  setHooks,
33
42
  uninstallClient,
34
43
  writeProjectMcpConfig
35
- } from "./chunk-J4T6HHI4.js";
44
+ } from "./chunk-L6BRYZ6G.js";
36
45
  import {
37
46
  AUTOMATION_MODES,
38
47
  CLIENT_ADAPTERS,
@@ -42,19 +51,14 @@ import {
42
51
  parseClient,
43
52
  parseMode,
44
53
  supportedModes
45
- } from "./chunk-W7GAXL6C.js";
46
- import {
47
- isDockerDaemonRunning,
48
- tryStartDockerDesktop,
49
- waitForDockerDaemon
50
- } from "./chunk-5UH3BEQR.js";
54
+ } from "./chunk-V7PT6ER5.js";
51
55
  import {
52
- postgresDown,
53
- postgresStatus,
54
- postgresUp
55
- } from "./chunk-CSRD5OQA.js";
56
+ renderLeanBlock,
57
+ runLeanHook
58
+ } from "./chunk-LRQNPYCK.js";
56
59
  import {
57
60
  TELEMETRY_NOTICE,
61
+ findReuse,
58
62
  flushTelemetry,
59
63
  generateSuggestions,
60
64
  migrateMemoryToFiles,
@@ -68,13 +72,14 @@ import {
68
72
  traceCliCommand,
69
73
  watchRepository,
70
74
  writeTelemetryState
71
- } from "./chunk-2WH2Q4TA.js";
75
+ } from "./chunk-YJPCS2GP.js";
72
76
  import {
73
77
  DEFAULT_ZVEC_MODEL,
74
78
  buildZvecIndex,
75
79
  zvecStatus
76
- } from "./chunk-GXOML4UZ.js";
80
+ } from "./chunk-LW75SKZR.js";
77
81
  import {
82
+ HookStagingStore,
78
83
  PlanFileStore,
79
84
  PlanLifecycle,
80
85
  ReviewStore,
@@ -83,28 +88,40 @@ import {
83
88
  assessRisk,
84
89
  buildBeforeEditContext,
85
90
  buildBootstrapContext,
91
+ findCodexRollouts,
92
+ forgetJournal,
93
+ importCodexRollout,
94
+ journalLag,
95
+ latestUnfinished,
96
+ listJournals,
86
97
  openHookSemantics,
98
+ readCheckpoint,
99
+ renderCheckpointNow,
87
100
  renderContextBlock,
88
101
  resolveRiskPaths,
89
102
  runCaptureHook,
103
+ runCheckpointHook,
90
104
  runContextHook,
91
105
  runResetHook,
106
+ runResumeHook,
92
107
  runStageHook,
93
- runSyncHook
94
- } from "./chunk-F4BJ3DXW.js";
95
- import {
96
- SessionCapsuleLedger,
97
- detectTestCommand,
98
- estimateTokens
99
- } from "./chunk-GKNYMNFM.js";
108
+ runSyncHook,
109
+ syncReuseVectors
110
+ } from "./chunk-XSNW6Y3X.js";
100
111
  import {
101
112
  parseTraceLines,
102
113
  readTraces,
103
114
  tracePath
104
115
  } from "./chunk-YN7TEIUH.js";
116
+ import {
117
+ SessionCapsuleLedger,
118
+ detectTestCommand,
119
+ estimateTokens
120
+ } from "./chunk-4DKYKDHI.js";
105
121
  import {
106
122
  hookRoot
107
123
  } from "./chunk-TGFTKWOR.js";
124
+ import "./chunk-GQKWUHMA.js";
108
125
  import "./chunk-DFV2SK6C.js";
109
126
  import {
110
127
  bootstrapDatabase,
@@ -117,11 +134,11 @@ import {
117
134
  pingDatabase,
118
135
  runDoctor,
119
136
  runMigrations
120
- } from "./chunk-QN76UJP7.js";
137
+ } from "./chunk-YWDH6DQ6.js";
121
138
  import {
122
139
  MemoryFileStore,
123
140
  VERSION
124
- } from "./chunk-SRA4MDVG.js";
141
+ } from "./chunk-7QMHIJBA.js";
125
142
  import {
126
143
  computeFactHash,
127
144
  generateEntityId
@@ -134,25 +151,24 @@ import {
134
151
  import {
135
152
  loadLlmSummaries,
136
153
  summarizeRepository
137
- } from "./chunk-6NZEZJYD.js";
154
+ } from "./chunk-ESC4Y3L7.js";
138
155
  import {
139
156
  chatJson,
140
157
  coolingDown,
141
158
  usageToday
142
159
  } from "./chunk-ESZXCZIN.js";
143
- import {
144
- generateGraphifyGraph,
145
- loadGraphifyMap,
146
- setupGraphify,
147
- syncFileVectors
148
- } from "./chunk-TLWGZVPC.js";
149
160
  import {
150
161
  buildArchitectureMap,
162
+ generateGraphifyGraph,
151
163
  indexRepository,
152
164
  loadFreshIndex,
165
+ loadGraphifyMap,
166
+ loadProjectArchitectureMap,
153
167
  neighbours,
154
- queryMap
155
- } from "./chunk-ITQO632W.js";
168
+ queryMap,
169
+ setupGraphify,
170
+ syncFileVectors
171
+ } from "./chunk-YNSTIMCF.js";
156
172
  import {
157
173
  extractSymbolTable,
158
174
  supportsSymbols
@@ -187,12 +203,14 @@ import {
187
203
  setZvecEnabled,
188
204
  writeConfigFile
189
205
  } from "./chunk-KNR2P2UY.js";
190
- import "./chunk-CG5B3LYG.js";
206
+ import {
207
+ writeAtomic
208
+ } from "./chunk-CG5B3LYG.js";
191
209
  import "./chunk-PZ5AY32C.js";
192
210
 
193
211
  // src/cli/main.ts
194
212
  import { Command } from "commander";
195
- import pc23 from "picocolors";
213
+ import pc25 from "picocolors";
196
214
 
197
215
  // src/integrations/github.ts
198
216
  import { constants } from "fs";
@@ -770,6 +788,7 @@ var CONFORMANCE = {
770
788
  const servers = json(result.stdout);
771
789
  if (!Array.isArray(servers)) return [fail("mcp", "codex mcp list --json returned no JSON")];
772
790
  return [
791
+ await clientHooksInstalled(root, "codex") ? pass("hooks", ".codex/hooks.json is installed; review trust in Codex /hooks") : skip("hooks", "not installed in this repo"),
773
792
  servers.some((server) => server.name === name2) ? pass("mcp", `codex mcp list has ${name2}`) : skip("mcp", `no ${name2} entry (clients install codex --global adds it)`)
774
793
  ];
775
794
  },
@@ -863,7 +882,7 @@ function registerClientsCommands(program, runSafely2) {
863
882
  console.log(pc2.dim("Per-turn token cost per client: codebuddy clients cost"));
864
883
  });
865
884
  });
866
- clients.command("install").argument("<client>", `One of: ${CLIENT_IDS.join(", ")}.`).requiredOption("--mode <mode>", `One of: ${AUTOMATION_MODES.join(", ")}.`).option("--namespace <name>", "Namespace for the MCP entry (defaults to config/folder).").option("--global", "Also add the MCP entry to the global config (Codex, Hermes).").description("Apply one automation mode: rules block, hooks, and MCP config.").action(
885
+ clients.command("install").argument("<client>", `One of: ${CLIENT_IDS.join(", ")}.`).requiredOption("--mode <mode>", `One of: ${AUTOMATION_MODES.join(", ")}.`).option("--namespace <name>", "Namespace for the MCP entry (defaults to config/folder).").option("--global", "Also add the MCP entry to the global config (Codex, Hermes).").option("--capture", "Capture turns into memory even in lean mode (Claude Code).").option("--no-capture", "Inject context without capturing turns (Claude Code).").description("Apply one automation mode: rules block, hooks, and MCP config.").action(
867
886
  async (client, opts) => {
868
887
  await runSafely2(async () => {
869
888
  const mode = parseMode(opts.mode);
@@ -871,7 +890,8 @@ function registerClientsCommands(program, runSafely2) {
871
890
  client: parseClient(client),
872
891
  mode,
873
892
  ...opts.namespace ? { namespace: opts.namespace } : {},
874
- ...opts.global ? { global: true } : {}
893
+ ...opts.global ? { global: true } : {},
894
+ ...opts.capture !== void 0 ? { capture: opts.capture } : {}
875
895
  });
876
896
  printReport2(report, `${MODE_LABELS[mode]} mode`);
877
897
  });
@@ -983,6 +1003,30 @@ function registerContextCommands(program, runSafely2) {
983
1003
  printBootstrap(result);
984
1004
  });
985
1005
  });
1006
+ context.command("reuse").argument("<task...>", "What you are about to implement.").option("--json", "Print machine-readable JSON.").description(
1007
+ "Existing helpers to reuse for a task: signature, a call site and the covering test."
1008
+ ).action(async (taskParts, opts) => {
1009
+ await runSafely2(async () => {
1010
+ const root = process.cwd();
1011
+ const { manifest } = await loadFreshIndex(root);
1012
+ const picks = await findReuse(
1013
+ root,
1014
+ taskParts.join(" "),
1015
+ manifest,
1016
+ await loadProjectArchitectureMap(root, manifest)
1017
+ );
1018
+ if (opts.json) {
1019
+ console.log(JSON.stringify(picks, null, 2));
1020
+ return;
1021
+ }
1022
+ if (picks.length === 0) console.log(pc3.dim("no existing helper clearly matches"));
1023
+ for (const pick of picks) {
1024
+ console.log(`${pick.path}:${pick.startLine}-${pick.endLine} ${pick.signature}`);
1025
+ if (pick.usage) console.log(pc3.dim(` used at ${pick.usage}`));
1026
+ if (pick.test) console.log(pc3.dim(` test ${pick.test}`));
1027
+ }
1028
+ });
1029
+ });
986
1030
  context.command("preview").argument("[task...]", "The task you are about to work on.").option("--paths <paths>", "Comma-separated repo-relative paths to target.").option("--plan <id>", "Resolve target files from a CodeBuddy plan.").option("--git", "Resolve target files from current git changes.").option("--json", "Print machine-readable JSON.").description("Show the context CodeBuddy would assemble before editing the given files.").action(
987
1031
  async (taskParts, opts) => {
988
1032
  await runSafely2(async () => {
@@ -1720,15 +1764,22 @@ function registerGitHubCommands(program, runSafely2) {
1720
1764
  import { execFile as execFile2, spawn as spawn3 } from "child_process";
1721
1765
  import { relative as relative2 } from "path";
1722
1766
  import pc7 from "picocolors";
1767
+
1768
+ // src/hooks/lean-command.ts
1769
+ async function runLeanHookCommand() {
1770
+ await failSoft(async () => {
1771
+ await runLeanHook(await readHookInput(), writeStdout);
1772
+ });
1773
+ }
1774
+
1775
+ // src/cli/commands/hooks.ts
1723
1776
  function registerHooksCommands(program, runSafely2) {
1724
1777
  const hooks = program.command("hooks").description("Enforce automatic recall/capture via client lifecycle hooks.");
1725
1778
  hooks.command("install").option(
1726
1779
  "--client <client>",
1727
- "Client with a CodeBuddy hook adapter (claude, opencode, hermes).",
1780
+ "Client with a CodeBuddy hook adapter (claude, codex, opencode, hermes).",
1728
1781
  "claude"
1729
- ).description(
1730
- "Add CodeBuddy's recall/capture hooks (.claude/settings.json, .opencode/plugins/codebuddy.js or .hermes/plugins/codebuddy/)."
1731
- ).action(async (opts) => {
1782
+ ).description("Add CodeBuddy's recall/capture hooks for the selected client.").action(async (opts) => {
1732
1783
  await runSafely2(async () => {
1733
1784
  const client = assertHookClient(opts.client);
1734
1785
  const step = await setHooks(process.cwd(), true, client);
@@ -1750,7 +1801,7 @@ function registerHooksCommands(program, runSafely2) {
1750
1801
  });
1751
1802
  hooks.command("uninstall").option(
1752
1803
  "--client <client>",
1753
- "Client with a CodeBuddy hook adapter (claude, opencode, hermes).",
1804
+ "Client with a CodeBuddy hook adapter (claude, codex, opencode, hermes).",
1754
1805
  "claude"
1755
1806
  ).description("Remove CodeBuddy's hooks for a client (user hooks untouched).").action(async (opts) => {
1756
1807
  await runSafely2(async () => {
@@ -1764,15 +1815,44 @@ function registerHooksCommands(program, runSafely2) {
1764
1815
  });
1765
1816
  hooks.command("status").option(
1766
1817
  "--client <client>",
1767
- "Client with a CodeBuddy hook adapter (claude, opencode, hermes).",
1818
+ "Client with a CodeBuddy hook adapter (claude, codex, opencode, hermes).",
1768
1819
  "claude"
1769
- ).description("Report whether CodeBuddy hooks are installed.").action(async (opts) => {
1820
+ ).description("Report which CodeBuddy hooks are installed and what they do.").action(async (opts) => {
1770
1821
  await runSafely2(async () => {
1771
- const installed = await clientHooksInstalled(process.cwd(), parseClient(opts.client));
1822
+ const caps = await clientHookCapabilities(process.cwd(), parseClient(opts.client));
1823
+ if (!caps?.injection) {
1824
+ console.log(pc7.dim("CodeBuddy hooks: not installed"));
1825
+ return;
1826
+ }
1827
+ console.log(pc7.green("CodeBuddy hooks: installed"));
1828
+ console.log(` injection: ${caps.injection}`);
1829
+ console.log(
1830
+ ` capture: ${caps.capture ? "on (edits staged, turn end captured)" : "off (turns are not captured into memory)"}`
1831
+ );
1772
1832
  console.log(
1773
- installed ? pc7.green("CodeBuddy hooks: installed") : pc7.dim("CodeBuddy hooks: not installed")
1833
+ ` recovery: ${caps.recovery ? "on (pre-compaction checkpoint, unfinished-task resume)" : "off"}`
1834
+ );
1835
+ console.log(` compaction reset: ${caps.reset ? "on" : "off"}`);
1836
+ console.log(` read guard: ${caps.guard ? "on" : "off"}`);
1837
+ });
1838
+ });
1839
+ hooks.command("codex").option("--injection <mode>", "full or lean", "full").option("--capture", "Capture visible lifecycle events", false).description("[hook] Native Codex event adapter (JSON output, no permission changes).").action(async (opts) => {
1840
+ let result = {};
1841
+ await failSoft(async () => {
1842
+ if (opts.injection !== "full" && opts.injection !== "lean")
1843
+ throw new Error("injection must be full or lean");
1844
+ result = await runCodexHook(
1845
+ await readHookInput(),
1846
+ opts.injection,
1847
+ opts.capture
1774
1848
  );
1775
1849
  });
1850
+ await writeStdout(JSON.stringify(result));
1851
+ });
1852
+ hooks.command("bridge").description("[hook] Capture normalized host events and restore checkpoint context.").action(async () => {
1853
+ await failSoft(async () => {
1854
+ await runBridgeHook(await readHookInput(), writeStdout);
1855
+ });
1776
1856
  });
1777
1857
  hooks.command("context").description("[hook] Emit compact project context for injection at prompt submit.").action(async () => {
1778
1858
  await failSoft(async () => {
@@ -1824,6 +1904,16 @@ function registerHooksCommands(program, runSafely2) {
1824
1904
  await runResetHook(await readHookInput(), writeStdout);
1825
1905
  });
1826
1906
  });
1907
+ hooks.command("checkpoint").description("[hook] Journal the transcript before compaction (PreCompact).").action(async () => {
1908
+ await failSoft(async () => {
1909
+ await runCheckpointHook(await readHookInput());
1910
+ });
1911
+ });
1912
+ hooks.command("resume").description("[hook] Print another session's unfinished task checkpoint, if any.").action(async () => {
1913
+ await failSoft(async () => {
1914
+ await runResumeHook(await readHookInput(), writeStdout);
1915
+ });
1916
+ });
1827
1917
  }
1828
1918
  function assertHookClient(value) {
1829
1919
  const client = parseClient(value);
@@ -2010,9 +2100,21 @@ function registerIndexCommands(program, runSafely2) {
2010
2100
  await runSafely2(async () => {
2011
2101
  const result = await indexRepository({ ...opts.full ? { full: true } : {} });
2012
2102
  const local = opts.vectors && (await loadRuntimeConfig(process.cwd(), { requireDatabase: false }).catch(() => null))?.provider.type === "local";
2103
+ const vectorStarted = Date.now();
2013
2104
  const vectors = local ? await syncFileVectors(process.cwd(), VECTOR_BUDGET_MS) : null;
2105
+ const reuseVectors = local ? await syncReuseVectors(process.cwd(), VECTOR_BUDGET_MS - (Date.now() - vectorStarted)) : null;
2014
2106
  if (opts.json) {
2015
- console.log(JSON.stringify({ ...result, ...vectors ? { vectors } : {} }, null, 2));
2107
+ console.log(
2108
+ JSON.stringify(
2109
+ {
2110
+ ...result,
2111
+ ...vectors ? { vectors } : {},
2112
+ ...reuseVectors ? { reuseVectors } : {}
2113
+ },
2114
+ null,
2115
+ 2
2116
+ )
2117
+ );
2016
2118
  return;
2017
2119
  }
2018
2120
  printResult(result);
@@ -2202,8 +2304,59 @@ async function runInitWizard() {
2202
2304
  }
2203
2305
  }
2204
2306
 
2205
- // src/cli/commands/learn.ts
2307
+ // src/cli/commands/journal.ts
2206
2308
  import pc10 from "picocolors";
2309
+ function registerJournalCommands(program, runSafely2) {
2310
+ const journal = program.command("journal").description("Inspect or delete session journals and active-task checkpoints.");
2311
+ journal.command("list", { isDefault: true }).description("List sessions with a checkpoint, newest first.").action(async () => {
2312
+ await runSafely2(async () => {
2313
+ const sessions = await listJournals(process.cwd());
2314
+ if (sessions.length === 0) console.log(pc10.dim("no session journals yet"));
2315
+ for (const { session, updatedAt } of sessions) console.log(`${updatedAt} ${session}`);
2316
+ });
2317
+ });
2318
+ journal.command("show").argument("<session>").description("Print a session's active-task checkpoint as JSON.").action(async (session) => {
2319
+ await runSafely2(async () => {
2320
+ const checkpoint = await readCheckpoint(process.cwd(), session);
2321
+ console.log(checkpoint ? JSON.stringify(checkpoint, null, 2) : pc10.dim("no checkpoint"));
2322
+ });
2323
+ });
2324
+ journal.command("resume").description("Print the newest unfinished task checkpoint (any session or agent).").action(async () => {
2325
+ await runSafely2(async () => {
2326
+ const checkpoint = await latestUnfinished(process.cwd(), void 0);
2327
+ console.log(
2328
+ checkpoint ? await renderCheckpointNow(
2329
+ process.cwd(),
2330
+ checkpoint,
2331
+ `unfinished, from session ${checkpoint.session}`
2332
+ ) : pc10.dim("no unfinished task in the last 3 days")
2333
+ );
2334
+ });
2335
+ });
2336
+ journal.command("import-codex").option("--all", "Import every Codex rollout for this repository, not only the newest.").description("Journal Codex rollouts (~/.codex/sessions) whose working directory is this repo.").action(async (opts) => {
2337
+ await runSafely2(async () => {
2338
+ const rollouts = await findCodexRollouts(process.cwd());
2339
+ if (rollouts.length === 0) {
2340
+ console.log(pc10.dim("no Codex rollout for this repository"));
2341
+ return;
2342
+ }
2343
+ for (const rollout of opts.all ? rollouts : rollouts.slice(0, 1)) {
2344
+ const result = await importCodexRollout(process.cwd(), rollout);
2345
+ console.log(`codex-${rollout.id}: ${result ? `${result.added} new events` : "busy"}`);
2346
+ }
2347
+ });
2348
+ });
2349
+ journal.command("forget").argument("[session]").option("--all", "Delete every session's journal.").description("Delete a session's journal, cursor and checkpoint.").action(async (session, opts) => {
2350
+ await runSafely2(async () => {
2351
+ if (!session && !opts.all) throw new Error("Name a session or pass --all.");
2352
+ await forgetJournal(process.cwd(), opts.all ? void 0 : session);
2353
+ console.log(pc10.yellow(opts.all ? "deleted all session journals" : `deleted ${session}`));
2354
+ });
2355
+ });
2356
+ }
2357
+
2358
+ // src/cli/commands/learn.ts
2359
+ import pc11 from "picocolors";
2207
2360
 
2208
2361
  // src/learning/outcomes.ts
2209
2362
  async function recordChangeOutcome(input) {
@@ -2264,15 +2417,15 @@ function registerLearnCommand(program, runSafely2) {
2264
2417
  ...opts.commit ? { commit: opts.commit } : {},
2265
2418
  ...opts.plan ? { planId: opts.plan } : {}
2266
2419
  });
2267
- console.log(`${pc10.green("recorded")} ${result.id} (${result.category})`);
2268
- console.log(pc10.dim(result.path));
2420
+ console.log(`${pc11.green("recorded")} ${result.id} (${result.category})`);
2421
+ console.log(pc11.dim(result.path));
2269
2422
  });
2270
2423
  }
2271
2424
  );
2272
2425
  }
2273
2426
 
2274
2427
  // src/cli/commands/map.ts
2275
- import pc11 from "picocolors";
2428
+ import pc12 from "picocolors";
2276
2429
  function registerMapCommands(program, runSafely2) {
2277
2430
  const map = program.command("map").description("Query the lightweight architecture map.");
2278
2431
  map.command("build").description("Scan the current repository and summarize the module graph.").action(async () => {
@@ -2292,7 +2445,7 @@ function registerMapCommands(program, runSafely2) {
2292
2445
  return;
2293
2446
  }
2294
2447
  if (edges.length === 0) {
2295
- console.log(pc11.dim("no dependencies found"));
2448
+ console.log(pc12.dim("no dependencies found"));
2296
2449
  return;
2297
2450
  }
2298
2451
  for (const edge of edges) console.log(`${edge.from} -> ${edge.to}`);
@@ -2307,7 +2460,7 @@ function registerMapCommands(program, runSafely2) {
2307
2460
  return;
2308
2461
  }
2309
2462
  if (result.edges.length === 0) {
2310
- console.log(pc11.dim("no dependents found"));
2463
+ console.log(pc12.dim("no dependents found"));
2311
2464
  return;
2312
2465
  }
2313
2466
  for (const edge of result.edges) console.log(`${edge.from} -> ${edge.to}`);
@@ -2318,7 +2471,7 @@ function registerMapCommands(program, runSafely2) {
2318
2471
  // src/cli/commands/memory-review.ts
2319
2472
  import { spawn as spawn4 } from "child_process";
2320
2473
  import { relative as relative3 } from "path";
2321
- import pc12 from "picocolors";
2474
+ import pc13 from "picocolors";
2322
2475
  function registerMemoryCommands(program, runSafely2) {
2323
2476
  const memory = program.command("memory").description("Inspect and curate CodeBuddy memory.");
2324
2477
  const review = memory.command("review").description("Review memory candidates captured automatically after turns.");
@@ -2330,11 +2483,11 @@ function registerMemoryCommands(program, runSafely2) {
2330
2483
  return;
2331
2484
  }
2332
2485
  if (items.length === 0) {
2333
- console.log(pc12.dim("no pending review items"));
2486
+ console.log(pc13.dim("no pending review items"));
2334
2487
  return;
2335
2488
  }
2336
2489
  for (const item of items) console.log(renderRow(item));
2337
- console.log(pc12.dim(`
2490
+ console.log(pc13.dim(`
2338
2491
  ${items.length} pending. approve <id> | reject <id> | show <id>`));
2339
2492
  });
2340
2493
  });
@@ -2348,7 +2501,7 @@ ${items.length} pending. approve <id> | reject <id> | show <id>`));
2348
2501
  const factId = await approveReviewItem(id, {
2349
2502
  ...opts.incident !== void 0 ? { incidentId: opts.incident } : {}
2350
2503
  });
2351
- console.log(`${pc12.green("approved")} ${id} \u2192 ${factId}`);
2504
+ console.log(`${pc13.green("approved")} ${id} \u2192 ${factId}`);
2352
2505
  });
2353
2506
  });
2354
2507
  review.command("reject").argument("<id>").description("Discard a review candidate without saving it.").action(async (id) => {
@@ -2356,7 +2509,7 @@ ${items.length} pending. approve <id> | reject <id> | show <id>`));
2356
2509
  const store = new ReviewStore();
2357
2510
  await store.read(id);
2358
2511
  await store.delete(id);
2359
- console.log(`${pc12.yellow("rejected")} ${id}`);
2512
+ console.log(`${pc13.yellow("rejected")} ${id}`);
2360
2513
  });
2361
2514
  });
2362
2515
  review.command("edit").argument("<id>").description("Open a review candidate in $EDITOR and re-validate on save.").action(async (id) => {
@@ -2365,44 +2518,44 @@ ${items.length} pending. approve <id> | reject <id> | show <id>`));
2365
2518
  const item = await store.read(id);
2366
2519
  await openEditor(item.path);
2367
2520
  await store.read(id);
2368
- console.log(pc12.dim("saved and validated"));
2521
+ console.log(pc13.dim("saved and validated"));
2369
2522
  });
2370
2523
  });
2371
2524
  }
2372
2525
  function renderRow(item) {
2373
- const flag = item.sensitive ? pc12.red(" \u26A0 sensitive") : "";
2374
- return `${classColor(item.class)} ${pc12.dim(item.id)} ${truncate(item.content, 70)}${flag}`;
2526
+ const flag = item.sensitive ? pc13.red(" \u26A0 sensitive") : "";
2527
+ return `${classColor(item.class)} ${pc13.dim(item.id)} ${truncate(item.content, 70)}${flag}`;
2375
2528
  }
2376
2529
  function renderItem(item) {
2377
2530
  const lines = [
2378
- `${classColor(item.class)} ${pc12.dim(item.id)}`,
2379
- `${pc12.bold("reason:")} ${item.reason}`,
2380
- `${pc12.bold("confidence:")} ${item.confidence.toFixed(2)}`,
2381
- `${pc12.bold("triple:")} ${item.subject} \xB7 ${item.predicate} \xB7 ${item.object}`
2531
+ `${classColor(item.class)} ${pc13.dim(item.id)}`,
2532
+ `${pc13.bold("reason:")} ${item.reason}`,
2533
+ `${pc13.bold("confidence:")} ${item.confidence.toFixed(2)}`,
2534
+ `${pc13.bold("triple:")} ${item.subject} \xB7 ${item.predicate} \xB7 ${item.object}`
2382
2535
  ];
2383
- if (item.paths.length > 0) lines.push(`${pc12.bold("paths:")} ${item.paths.join(", ")}`);
2384
- if (item.severity) lines.push(`${pc12.bold("severity:")} ${item.severity}`);
2536
+ if (item.paths.length > 0) lines.push(`${pc13.bold("paths:")} ${item.paths.join(", ")}`);
2537
+ if (item.severity) lines.push(`${pc13.bold("severity:")} ${item.severity}`);
2385
2538
  if (item.resolvesIncidents) {
2386
2539
  lines.push(
2387
- `${pc12.bold("resolves:")} ${item.resolvesIncidents.join(", ") || "(no candidate; approve with --incident <id>)"}`
2540
+ `${pc13.bold("resolves:")} ${item.resolvesIncidents.join(", ") || "(no candidate; approve with --incident <id>)"}`
2388
2541
  );
2389
2542
  }
2390
- if (item.sensitive) lines.push(pc12.red(`\u26A0 sensitive: ${item.sensitiveReasons.join(", ")}`));
2391
- lines.push("", item.content, "", pc12.dim(relative3(process.cwd(), item.path)));
2543
+ if (item.sensitive) lines.push(pc13.red(`\u26A0 sensitive: ${item.sensitiveReasons.join(", ")}`));
2544
+ lines.push("", item.content, "", pc13.dim(relative3(process.cwd(), item.path)));
2392
2545
  return lines.join("\n");
2393
2546
  }
2394
2547
  function classColor(memoryClass) {
2395
2548
  switch (memoryClass) {
2396
2549
  case "incident":
2397
- return pc12.red(memoryClass);
2550
+ return pc13.red(memoryClass);
2398
2551
  case "decision":
2399
- return pc12.cyan(memoryClass);
2552
+ return pc13.cyan(memoryClass);
2400
2553
  case "fact":
2401
- return pc12.green(memoryClass);
2554
+ return pc13.green(memoryClass);
2402
2555
  case "note":
2403
- return pc12.yellow(memoryClass);
2556
+ return pc13.yellow(memoryClass);
2404
2557
  default:
2405
- return pc12.dim(memoryClass);
2558
+ return pc13.dim(memoryClass);
2406
2559
  }
2407
2560
  }
2408
2561
  function truncate(value, max) {
@@ -2422,7 +2575,7 @@ function openEditor(path) {
2422
2575
  import { existsSync } from "fs";
2423
2576
  import { join as join5 } from "path";
2424
2577
  import { spinner as spinner2 } from "@clack/prompts";
2425
- import pc13 from "picocolors";
2578
+ import pc14 from "picocolors";
2426
2579
 
2427
2580
  // evals/fact-relevance.json
2428
2581
  var fact_relevance_default = {
@@ -2686,9 +2839,9 @@ async function calibrateEmbedder(embed) {
2686
2839
  var EMBEDDERS = ["local", "huggingface", "openrouter"];
2687
2840
  var RERANKERS = ["off", "local", "huggingface", "openrouter"];
2688
2841
  var KEY_ENV2 = { huggingface: "HF_TOKEN", openrouter: "OPENROUTER_API_KEY" };
2689
- var ok = (text3) => `${pc13.green("\u2713")} ${text3}`;
2690
- var warn = (text3) => `${pc13.yellow("!")} ${text3}`;
2691
- var row = (label, value) => ` ${pc13.dim(label.padEnd(11))}${value}`;
2842
+ var ok = (text3) => `${pc14.green("\u2713")} ${text3}`;
2843
+ var warn = (text3) => `${pc14.yellow("!")} ${text3}`;
2844
+ var row = (label, value) => ` ${pc14.dim(label.padEnd(11))}${value}`;
2692
2845
  async function withSpinner(label, work, done) {
2693
2846
  const progress = spinner2();
2694
2847
  progress.start(label);
@@ -2697,7 +2850,7 @@ async function withSpinner(label, work, done) {
2697
2850
  progress.stop(done(value));
2698
2851
  return value;
2699
2852
  } catch (error) {
2700
- progress.error(pc13.red(`\u2717 ${label}`));
2853
+ progress.error(pc14.red(`\u2717 ${label}`));
2701
2854
  throw error;
2702
2855
  }
2703
2856
  }
@@ -2736,8 +2889,8 @@ async function printStatus() {
2736
2889
  const model = provider.type === "local" ? LOCAL_EMBEDDING_MODEL.id : provider.model ?? DEFAULT_CLOUD_EMBEDDING_MODELS[provider.type];
2737
2890
  const floor = provider.relevanceFloor ?? getModelDefinition(model)?.relevanceFloor;
2738
2891
  const next = [];
2739
- console.log(pc13.bold("Embeddings"));
2740
- console.log(row("provider", `${provider.type} ${pc13.dim(model)}`));
2892
+ console.log(pc14.bold("Embeddings"));
2893
+ console.log(row("provider", `${provider.type} ${pc14.dim(model)}`));
2741
2894
  if (provider.type === "local" && !localRuntimeInstalled()) {
2742
2895
  console.log(
2743
2896
  row("runtime", warn("@huggingface/transformers is not installed: dense recall is off"))
@@ -2748,7 +2901,7 @@ async function printStatus() {
2748
2901
  console.log(
2749
2902
  row(
2750
2903
  "model",
2751
- ready ? ok(`downloaded ${pc13.dim(modelCacheDir())}`) : warn("not downloaded yet (~90 MB)")
2904
+ ready ? ok(`downloaded ${pc14.dim(modelCacheDir())}`) : warn("not downloaded yet (~90 MB)")
2752
2905
  )
2753
2906
  );
2754
2907
  if (!ready) next.push("codebuddy models pull");
@@ -2765,21 +2918,21 @@ async function printStatus() {
2765
2918
  console.log(
2766
2919
  row(
2767
2920
  "floor",
2768
- floor !== void 0 ? `${floor} ${pc13.dim(provider.relevanceFloor !== void 0 ? "(calibrated for this project)" : "(built-in)")}` : warn("not calibrated: dense recall is off for this model")
2921
+ floor !== void 0 ? `${floor} ${pc14.dim(provider.relevanceFloor !== void 0 ? "(calibrated for this project)" : "(built-in)")}` : warn("not calibrated: dense recall is off for this model")
2769
2922
  )
2770
2923
  );
2771
2924
  if (floor === void 0) next.push("codebuddy models calibrate");
2772
2925
  const rerank = file.rerank?.provider ?? "off";
2773
- console.log(pc13.bold("Reranker"));
2926
+ console.log(pc14.bold("Reranker"));
2774
2927
  if (rerank === "off") {
2775
2928
  console.log(
2776
- row("provider", `off ${pc13.dim("(default: no reranker beat dense order in evals)")}`)
2929
+ row("provider", `off ${pc14.dim("(default: no reranker beat dense order in evals)")}`)
2777
2930
  );
2778
2931
  } else {
2779
2932
  console.log(
2780
2933
  row(
2781
2934
  "provider",
2782
- `${rerank} ${pc13.dim(file.rerank?.model ?? DEFAULT_RERANK_MODELS[rerank].id)}`
2935
+ `${rerank} ${pc14.dim(file.rerank?.model ?? DEFAULT_RERANK_MODELS[rerank].id)}`
2783
2936
  )
2784
2937
  );
2785
2938
  if (rerank !== "local") {
@@ -2792,7 +2945,7 @@ async function printStatus() {
2792
2945
  );
2793
2946
  }
2794
2947
  }
2795
- console.log(pc13.bold("Memory index"));
2948
+ console.log(pc14.bold("Memory index"));
2796
2949
  if (process.env.CODEBUDDY_SEMANTIC === "0") {
2797
2950
  console.log(row("recall", warn("CODEBUDDY_SEMANTIC=0: lexical only")));
2798
2951
  }
@@ -2813,7 +2966,7 @@ async function printStatus() {
2813
2966
  console.log(
2814
2967
  row(
2815
2968
  "vectors",
2816
- `${counts.ready} ready` + (counts.pending ? `, ${counts.pending} pending` : "") + (counts.failed ? pc13.red(`, ${counts.failed} failed`) : "") + (counts.other ? pc13.yellow(`, ${counts.other} from another model`) : "")
2969
+ `${counts.ready} ready` + (counts.pending ? `, ${counts.pending} pending` : "") + (counts.failed ? pc14.red(`, ${counts.failed} failed`) : "") + (counts.other ? pc14.yellow(`, ${counts.other} from another model`) : "")
2817
2970
  )
2818
2971
  );
2819
2972
  if (counts.pending || counts.other)
@@ -2822,9 +2975,9 @@ async function printStatus() {
2822
2975
  }
2823
2976
  console.log(
2824
2977
  next.length > 0 ? `
2825
- ${pc13.bold("Next")}
2826
- ${next.map((step) => ` ${pc13.cyan(step)}`).join("\n")}` : `
2827
- ${ok("Semantic recall is ready.")} ${pc13.dim('Try: codebuddy context preview "<task>"')}`
2978
+ ${pc14.bold("Next")}
2979
+ ${next.map((step) => ` ${pc14.cyan(step)}`).join("\n")}` : `
2980
+ ${ok("Semantic recall is ready.")} ${pc14.dim('Try: codebuddy context preview "<task>"')}`
2828
2981
  );
2829
2982
  }
2830
2983
  function registerModelsCommands(program, runSafely2) {
@@ -2861,27 +3014,27 @@ Examples:
2861
3014
  ...opts.floor !== void 0 ? { relevanceFloor: opts.floor } : {}
2862
3015
  });
2863
3016
  const model = type === "local" ? LOCAL_EMBEDDING_MODEL.id : opts.model ?? DEFAULT_CLOUD_EMBEDDING_MODELS[type];
2864
- console.log(ok(`Embeddings: ${type} ${pc13.dim(model)}`));
3017
+ console.log(ok(`Embeddings: ${type} ${pc14.dim(model)}`));
2865
3018
  const missingKey = type !== "local" && !keyPresent(type, current?.apiKey);
2866
3019
  if (missingKey) {
2867
3020
  console.log(
2868
3021
  warn(
2869
- `Set ${KEY_ENV2[type]} in your shell (keys are never stored in config), then run ${pc13.cyan("codebuddy hooks sync")}.`
3022
+ `Set ${KEY_ENV2[type]} in your shell (keys are never stored in config), then run ${pc14.cyan("codebuddy hooks sync")}.`
2870
3023
  )
2871
3024
  );
2872
- console.log(pc13.dim(" Existing vectors keep working until the new model can embed."));
3025
+ console.log(pc14.dim(" Existing vectors keep working until the new model can embed."));
2873
3026
  }
2874
3027
  if (opts.floor === void 0 && getModelDefinition(model)?.relevanceFloor === void 0) {
2875
3028
  console.log(
2876
3029
  warn(
2877
- `No floor for ${model} yet: run ${pc13.cyan("codebuddy models calibrate")} to turn on dense recall.`
3030
+ `No floor for ${model} yet: run ${pc14.cyan("codebuddy models calibrate")} to turn on dense recall.`
2878
3031
  )
2879
3032
  );
2880
3033
  }
2881
3034
  if (!missingKey) {
2882
3035
  syncInBackground(process.cwd(), true);
2883
3036
  console.log(
2884
- pc13.dim(" Re-embedding existing memory in the background (codebuddy hooks sync).")
3037
+ pc14.dim(" Re-embedding existing memory in the background (codebuddy hooks sync).")
2885
3038
  );
2886
3039
  }
2887
3040
  });
@@ -2908,13 +3061,13 @@ before keeping one on. A reranker never admits a fact, it only orders them.`
2908
3061
  }
2909
3062
  const reranker = choice;
2910
3063
  console.log(
2911
- ok(`Reranker: ${reranker} ${pc13.dim(opts.model ?? DEFAULT_RERANK_MODELS[reranker].id)}`)
3064
+ ok(`Reranker: ${reranker} ${pc14.dim(opts.model ?? DEFAULT_RERANK_MODELS[reranker].id)}`)
2912
3065
  );
2913
3066
  if (reranker !== "local" && !keyPresent(reranker, (await readConfigFile()).provider?.apiKey)) {
2914
3067
  console.log(warn(`Set ${KEY_ENV2[reranker]}; without it reranking is skipped.`));
2915
3068
  }
2916
3069
  if (reranker === "local")
2917
- console.log(pc13.dim(" Run codebuddy models pull to download it now."));
3070
+ console.log(pc14.dim(" Run codebuddy models pull to download it now."));
2918
3071
  });
2919
3072
  });
2920
3073
  models.command("pull").description("Download the local models now instead of on first use.").action(async () => {
@@ -2925,7 +3078,7 @@ before keeping one on. A reranker never admits a fact, it only orders them.`
2925
3078
  await withSpinner(
2926
3079
  `Downloading ${LOCAL_EMBEDDING_MODEL.id}`,
2927
3080
  () => new LocalModelProvider().embed({ input: "warm up" }),
2928
- () => ok(`${LOCAL_EMBEDDING_MODEL.id} ready ${pc13.dim(`${Date.now() - started} ms`)}`)
3081
+ () => ok(`${LOCAL_EMBEDDING_MODEL.id} ready ${pc14.dim(`${Date.now() - started} ms`)}`)
2929
3082
  );
2930
3083
  if (file.rerank?.provider === "local") {
2931
3084
  const id = file.rerank.model ?? DEFAULT_RERANK_MODELS.local.id;
@@ -2934,10 +3087,10 @@ before keeping one on. A reranker never admits a fact, it only orders them.`
2934
3087
  await withSpinner(
2935
3088
  `Downloading ${id}`,
2936
3089
  () => rerankLocally({ id, ...revision ? { revision } : {} }, "warm", ["up"]),
2937
- () => ok(`${id} ready ${pc13.dim(`${Date.now() - started} ms`)}`)
3090
+ () => ok(`${id} ready ${pc14.dim(`${Date.now() - started} ms`)}`)
2938
3091
  );
2939
3092
  }
2940
- console.log(pc13.dim(` Cache: ${modelCacheDir()}`));
3093
+ console.log(pc14.dim(` Cache: ${modelCacheDir()}`));
2941
3094
  });
2942
3095
  });
2943
3096
  models.command("calibrate").option("--dry-run", "Measure only; do not save the floor.").description(
@@ -2955,19 +3108,19 @@ before keeping one on. A reranker never admits a fact, it only orders them.`
2955
3108
  console.log(
2956
3109
  row(
2957
3110
  "ranking",
2958
- `AUC ${result.auc.toFixed(3)} \xB7 right fact first ${pct(result.recallAt1)} ${pc13.dim("(held-out tasks)")}`
3111
+ `AUC ${result.auc.toFixed(3)} \xB7 right fact first ${pct(result.recallAt1)} ${pc14.dim("(held-out tasks)")}`
2959
3112
  )
2960
3113
  );
2961
3114
  console.log(
2962
3115
  row(
2963
3116
  "floor",
2964
- `${result.best.floor}: precision ${pct(result.test.precision)}, recall ${pct(result.test.recall)}, silent on ${pct(result.test.abstain)} of unrelated tasks ${pc13.dim("(chosen on dev, measured on test)")}`
3117
+ `${result.best.floor}: precision ${pct(result.test.precision)}, recall ${pct(result.test.recall)}, silent on ${pct(result.test.abstain)} of unrelated tasks ${pc14.dim("(chosen on dev, measured on test)")}`
2965
3118
  )
2966
3119
  );
2967
3120
  console.log(
2968
3121
  row(
2969
3122
  "recall floor",
2970
- `${result.recall.dev.floor}: precision ${pct(result.recall.test.precision)}, recall ${pct(result.recall.test.recall)} ` + pc13.dim("(explicit recall: best F2 on dev, measured on test)")
3123
+ `${result.recall.dev.floor}: precision ${pct(result.recall.test.precision)}, recall ${pct(result.recall.test.recall)} ` + pc14.dim("(explicit recall: best F2 on dev, measured on test)")
2971
3124
  )
2972
3125
  );
2973
3126
  if (opts.dryRun) return;
@@ -2986,10 +3139,238 @@ before keeping one on. A reranker never admits a fact, it only orders them.`
2986
3139
  });
2987
3140
  }
2988
3141
 
2989
- // src/cli/commands/plan.ts
3142
+ // src/cli/commands/output.ts
2990
3143
  import { spawn as spawn5 } from "child_process";
3144
+ import { constants as constants2 } from "os";
3145
+ import pc15 from "picocolors";
3146
+
3147
+ // src/output/reduce.ts
3148
+ import { createHash } from "crypto";
3149
+ import { mkdir as mkdir2, readdir, readFile as readFile3, rm as rm2 } from "fs/promises";
3150
+ import { join as join6 } from "path";
3151
+ var OUTPUT_LIMITS = {
3152
+ /** At or below either, output is never reduced. */
3153
+ minLines: 80,
3154
+ minBytes: 6e3,
3155
+ head: 20,
3156
+ tail: 30,
3157
+ contextBefore: 2,
3158
+ contextAfter: 4,
3159
+ /** A reduction must save at least this share, or the original is printed. */
3160
+ minSaving: 0.3,
3161
+ /** Stored bytes per run; beyond this the stored copy says it was cut. */
3162
+ storeBytes: 2e7,
3163
+ retentionDays: 7,
3164
+ /** Lines one `output show` may print. */
3165
+ showLines: 400
3166
+ };
3167
+ var SIGNAL = /\b(error|errors|fail|failed|failure|failing|exception|traceback|panic|fatal|assert(?:ion)?|warn(?:ing)?|denied|not found|undefined|segfault|timeout|timed out)\b|✗|×|✖|FAIL|ERR!/i;
3168
+ function outputDir(root) {
3169
+ return join6(root, ".codebuddy", "cache", "outputs");
3170
+ }
3171
+ var shape = (line) => line.replace(/\d+/g, "#").trim();
3172
+ function reduceOutput(text3, id) {
3173
+ const lines = text3.split("\n");
3174
+ if (lines.length <= OUTPUT_LIMITS.minLines || Buffer.byteLength(text3) <= OUTPUT_LIMITS.minBytes) {
3175
+ return null;
3176
+ }
3177
+ if (text3.includes("\0")) return null;
3178
+ const keep = /* @__PURE__ */ new Set();
3179
+ for (let i = 0; i < Math.min(OUTPUT_LIMITS.head, lines.length); i++) keep.add(i);
3180
+ for (let i = Math.max(0, lines.length - OUTPUT_LIMITS.tail); i < lines.length; i++) keep.add(i);
3181
+ lines.forEach((line, index2) => {
3182
+ if (!SIGNAL.test(line)) return;
3183
+ const from = Math.max(0, index2 - OUTPUT_LIMITS.contextBefore);
3184
+ const to = Math.min(lines.length - 1, index2 + OUTPUT_LIMITS.contextAfter);
3185
+ for (let i = from; i <= to; i++) keep.add(i);
3186
+ });
3187
+ const out = [];
3188
+ let index = 0;
3189
+ while (index < lines.length) {
3190
+ if (!keep.has(index)) {
3191
+ const start = index;
3192
+ while (index < lines.length && !keep.has(index)) index++;
3193
+ out.push(
3194
+ `\u2026 ${index - start} lines omitted (codebuddy output show ${id} --range ${start + 1}:${index})`
3195
+ );
3196
+ continue;
3197
+ }
3198
+ const line = lines[index];
3199
+ const same = SIGNAL.test(line) ? (other) => other === line : (other) => shape(other) === shape(line);
3200
+ let run = index + 1;
3201
+ while (run < lines.length && keep.has(run) && same(lines[run])) run++;
3202
+ out.push(line);
3203
+ if (run - index > 2) {
3204
+ out.push(
3205
+ `\u2026 ${run - index - 1} similar lines (codebuddy output show ${id} --range ${index + 2}:${run})`
3206
+ );
3207
+ index = run;
3208
+ } else index++;
3209
+ }
3210
+ const reduced = out.join("\n");
3211
+ return Buffer.byteLength(reduced) <= Buffer.byteLength(text3) * (1 - OUTPUT_LIMITS.minSaving) ? reduced : null;
3212
+ }
3213
+ async function storeOutput(root, command, output, exitCode, now = /* @__PURE__ */ new Date()) {
3214
+ const truncated = Buffer.byteLength(output) > OUTPUT_LIMITS.storeBytes;
3215
+ const body = truncated ? `${output.slice(0, OUTPUT_LIMITS.storeBytes)}
3216
+ [codebuddy: stored copy cut at ${OUTPUT_LIMITS.storeBytes} bytes]` : output;
3217
+ const id = createHash("sha256").update(`${command}\0${now.toISOString()}\0${body}`).digest("hex").slice(0, 12);
3218
+ const meta = {
3219
+ id,
3220
+ command,
3221
+ exitCode,
3222
+ createdAt: now.toISOString(),
3223
+ expiresAt: new Date(now.getTime() + OUTPUT_LIMITS.retentionDays * 864e5).toISOString(),
3224
+ lines: body.split("\n").length,
3225
+ bytes: Buffer.byteLength(body),
3226
+ truncated
3227
+ };
3228
+ const dir = outputDir(root);
3229
+ await mkdir2(dir, { recursive: true, mode: 448 });
3230
+ await writeAtomic(join6(dir, `${id}.log`), body, { mode: 384 });
3231
+ await writeAtomic(join6(dir, `${id}.json`), `${JSON.stringify(meta)}
3232
+ `, { mode: 384 });
3233
+ await pruneOutputs(root, now).catch(() => void 0);
3234
+ return meta;
3235
+ }
3236
+ async function pruneOutputs(root, now = /* @__PURE__ */ new Date()) {
3237
+ const dir = outputDir(root);
3238
+ let removed = 0;
3239
+ for (const name2 of (await readdir(dir).catch(() => [])).filter(
3240
+ (n) => n.endsWith(".json")
3241
+ )) {
3242
+ const meta = await readMeta(root, name2.slice(0, -5));
3243
+ if (meta && Date.parse(meta.expiresAt) <= now.getTime()) {
3244
+ await rm2(join6(dir, `${meta.id}.log`), { force: true });
3245
+ await rm2(join6(dir, name2), { force: true });
3246
+ removed++;
3247
+ }
3248
+ }
3249
+ return removed;
3250
+ }
3251
+ async function readMeta(root, id) {
3252
+ if (!/^[a-f0-9]{12}$/.test(id)) return null;
3253
+ try {
3254
+ return JSON.parse(await readFile3(join6(outputDir(root), `${id}.json`), "utf8"));
3255
+ } catch {
3256
+ return null;
3257
+ }
3258
+ }
3259
+ async function showOutput(root, id, options = {}) {
3260
+ const meta = await readMeta(root, id);
3261
+ if (!meta) return { status: "missing", id };
3262
+ if (Date.parse(meta.expiresAt) <= (options.now ?? /* @__PURE__ */ new Date()).getTime()) {
3263
+ return { status: "expired", id };
3264
+ }
3265
+ const body = await readFile3(join6(outputDir(root), `${id}.log`), "utf8").catch(() => null);
3266
+ if (body === null) return { status: "missing", id };
3267
+ const lines = body.split("\n");
3268
+ let picked;
3269
+ if (options.grep) {
3270
+ const pattern = new RegExp(options.grep, "i");
3271
+ const set = /* @__PURE__ */ new Set();
3272
+ lines.forEach((line, index) => {
3273
+ if (!pattern.test(line)) return;
3274
+ for (let i = Math.max(0, index - 2); i <= Math.min(lines.length - 1, index + 2); i++)
3275
+ set.add(i);
3276
+ });
3277
+ picked = [...set].sort((a, b) => a - b);
3278
+ } else {
3279
+ const [from, to] = options.range ?? [1, lines.length];
3280
+ picked = [];
3281
+ for (let i = Math.max(1, from); i <= Math.min(lines.length, to); i++) picked.push(i - 1);
3282
+ }
3283
+ const shown = picked.slice(0, OUTPUT_LIMITS.showLines);
3284
+ return {
3285
+ status: "ok",
3286
+ meta,
3287
+ text: shown.map((i) => `${i + 1}: ${lines[i]}`).join("\n"),
3288
+ more: picked.length > shown.length
3289
+ };
3290
+ }
3291
+ async function listOutputs(root) {
3292
+ const names = await readdir(outputDir(root)).catch(() => []);
3293
+ const metas = await Promise.all(
3294
+ names.filter((n) => n.endsWith(".json")).map((n) => readMeta(root, n.slice(0, -5)))
3295
+ );
3296
+ return metas.filter((meta) => meta !== null).sort((a, b) => b.createdAt.localeCompare(a.createdAt));
3297
+ }
3298
+
3299
+ // src/cli/commands/output.ts
3300
+ function registerOutputCommands(program, runSafely2) {
3301
+ program.command("run").argument("<command...>", "The command and its arguments (after --).").description(
3302
+ "Run a command; print a reduced view of long output and keep the original for `codebuddy output show`."
3303
+ ).action(async (parts) => {
3304
+ await runSafely2(async () => {
3305
+ const command = parts.join(" ");
3306
+ const { output: output2, exitCode } = await capture(parts);
3307
+ const root = process.cwd();
3308
+ const provisional = reduceOutput(output2, "xxxxxxxxxxxx");
3309
+ if (provisional === null) {
3310
+ process.stdout.write(output2);
3311
+ } else {
3312
+ const stored = await storeOutput(root, command, output2, exitCode);
3313
+ const reduced = reduceOutput(output2, stored.id) ?? output2;
3314
+ process.stdout.write(`${reduced}
3315
+ `);
3316
+ process.stdout.write(
3317
+ `[codebuddy run: exit ${exitCode ?? "signal"}; ${stored.lines} lines shown as ${reduced.split("\n").length}; full output: codebuddy output show ${stored.id} [--grep RE | --range A:B], kept until ${stored.expiresAt.slice(0, 10)}]
3318
+ `
3319
+ );
3320
+ }
3321
+ process.exitCode = exitCode ?? 1;
3322
+ });
3323
+ });
3324
+ const output = program.command("output").description("Stored originals of `codebuddy run` output.");
3325
+ output.command("list", { isDefault: true }).description("List stored outputs, newest first.").action(async () => {
3326
+ await runSafely2(async () => {
3327
+ const stored = await listOutputs(process.cwd());
3328
+ if (stored.length === 0) console.log(pc15.dim("no stored outputs"));
3329
+ for (const meta of stored) {
3330
+ console.log(
3331
+ `${meta.id} exit ${meta.exitCode ?? "-"} ${meta.lines} lines until ${meta.expiresAt.slice(0, 10)} ${meta.command.slice(0, 80)}`
3332
+ );
3333
+ }
3334
+ });
3335
+ });
3336
+ output.command("show").argument("<id>").option("--range <a:b>", "1-based inclusive line range.").option("--grep <pattern>", "Lines matching a regular expression, with 2 lines of context.").description("Print exact stored lines (at most 400 per call).").action(async (id, opts) => {
3337
+ await runSafely2(async () => {
3338
+ const range = opts.range?.match(/^(\d+):(\d+)$/);
3339
+ if (opts.range && !range) throw new Error("--range must look like 120:180");
3340
+ const result = await showOutput(process.cwd(), id, {
3341
+ ...range ? { range: [Number(range[1]), Number(range[2])] } : {},
3342
+ ...opts.grep ? { grep: opts.grep } : {}
3343
+ });
3344
+ if (result.status !== "ok") {
3345
+ throw new Error(`stored output ${id} is ${result.status}`);
3346
+ }
3347
+ console.log(result.text);
3348
+ if (result.more) console.log(pc15.dim("\u2026 more lines; narrow --range or --grep"));
3349
+ });
3350
+ });
3351
+ }
3352
+ function capture(parts) {
3353
+ return new Promise((resolve6, reject) => {
3354
+ const [file, ...args] = parts;
3355
+ const child = parts.length === 1 ? spawn5(file, { shell: true, stdio: ["inherit", "pipe", "pipe"] }) : spawn5(file, args, { stdio: ["inherit", "pipe", "pipe"] });
3356
+ const chunks = [];
3357
+ child.stdout.on("data", (chunk) => chunks.push(chunk));
3358
+ child.stderr.on("data", (chunk) => chunks.push(chunk));
3359
+ child.on("error", reject);
3360
+ child.on(
3361
+ "close",
3362
+ (code, signal) => resolve6({
3363
+ output: Buffer.concat(chunks).toString("utf8"),
3364
+ exitCode: code ?? (signal ? 128 + (constants2.signals[signal] ?? 0) : null)
3365
+ })
3366
+ );
3367
+ });
3368
+ }
3369
+
3370
+ // src/cli/commands/plan.ts
3371
+ import { spawn as spawn6 } from "child_process";
2991
3372
  import { relative as relative4 } from "path";
2992
- import pc14 from "picocolors";
3373
+ import pc16 from "picocolors";
2993
3374
  function makeLifecycle(cwd = process.cwd()) {
2994
3375
  const store = new PlanFileStore(cwd);
2995
3376
  return { store, lifecycle: new PlanLifecycle(store) };
@@ -2997,67 +3378,67 @@ function makeLifecycle(cwd = process.cwd()) {
2997
3378
  function statusColor(status) {
2998
3379
  switch (status) {
2999
3380
  case "draft":
3000
- return pc14.dim(status);
3381
+ return pc16.dim(status);
3001
3382
  case "approved":
3002
- return pc14.cyan(status);
3383
+ return pc16.cyan(status);
3003
3384
  case "executing":
3004
- return pc14.yellow(status);
3385
+ return pc16.yellow(status);
3005
3386
  case "complete":
3006
- return pc14.green(status);
3387
+ return pc16.green(status);
3007
3388
  case "abandoned":
3008
- return pc14.red(status);
3389
+ return pc16.red(status);
3009
3390
  }
3010
3391
  }
3011
3392
  function renderPlan(plan) {
3012
3393
  const lines = [];
3013
- lines.push(`${pc14.bold(plan.title)} ${statusColor(plan.status)}`);
3014
- lines.push(pc14.dim(plan.id));
3394
+ lines.push(`${pc16.bold(plan.title)} ${statusColor(plan.status)}`);
3395
+ lines.push(pc16.dim(plan.id));
3015
3396
  lines.push("");
3016
- lines.push(`${pc14.bold("Brief:")} ${plan.brief}`);
3397
+ lines.push(`${pc16.bold("Brief:")} ${plan.brief}`);
3017
3398
  if (plan.filesToTouch.length > 0) {
3018
3399
  lines.push("");
3019
- lines.push(pc14.bold("Files to touch:"));
3400
+ lines.push(pc16.bold("Files to touch:"));
3020
3401
  for (const file of plan.filesToTouch) {
3021
3402
  lines.push(` ${file.action.padEnd(6)} ${file.path}${file.notes ? ` \u2014 ${file.notes}` : ""}`);
3022
3403
  }
3023
3404
  }
3024
3405
  if (plan.testsToAdd.length > 0) {
3025
3406
  lines.push("");
3026
- lines.push(pc14.bold("Tests to add:"));
3407
+ lines.push(pc16.bold("Tests to add:"));
3027
3408
  for (const test of plan.testsToAdd) {
3028
3409
  lines.push(` ${test.path}${test.description ? ` \u2014 ${test.description}` : ""}`);
3029
3410
  }
3030
3411
  }
3031
3412
  if (plan.outOfScope.length > 0) {
3032
3413
  lines.push("");
3033
- lines.push(pc14.bold("Out of scope:"));
3414
+ lines.push(pc16.bold("Out of scope:"));
3034
3415
  for (const item of plan.outOfScope) lines.push(` - ${item}`);
3035
3416
  }
3036
3417
  if (plan.risks.length > 0) {
3037
3418
  lines.push("");
3038
- lines.push(pc14.bold("Risks:"));
3419
+ lines.push(pc16.bold("Risks:"));
3039
3420
  for (const item of plan.risks) lines.push(` - ${item}`);
3040
3421
  }
3041
3422
  if (plan.commitSha) {
3042
3423
  lines.push("");
3043
- lines.push(`${pc14.bold("Commit:")} ${plan.commitSha}`);
3424
+ lines.push(`${pc16.bold("Commit:")} ${plan.commitSha}`);
3044
3425
  }
3045
3426
  lines.push("");
3046
- lines.push(pc14.dim("\u2500".repeat(40)));
3427
+ lines.push(pc16.dim("\u2500".repeat(40)));
3047
3428
  lines.push(plan.body);
3048
3429
  return lines.join("\n");
3049
3430
  }
3050
3431
  function openEditor2(path) {
3051
3432
  const editor = process.env.EDITOR ?? process.env.VISUAL ?? "vi";
3052
3433
  return new Promise((resolve6, reject) => {
3053
- const child = spawn5(editor, [path], { stdio: "inherit" });
3434
+ const child = spawn6(editor, [path], { stdio: "inherit" });
3054
3435
  child.on("error", reject);
3055
3436
  child.on("close", () => resolve6());
3056
3437
  });
3057
3438
  }
3058
3439
  function runGit(args) {
3059
3440
  return new Promise((resolve6, reject) => {
3060
- const child = spawn5("git", args, { stdio: ["ignore", "pipe", "pipe"] });
3441
+ const child = spawn6("git", args, { stdio: ["ignore", "pipe", "pipe"] });
3061
3442
  let stdout = "";
3062
3443
  let stderr = "";
3063
3444
  child.stdout.on("data", (c) => {
@@ -3082,14 +3463,14 @@ function registerPlanCommands(program, runSafely2) {
3082
3463
  title: opts.title ?? brief,
3083
3464
  brief
3084
3465
  });
3085
- console.log(`${pc14.green("created")} ${created.id} (${statusColor(created.status)})`);
3466
+ console.log(`${pc16.green("created")} ${created.id} (${statusColor(created.status)})`);
3086
3467
  if (opts.edit !== false && process.stdin.isTTY) {
3087
3468
  await openEditor2(created.path);
3088
3469
  const { store } = makeLifecycle();
3089
3470
  await store.readPlan(created.id);
3090
- console.log(pc14.dim("saved and validated"));
3471
+ console.log(pc16.dim("saved and validated"));
3091
3472
  }
3092
- console.log(pc14.dim(relative4(process.cwd(), created.path)));
3473
+ console.log(pc16.dim(relative4(process.cwd(), created.path)));
3093
3474
  });
3094
3475
  });
3095
3476
  plan.command("list").option("--status <status>", "Filter by status.").option("--all", "Include completed and abandoned plans.").description("List plans in the current namespace.").action(async (opts) => {
@@ -3101,11 +3482,11 @@ function registerPlanCommands(program, runSafely2) {
3101
3482
  else if (!opts.all)
3102
3483
  plans = plans.filter((p) => p.status !== "complete" && p.status !== "abandoned");
3103
3484
  if (plans.length === 0) {
3104
- console.log(pc14.dim("no plans"));
3485
+ console.log(pc16.dim("no plans"));
3105
3486
  return;
3106
3487
  }
3107
3488
  for (const p of plans) {
3108
- console.log(`${statusColor(p.status).padEnd(20)} ${pc14.bold(p.title)} ${pc14.dim(p.id)}`);
3489
+ console.log(`${statusColor(p.status).padEnd(20)} ${pc16.bold(p.title)} ${pc16.dim(p.id)}`);
3109
3490
  }
3110
3491
  });
3111
3492
  });
@@ -3121,7 +3502,7 @@ function registerPlanCommands(program, runSafely2) {
3121
3502
  const existing = await store.readPlan(id);
3122
3503
  await openEditor2(existing.path);
3123
3504
  await store.readPlan(id);
3124
- console.log(pc14.dim("saved and validated"));
3505
+ console.log(pc16.dim("saved and validated"));
3125
3506
  });
3126
3507
  });
3127
3508
  plan.command("approve").argument("<id>").description("Promote a draft to approved (one active plan per namespace).").action(async (id) => {
@@ -3145,7 +3526,7 @@ function registerPlanCommands(program, runSafely2) {
3145
3526
  ...opts.commit ? { commitSha: opts.commit } : {},
3146
3527
  ...opts.notes ? { notes: opts.notes } : {}
3147
3528
  });
3148
- if (!opts.commit) console.log(pc14.yellow("note: no --commit supplied"));
3529
+ if (!opts.commit) console.log(pc16.yellow("note: no --commit supplied"));
3149
3530
  console.log(`${next.id} \u2192 ${statusColor(next.status)}`);
3150
3531
  });
3151
3532
  });
@@ -3164,7 +3545,7 @@ function registerPlanCommands(program, runSafely2) {
3164
3545
  const args = ["diff", opts.from, ...opts.to ? [opts.to] : [], "--", relPath];
3165
3546
  const result = await runGit(args);
3166
3547
  if (result.stdout.trim().length === 0) {
3167
- console.log(pc14.dim("no changes"));
3548
+ console.log(pc16.dim("no changes"));
3168
3549
  } else {
3169
3550
  process.stdout.write(result.stdout);
3170
3551
  }
@@ -3181,13 +3562,13 @@ function registerPlanCommands(program, runSafely2) {
3181
3562
  throw new Error(`Invalid policy "${mode}". Use one of: ${PLAN_POLICIES.join(", ")}.`);
3182
3563
  }
3183
3564
  await setPlanPolicy(mode);
3184
- console.log(`${pc14.green("plan policy")} = ${mode}`);
3565
+ console.log(`${pc16.green("plan policy")} = ${mode}`);
3185
3566
  });
3186
3567
  });
3187
3568
  }
3188
3569
 
3189
3570
  // src/cli/commands/risk.ts
3190
- import pc15 from "picocolors";
3571
+ import pc17 from "picocolors";
3191
3572
  function registerRiskCommands(program, runSafely2) {
3192
3573
  const risk = program.command("risk").description("Assess evidence-backed change risk.");
3193
3574
  risk.command("assess").option("--plan <id>", "Assess paths listed in a CodeBuddy plan.").option("--paths <paths>", "Comma-separated repo-relative paths to assess.").option("--git", "Assess staged, unstaged, and untracked git changes.").option("--limit <n>", "Maximum risk items to show.", Number).option("--json", "Print machine-readable JSON.").option("--explain", "Show evidence for each risk item.").description("Assess risk for a plan, explicit paths, or current git changes.").action(
@@ -3215,31 +3596,31 @@ function parsePaths3(value) {
3215
3596
  }
3216
3597
  function printAssessment(assessment, explain) {
3217
3598
  if (assessment.items.length === 0) {
3218
- console.log(pc15.dim("no risk evidence found"));
3599
+ console.log(pc17.dim("no risk evidence found"));
3219
3600
  return;
3220
3601
  }
3221
3602
  for (const item of assessment.items) {
3222
3603
  console.log(
3223
- `${pc15.bold(item.path)} score ${scoreColor2(item.score)} category ${pc15.cyan(item.category)}`
3604
+ `${pc17.bold(item.path)} score ${scoreColor2(item.score)} category ${pc17.cyan(item.category)}`
3224
3605
  );
3225
3606
  console.log(` ${item.reason}`);
3226
3607
  if (explain) {
3227
3608
  for (const evidence of item.evidence) console.log(` \u21B3 ${formatEvidence(evidence)}`);
3228
3609
  } else {
3229
- console.log(pc15.dim(` ${item.evidence.length} evidence item(s)`));
3610
+ console.log(pc17.dim(` ${item.evidence.length} evidence item(s)`));
3230
3611
  }
3231
3612
  }
3232
3613
  console.log(
3233
- pc15.dim(
3614
+ pc17.dim(
3234
3615
  `
3235
3616
  ${assessment.stats.itemsProduced} item(s), ${assessment.stats.signalsRun} signal(s), ${assessment.stats.pathsConsidered} path(s) considered`
3236
3617
  )
3237
3618
  );
3238
3619
  }
3239
3620
  function scoreColor2(score) {
3240
- if (score >= 75) return pc15.red(String(score));
3241
- if (score >= 50) return pc15.yellow(String(score));
3242
- return pc15.green(String(score));
3621
+ if (score >= 75) return pc17.red(String(score));
3622
+ if (score >= 50) return pc17.yellow(String(score));
3623
+ return pc17.green(String(score));
3243
3624
  }
3244
3625
  function formatEvidence(evidence) {
3245
3626
  switch (evidence.kind) {
@@ -3261,7 +3642,7 @@ function truncate2(value, max) {
3261
3642
 
3262
3643
  // src/cli/commands/rules.ts
3263
3644
  import { relative as relative5 } from "path";
3264
- import pc16 from "picocolors";
3645
+ import pc18 from "picocolors";
3265
3646
  async function resolveMode(client, value) {
3266
3647
  if (value) {
3267
3648
  const mode = parseMode(value);
@@ -3301,7 +3682,7 @@ function registerRulesCommands(program, runSafely2) {
3301
3682
  `${actionLabel(result.action)} ${clientDisplayName(client)} workflow in ${where}`
3302
3683
  );
3303
3684
  if (result.action !== "unchanged") {
3304
- console.log(pc16.dim(" managed block between codebuddy:workflow markers; safe to re-run"));
3685
+ console.log(pc18.dim(" managed block between codebuddy:workflow markers; safe to re-run"));
3305
3686
  }
3306
3687
  });
3307
3688
  });
@@ -3309,20 +3690,20 @@ function registerRulesCommands(program, runSafely2) {
3309
3690
  function actionLabel(action) {
3310
3691
  switch (action) {
3311
3692
  case "created":
3312
- return pc16.green("created");
3693
+ return pc18.green("created");
3313
3694
  case "added":
3314
- return pc16.green("added");
3695
+ return pc18.green("added");
3315
3696
  case "updated":
3316
- return pc16.cyan("updated");
3697
+ return pc18.cyan("updated");
3317
3698
  case "removed":
3318
- return pc16.yellow("removed");
3699
+ return pc18.yellow("removed");
3319
3700
  default:
3320
- return pc16.dim("unchanged");
3701
+ return pc18.dim("unchanged");
3321
3702
  }
3322
3703
  }
3323
3704
 
3324
3705
  // src/cli/commands/suggest.ts
3325
- import pc17 from "picocolors";
3706
+ import pc19 from "picocolors";
3326
3707
  function parsePaths4(value) {
3327
3708
  return value.split(",").map((path) => path.trim()).filter(Boolean);
3328
3709
  }
@@ -3344,7 +3725,7 @@ function registerSuggestCommand(program, runSafely2) {
3344
3725
  }
3345
3726
  printSuggestions(result.suggestions);
3346
3727
  console.log(
3347
- pc17.dim(
3728
+ pc19.dim(
3348
3729
  `
3349
3730
  ${result.stats.produced} suggestion(s) across ${result.stats.pathsConsidered} path(s)`
3350
3731
  )
@@ -3355,72 +3736,72 @@ ${result.stats.produced} suggestion(s) across ${result.stats.pathsConsidered} pa
3355
3736
  }
3356
3737
  function printSuggestions(suggestions) {
3357
3738
  if (suggestions.length === 0) {
3358
- console.log(pc17.dim("no suggestions \u2014 nothing high-signal to report"));
3739
+ console.log(pc19.dim("no suggestions \u2014 nothing high-signal to report"));
3359
3740
  return;
3360
3741
  }
3361
3742
  for (const suggestion of suggestions) {
3362
- console.log(`${severityBadge2(suggestion.severity)} ${pc17.bold(suggestion.title)}`);
3743
+ console.log(`${severityBadge2(suggestion.severity)} ${pc19.bold(suggestion.title)}`);
3363
3744
  console.log(` ${suggestion.detail}`);
3364
3745
  for (const evidence of suggestion.evidence) {
3365
- console.log(pc17.dim(` \u21B3 ${evidence.kind}: ${evidence.detail}`));
3746
+ console.log(pc19.dim(` \u21B3 ${evidence.kind}: ${evidence.detail}`));
3366
3747
  }
3367
- if (suggestion.commandHint) console.log(pc17.dim(` \u2192 ${suggestion.commandHint}`));
3748
+ if (suggestion.commandHint) console.log(pc19.dim(` \u2192 ${suggestion.commandHint}`));
3368
3749
  }
3369
3750
  }
3370
3751
  function severityBadge2(severity) {
3371
3752
  switch (severity) {
3372
3753
  case "high":
3373
- return pc17.red("[high]");
3754
+ return pc19.red("[high]");
3374
3755
  case "medium":
3375
- return pc17.yellow("[med ]");
3756
+ return pc19.yellow("[med ]");
3376
3757
  case "low":
3377
- return pc17.cyan("[low ]");
3758
+ return pc19.cyan("[low ]");
3378
3759
  default:
3379
- return pc17.dim("[info]");
3760
+ return pc19.dim("[info]");
3380
3761
  }
3381
3762
  }
3382
3763
 
3383
3764
  // src/cli/commands/symbols.ts
3384
- import { readFile as readFile3 } from "fs/promises";
3765
+ import { readFile as readFile4 } from "fs/promises";
3385
3766
  import { resolve as resolve5 } from "path";
3386
- import pc18 from "picocolors";
3767
+ import pc20 from "picocolors";
3387
3768
  function registerSymbolsCommand(program, runSafely2) {
3388
3769
  program.command("symbols").argument("<path>", "Repo-relative or absolute path to a code file.").option("--json", "Print machine-readable JSON.").description("List a file's top-level symbols (functions, classes, types, exports).").action(async (path, opts) => {
3389
3770
  await runSafely2(async () => {
3390
3771
  if (!supportsSymbols(path)) {
3391
- console.log(pc18.dim(`no symbol extraction for ${path} (unsupported language)`));
3772
+ console.log(pc20.dim(`no symbol extraction for ${path} (unsupported language)`));
3392
3773
  return;
3393
3774
  }
3394
- const content = await readFile3(resolve5(path), "utf8");
3775
+ const content = await readFile4(resolve5(path), "utf8");
3395
3776
  const table = extractSymbolTable(content, path);
3396
3777
  if (opts.json) {
3397
3778
  console.log(JSON.stringify({ path, symbols: table }, null, 2));
3398
3779
  return;
3399
3780
  }
3400
3781
  if (table.length === 0) {
3401
- console.log(pc18.dim("no top-level symbols found"));
3782
+ console.log(pc20.dim("no top-level symbols found"));
3402
3783
  return;
3403
3784
  }
3404
3785
  for (const symbol of table) {
3405
- const badge = symbol.exported ? pc18.green("export") : pc18.dim("local ");
3786
+ const badge = symbol.exported ? pc20.green("export") : pc20.dim("local ");
3406
3787
  console.log(
3407
- `${badge} ${pc18.cyan(symbol.kind.padEnd(9))} ${pc18.bold(symbol.name)} ${pc18.dim(`L${symbol.startLine}-${symbol.endLine}`)}`
3788
+ `${badge} ${pc20.cyan(symbol.kind.padEnd(9))} ${pc20.bold(symbol.name)} ${pc20.dim(`L${symbol.startLine}-${symbol.endLine}`)}`
3408
3789
  );
3409
3790
  }
3410
- console.log(pc18.dim(`
3791
+ console.log(pc20.dim(`
3411
3792
  ${table.length} symbol(s)`));
3412
3793
  });
3413
3794
  });
3414
3795
  }
3415
3796
 
3416
3797
  // src/cli/commands/telemetry.ts
3417
- import pc19 from "picocolors";
3798
+ import pc21 from "picocolors";
3418
3799
 
3419
3800
  // src/telemetry/collector.ts
3420
- import { createHash, timingSafeEqual } from "crypto";
3421
- import { appendFile, mkdir as mkdir2, readdir, readFile as readFile4 } from "fs/promises";
3801
+ import { createHash as createHash2, timingSafeEqual } from "crypto";
3802
+ import { appendFile, mkdir as mkdir3, readdir as readdir2, readFile as readFile5 } from "fs/promises";
3422
3803
  import { createServer as createServer2 } from "http";
3423
- import { join as join6 } from "path";
3804
+ import { join as join7 } from "path";
3424
3805
  import { z as z2 } from "zod";
3425
3806
 
3426
3807
  // src/telemetry/admin-page.ts
@@ -3945,7 +4326,7 @@ var batchSchema = z2.object({
3945
4326
  });
3946
4327
  async function startCollector(options) {
3947
4328
  const { dataDir } = options;
3948
- await mkdir2(dataDir, { recursive: true });
4329
+ await mkdir3(dataDir, { recursive: true });
3949
4330
  const registry = await loadRegistry(dataDir);
3950
4331
  let summaryCache;
3951
4332
  let npmCache;
@@ -3993,7 +4374,7 @@ async function startCollector(options) {
3993
4374
  if (!registry.has(batch.installId)) {
3994
4375
  registry.set(batch.installId, receivedAt);
3995
4376
  await appendFile(
3996
- join6(dataDir, "installs.jsonl"),
4377
+ join7(dataDir, "installs.jsonl"),
3997
4378
  `${JSON.stringify({ installId: batch.installId, firstSeen: receivedAt })}
3998
4379
  `
3999
4380
  );
@@ -4001,7 +4382,7 @@ async function startCollector(options) {
4001
4382
  const { events, ...meta } = batch;
4002
4383
  const lines = events.map((event) => JSON.stringify({ ...meta, ...event, receivedAt }));
4003
4384
  await appendFile(
4004
- join6(dataDir, `events-${receivedAt.slice(0, 10)}.jsonl`),
4385
+ join7(dataDir, `events-${receivedAt.slice(0, 10)}.jsonl`),
4005
4386
  `${lines.join("\n")}
4006
4387
  `
4007
4388
  );
@@ -4098,7 +4479,7 @@ function passwordMatches(request, password3) {
4098
4479
  if (!header.startsWith("Basic ")) return false;
4099
4480
  const decoded = Buffer.from(header.slice(6), "base64").toString("utf8");
4100
4481
  const given = decoded.slice(decoded.indexOf(":") + 1);
4101
- const digest = (value) => createHash("sha256").update(value).digest();
4482
+ const digest = (value) => createHash2("sha256").update(value).digest();
4102
4483
  return timingSafeEqual(digest(given), digest(password3));
4103
4484
  }
4104
4485
  function readBody(request) {
@@ -4122,10 +4503,10 @@ function listen2(server, port) {
4122
4503
  }
4123
4504
  async function readEvents(dataDir, days, now = Date.now()) {
4124
4505
  const since = new Date(now - (days - 1) * 864e5).toISOString().slice(0, 10);
4125
- const files = (await readdir(dataDir).catch(() => [])).filter((file) => /^events-\d{4}-\d{2}-\d{2}\.jsonl$/.test(file) && file.slice(7, 17) >= since).sort();
4506
+ const files = (await readdir2(dataDir).catch(() => [])).filter((file) => /^events-\d{4}-\d{2}-\d{2}\.jsonl$/.test(file) && file.slice(7, 17) >= since).sort();
4126
4507
  const events = [];
4127
4508
  for (const file of files) {
4128
- for (const line of (await readFile4(join6(dataDir, file), "utf8")).split("\n")) {
4509
+ for (const line of (await readFile5(join7(dataDir, file), "utf8")).split("\n")) {
4129
4510
  if (!line) continue;
4130
4511
  try {
4131
4512
  events.push(JSON.parse(line));
@@ -4400,8 +4781,8 @@ function installTrace(events, prefix) {
4400
4781
  }
4401
4782
  async function loadRegistry(dataDir) {
4402
4783
  const registry = /* @__PURE__ */ new Map();
4403
- const path = join6(dataDir, "installs.jsonl");
4404
- const raw = await readFile4(path, "utf8").catch(() => null);
4784
+ const path = join7(dataDir, "installs.jsonl");
4785
+ const raw = await readFile5(path, "utf8").catch(() => null);
4405
4786
  if (raw !== null) {
4406
4787
  for (const line of raw.split("\n")) {
4407
4788
  try {
@@ -4446,10 +4827,10 @@ function registerTelemetryCommands(program, runSafely2) {
4446
4827
  const state = await readTelemetryState();
4447
4828
  const blocked = telemetryBlocked(state);
4448
4829
  console.log(
4449
- blocked ? `${pc19.yellow("\u25CB")} telemetry off (${blocked})` : `${pc19.green("\u25CF")} telemetry on`
4830
+ blocked ? `${pc21.yellow("\u25CB")} telemetry off (${blocked})` : `${pc21.green("\u25CF")} telemetry on`
4450
4831
  );
4451
- console.log(pc19.dim(` install id ${state.installId} (${telemetryStatePath()})`));
4452
- if (telemetryUrl()) console.log(pc19.dim(` collector ${telemetryUrl()}`));
4832
+ console.log(pc21.dim(` install id ${state.installId} (${telemetryStatePath()})`));
4833
+ if (telemetryUrl()) console.log(pc21.dim(` collector ${telemetryUrl()}`));
4453
4834
  });
4454
4835
  });
4455
4836
  telemetry.command("enable").description("Allow anonymous usage data from this machine.").action(async () => {
@@ -4460,7 +4841,7 @@ function registerTelemetryCommands(program, runSafely2) {
4460
4841
  noticeAt: state.noticeAt ?? (/* @__PURE__ */ new Date()).toISOString()
4461
4842
  });
4462
4843
  console.log(TELEMETRY_NOTICE);
4463
- console.log(pc19.green("Telemetry enabled."));
4844
+ console.log(pc21.green("Telemetry enabled."));
4464
4845
  });
4465
4846
  });
4466
4847
  telemetry.command("disable").description("Stop sending anonymous usage data from this machine.").action(async () => {
@@ -4488,14 +4869,14 @@ function registerTelemetryCommands(program, runSafely2) {
4488
4869
  adminPort: opts.adminPort,
4489
4870
  ...process.env.CODEBUDDY_ADMIN_PASSWORD ? { adminPassword: process.env.CODEBUDDY_ADMIN_PASSWORD } : {}
4490
4871
  });
4491
- console.log(`${pc19.green("\u25CF")} ingest ${pc19.bold(collector.ingestUrl)}`);
4492
- console.log(`${pc19.green("\u25CF")} admin ${pc19.bold(collector.adminUrl)}`);
4872
+ console.log(`${pc21.green("\u25CF")} ingest ${pc21.bold(collector.ingestUrl)}`);
4873
+ console.log(`${pc21.green("\u25CF")} admin ${pc21.bold(collector.adminUrl)}`);
4493
4874
  console.log(
4494
- pc19.dim(
4875
+ pc21.dim(
4495
4876
  process.env.CODEBUDDY_ADMIN_PASSWORD ? " admin panel is password-protected (CODEBUDDY_ADMIN_PASSWORD)." : " admin panel has no password: keep it private, or set CODEBUDDY_ADMIN_PASSWORD."
4496
4877
  )
4497
4878
  );
4498
- console.log(pc19.dim(` events in ${opts.data}. Ctrl+C to stop.`));
4879
+ console.log(pc21.dim(` events in ${opts.data}. Ctrl+C to stop.`));
4499
4880
  await new Promise((resolve6) => {
4500
4881
  const stop = () => void collector.close().then(resolve6);
4501
4882
  process.once("SIGINT", stop);
@@ -4508,12 +4889,12 @@ function registerTelemetryCommands(program, runSafely2) {
4508
4889
  // src/cli/commands/use-command.ts
4509
4890
  import { basename as basename2 } from "path";
4510
4891
  import { confirm as confirm2, isCancel as isCancel2, log as log2, note as note2, password as password2, select, spinner as spinner3, text as text2 } from "@clack/prompts";
4511
- import pc20 from "picocolors";
4892
+ import pc22 from "picocolors";
4512
4893
 
4513
4894
  // src/core/global-config.ts
4514
- import { chmod, mkdir as mkdir3, readFile as readFile5, writeFile as writeFile3 } from "fs/promises";
4895
+ import { chmod, mkdir as mkdir4, readFile as readFile6, writeFile as writeFile3 } from "fs/promises";
4515
4896
  import { homedir } from "os";
4516
- import { dirname as dirname3, join as join7 } from "path";
4897
+ import { dirname as dirname3, join as join8 } from "path";
4517
4898
  import { z as z3 } from "zod";
4518
4899
  var GLOBAL_DIR = ".codebuddy";
4519
4900
  var GLOBAL_FILE = "global.json";
@@ -4526,12 +4907,12 @@ var globalConfigSchema = z3.object({
4526
4907
  installPath: z3.string().optional()
4527
4908
  });
4528
4909
  function globalConfigPath(home = homedir()) {
4529
- return join7(home, GLOBAL_DIR, GLOBAL_FILE);
4910
+ return join8(home, GLOBAL_DIR, GLOBAL_FILE);
4530
4911
  }
4531
4912
  async function readGlobalConfig(home = homedir()) {
4532
4913
  const path = globalConfigPath(home);
4533
4914
  try {
4534
- const raw = await readFile5(path, "utf8");
4915
+ const raw = await readFile6(path, "utf8");
4535
4916
  return globalConfigSchema.parse(JSON.parse(raw));
4536
4917
  } catch (error) {
4537
4918
  if (error.code === "ENOENT") return {};
@@ -4540,7 +4921,7 @@ async function readGlobalConfig(home = homedir()) {
4540
4921
  }
4541
4922
  async function writeGlobalConfig(next, home = homedir()) {
4542
4923
  const path = globalConfigPath(home);
4543
- await mkdir3(dirname3(path), { recursive: true, mode: 448 });
4924
+ await mkdir4(dirname3(path), { recursive: true, mode: 448 });
4544
4925
  await writeFile3(path, `${JSON.stringify(next, null, 2)}
4545
4926
  `, { mode: 384 });
4546
4927
  await chmod(path, 384);
@@ -4592,15 +4973,15 @@ async function runUseCommand(options = {}) {
4592
4973
  })
4593
4974
  ));
4594
4975
  note2(
4595
- `Project folder: ${pc20.bold(process.cwd())}
4596
- Namespace: ${pc20.bold(namespace)}`,
4976
+ `Project folder: ${pc22.bold(process.cwd())}
4977
+ Namespace: ${pc22.bold(namespace)}`,
4597
4978
  "codebuddy use"
4598
4979
  );
4599
4980
  const stored = await readGlobalConfig();
4600
4981
  const projectConfig = await readConfigFile();
4601
4982
  let embeddings = projectConfig.provider?.type;
4602
4983
  if (embeddings) {
4603
- log2.info(`Embeddings: ${embeddings} (change with ${pc20.cyan("codebuddy models use")}).`);
4984
+ log2.info(`Embeddings: ${embeddings} (change with ${pc22.cyan("codebuddy models use")}).`);
4604
4985
  } else if (!interactive) {
4605
4986
  embeddings = "local";
4606
4987
  } else {
@@ -4641,7 +5022,7 @@ Namespace: ${pc20.bold(namespace)}`,
4641
5022
  log2.info("Using HF_TOKEN from environment.");
4642
5023
  } else if (stored.hfToken) {
4643
5024
  hfToken = stored.hfToken;
4644
- log2.info(`Reusing saved Hugging Face token (${pc20.dim("~/.codebuddy/global.json")}).`);
5025
+ log2.info(`Reusing saved Hugging Face token (${pc22.dim("~/.codebuddy/global.json")}).`);
4645
5026
  } else if (!interactive) {
4646
5027
  throw new Error(
4647
5028
  "Hugging Face embeddings need a token: set HF_TOKEN (no terminal to prompt for it)."
@@ -4775,7 +5156,7 @@ Namespace: ${pc20.bold(namespace)}`,
4775
5156
  databaseUrl
4776
5157
  });
4777
5158
  log2.success(
4778
- `Registered ${pc20.bold(install.serverKey)} in Claude Desktop. Restart with \u2318Q to activate.`
5159
+ `Registered ${pc22.bold(install.serverKey)} in Claude Desktop. Restart with \u2318Q to activate.`
4779
5160
  );
4780
5161
  } catch (error) {
4781
5162
  log2.warn(`Claude Desktop registration skipped: ${error.message}`);
@@ -4790,7 +5171,7 @@ Namespace: ${pc20.bold(namespace)}`,
4790
5171
  databaseUrl
4791
5172
  });
4792
5173
  log2.success(
4793
- `Registered ${pc20.bold(install.serverKey)} in Codex. Restart Codex to activate.`
5174
+ `Registered ${pc22.bold(install.serverKey)} in Codex. Restart Codex to activate.`
4794
5175
  );
4795
5176
  } catch (error) {
4796
5177
  log2.warn(`Codex registration skipped: ${error.message}`);
@@ -4798,24 +5179,24 @@ Namespace: ${pc20.bold(namespace)}`,
4798
5179
  }
4799
5180
  note2(
4800
5181
  [
4801
- `${pc20.green("\u2713")} CodeBuddy is ready for ${pc20.bold(folderName)}.`,
4802
- `${pc20.green("\u2713")} Scaffolded ${pc20.cyan(".codebuddy/policies.yaml")} for project-specific guardrails.`,
4803
- `${pc20.green("\u2713")} ${graphifyMessage}`,
4804
- `${pc20.green("\u2713")} Added agent workflow instructions to ${pc20.cyan("CLAUDE.md")} and ${pc20.cyan("AGENTS.md")}.`,
5182
+ `${pc22.green("\u2713")} CodeBuddy is ready for ${pc22.bold(folderName)}.`,
5183
+ `${pc22.green("\u2713")} Scaffolded ${pc22.cyan(".codebuddy/policies.yaml")} for project-specific guardrails.`,
5184
+ `${pc22.green("\u2713")} ${graphifyMessage}`,
5185
+ `${pc22.green("\u2713")} Added agent workflow instructions to ${pc22.cyan("CLAUDE.md")} and ${pc22.cyan("AGENTS.md")}.`,
4805
5186
  ``,
4806
5187
  `Next:`,
4807
5188
  ` 1. Restart Claude Desktop and Codex.`,
4808
- ` 2. Ask Claude or Codex to use the ${pc20.cyan("context_pack")} tool for each coding task.`,
5189
+ ` 2. Ask Claude or Codex to use the ${pc22.cyan("context_pack")} tool for each coding task.`,
4809
5190
  ` 3. Indexing, Graphify, Markdown memory import, and embeddings now update automatically while the MCP server is running.`,
4810
- ` 4. Run ${pc20.cyan("codebuddy doctor")} or ${pc20.cyan("codebuddy db doctor")} if you want a full health check.`,
5191
+ ` 4. Run ${pc22.cyan("codebuddy doctor")} or ${pc22.cyan("codebuddy db doctor")} if you want a full health check.`,
4811
5192
  ``,
4812
5193
  `Manage:`,
4813
- ` codebuddy claude list ${pc20.dim("# see every project wired up")}`,
4814
- ` codebuddy codex list ${pc20.dim("# see Codex entries")}`,
4815
- ` codebuddy db test ${pc20.dim("# quick Postgres connectivity check")}`,
5194
+ ` codebuddy claude list ${pc22.dim("# see every project wired up")}`,
5195
+ ` codebuddy codex list ${pc22.dim("# see Codex entries")}`,
5196
+ ` codebuddy db test ${pc22.dim("# quick Postgres connectivity check")}`,
4816
5197
  ` codebuddy claude remove codebuddy-${namespace}`,
4817
5198
  ` codebuddy codex remove codebuddy-${namespace}`,
4818
- ` codebuddy postgres down ${pc20.dim("# stop the DB (data preserved)")}`
5199
+ ` codebuddy postgres down ${pc22.dim("# stop the DB (data preserved)")}`
4819
5200
  ].join("\n"),
4820
5201
  "Done"
4821
5202
  );
@@ -4826,7 +5207,7 @@ Namespace: ${pc20.bold(namespace)}`,
4826
5207
 
4827
5208
  // src/cli/commands/worklog.ts
4828
5209
  import { relative as relative6 } from "path";
4829
- import pc21 from "picocolors";
5210
+ import pc23 from "picocolors";
4830
5211
  function registerWorklogCommands(program, runSafely2) {
4831
5212
  const worklog = program.command("worklog").description("Inspect the cross-chat worklog of completed turns.");
4832
5213
  worklog.command("list", { isDefault: true }).option("--limit <n>", "Maximum entries to show.", "20").option("--json", "Print machine-readable JSON.").description("List worklog entries, newest first.").action(async (opts) => {
@@ -4837,7 +5218,7 @@ function registerWorklogCommands(program, runSafely2) {
4837
5218
  return;
4838
5219
  }
4839
5220
  if (entries.length === 0) {
4840
- console.log(pc21.dim("no worklog entries yet"));
5221
+ console.log(pc23.dim("no worklog entries yet"));
4841
5222
  return;
4842
5223
  }
4843
5224
  for (const entry of entries) console.log(renderRow2(entry));
@@ -4851,36 +5232,36 @@ function registerWorklogCommands(program, runSafely2) {
4851
5232
  worklog.command("latest").description("Show the most recent worklog entry.").action(async () => {
4852
5233
  await runSafely2(async () => {
4853
5234
  const [entry] = await new WorklogStore().list();
4854
- console.log(entry ? renderEntry(entry) : pc21.dim("no worklog entries yet"));
5235
+ console.log(entry ? renderEntry(entry) : pc23.dim("no worklog entries yet"));
4855
5236
  });
4856
5237
  });
4857
5238
  }
4858
5239
  function renderRow2(entry) {
4859
5240
  const summary = entry.summary.replace(/\s+/g, " ");
4860
5241
  const short2 = summary.length <= 70 ? summary : `${summary.slice(0, 69)}\u2026`;
4861
- return `${pc21.dim(entry.createdAt.slice(0, 16))} ${pc21.dim(entry.id)} ${short2} ${pc21.dim(`(${entry.files.length} file(s))`)}`;
5242
+ return `${pc23.dim(entry.createdAt.slice(0, 16))} ${pc23.dim(entry.id)} ${short2} ${pc23.dim(`(${entry.files.length} file(s))`)}`;
4862
5243
  }
4863
5244
  function renderEntry(entry) {
4864
- const lines = [`${pc21.bold(entry.id)} ${pc21.dim(entry.createdAt)} ${pc21.dim(entry.namespace)}`];
4865
- if (entry.sourceSessionId) lines.push(`${pc21.bold("session:")} ${entry.sourceSessionId}`);
4866
- if (entry.sourcePlanId) lines.push(`${pc21.bold("plan:")} ${entry.sourcePlanId}`);
4867
- if (entry.commitSha) lines.push(`${pc21.bold("commit:")} ${entry.commitSha}`);
4868
- if (entry.files.length > 0) lines.push(`${pc21.bold("files:")} ${entry.files.join(", ")}`);
5245
+ const lines = [`${pc23.bold(entry.id)} ${pc23.dim(entry.createdAt)} ${pc23.dim(entry.namespace)}`];
5246
+ if (entry.sourceSessionId) lines.push(`${pc23.bold("session:")} ${entry.sourceSessionId}`);
5247
+ if (entry.sourcePlanId) lines.push(`${pc23.bold("plan:")} ${entry.sourcePlanId}`);
5248
+ if (entry.commitSha) lines.push(`${pc23.bold("commit:")} ${entry.commitSha}`);
5249
+ if (entry.files.length > 0) lines.push(`${pc23.bold("files:")} ${entry.files.join(", ")}`);
4869
5250
  if (entry.verification.length > 0) {
4870
- lines.push(`${pc21.bold("verification:")}`, ...entry.verification.map((line) => ` - ${line}`));
5251
+ lines.push(`${pc23.bold("verification:")}`, ...entry.verification.map((line) => ` - ${line}`));
4871
5252
  }
4872
5253
  if (entry.savedMemory.length > 0) {
4873
- lines.push(`${pc21.bold("saved memory:")} ${entry.savedMemory.join(", ")}`);
5254
+ lines.push(`${pc23.bold("saved memory:")} ${entry.savedMemory.join(", ")}`);
4874
5255
  }
4875
5256
  if (entry.queuedForReview.length > 0) {
4876
- lines.push(`${pc21.bold("queued for review:")} ${entry.queuedForReview.join(", ")}`);
5257
+ lines.push(`${pc23.bold("queued for review:")} ${entry.queuedForReview.join(", ")}`);
4877
5258
  }
4878
- lines.push("", entry.summary, "", pc21.dim(relative6(process.cwd(), entry.path)));
5259
+ lines.push("", entry.summary, "", pc23.dim(relative6(process.cwd(), entry.path)));
4879
5260
  return lines.join("\n");
4880
5261
  }
4881
5262
 
4882
5263
  // src/cli/commands/zvec.ts
4883
- import pc22 from "picocolors";
5264
+ import pc24 from "picocolors";
4884
5265
  function registerZvecCommands(program, runSafely2) {
4885
5266
  const zvec = program.command("zvec").description("Optional zvec-grep semantic file discovery for the context tools.");
4886
5267
  zvec.command("status", { isDefault: true }).option("--json", "Print machine-readable JSON.").description("Show whether zvec-grep is enabled, installed, supported and indexed.").action(async (opts) => {
@@ -4890,22 +5271,22 @@ function registerZvecCommands(program, runSafely2) {
4890
5271
  console.log(JSON.stringify(report, null, 2));
4891
5272
  return;
4892
5273
  }
4893
- const mark = (ok2) => ok2 ? pc22.green("yes") : pc22.yellow("no");
5274
+ const mark = (ok2) => ok2 ? pc24.green("yes") : pc24.yellow("no");
4894
5275
  console.log(`enabled: ${mark(report.enabled)}`);
4895
5276
  console.log(`binary: ${report.binary}`);
4896
5277
  console.log(
4897
- `version: ${report.version ?? pc22.yellow("not found")}${report.version && !report.supported ? pc22.yellow(" (unsupported; need 0.2.x)") : ""}`
5278
+ `version: ${report.version ?? pc24.yellow("not found")}${report.version && !report.supported ? pc24.yellow(" (unsupported; need 0.2.x)") : ""}`
4898
5279
  );
4899
5280
  console.log(`index ready: ${mark(report.indexReady)}`);
4900
5281
  console.log(
4901
- `zg server: ${report.server} ${pc22.dim("(running = faster queries; optional)")}`
5282
+ `zg server: ${report.server} ${pc24.dim("(running = faster queries; optional)")}`
4902
5283
  );
4903
5284
  console.log(`status: ${report.reason}`);
4904
- if (!report.version) console.log(pc22.dim("install: npm install -g @zvec/zvec-grep"));
5285
+ if (!report.version) console.log(pc24.dim("install: npm install -g @zvec/zvec-grep"));
4905
5286
  else if (report.supported && !report.indexReady)
4906
- console.log(pc22.dim("next: codebuddy zvec index"));
5287
+ console.log(pc24.dim("next: codebuddy zvec index"));
4907
5288
  else if (report.indexReady && !report.enabled)
4908
- console.log(pc22.dim("next: codebuddy zvec enable"));
5289
+ console.log(pc24.dim("next: codebuddy zvec enable"));
4909
5290
  });
4910
5291
  });
4911
5292
  zvec.command("index").option("--model <model>", "Embedding model for a new index.", DEFAULT_ZVEC_MODEL).option("--rebuild", "Rebuild the index from scratch (e.g. after changing model).").description("Build or refresh the zg index for this repository (explicit; never automatic).").action(async (opts) => {
@@ -4915,21 +5296,21 @@ function registerZvecCommands(program, runSafely2) {
4915
5296
  ...opts.rebuild ? { rebuild: true } : {}
4916
5297
  });
4917
5298
  if (code !== 0) throw new Error(`zg index exited with code ${code}`);
4918
- console.log(pc22.dim("next: codebuddy zvec enable (.zvec-grep/ ignores itself in git)"));
5299
+ console.log(pc24.dim("next: codebuddy zvec enable (.zvec-grep/ ignores itself in git)"));
4919
5300
  });
4920
5301
  });
4921
5302
  zvec.command("enable").description("Use zvec-grep candidates in context_pack / context_before_edit.").action(async () => {
4922
5303
  await runSafely2(async () => {
4923
5304
  await setZvecEnabled(true);
4924
5305
  console.log(
4925
- `${pc22.green("enabled")} zvec-grep retrieval (falls back to keywords if unavailable)`
5306
+ `${pc24.green("enabled")} zvec-grep retrieval (falls back to keywords if unavailable)`
4926
5307
  );
4927
5308
  });
4928
5309
  });
4929
5310
  zvec.command("disable").description("Stop using zvec-grep; keyword ranking only.").action(async () => {
4930
5311
  await runSafely2(async () => {
4931
5312
  await setZvecEnabled(false);
4932
- console.log(`${pc22.yellow("disabled")} zvec-grep retrieval`);
5313
+ console.log(`${pc24.yellow("disabled")} zvec-grep retrieval`);
4933
5314
  });
4934
5315
  });
4935
5316
  }
@@ -5202,6 +5583,36 @@ function createCli() {
5202
5583
  console.log(JSON.stringify(await runtime.repository.getStats(), null, 2));
5203
5584
  });
5204
5585
  });
5586
+ program.command("storage").option("--json", "Print machine-readable JSON.").description(
5587
+ "Memory storage health: Markdown/database drift, backup coverage, embedding backlog and spaces, capture lag."
5588
+ ).action(async (opts) => {
5589
+ await runSafely(async () => {
5590
+ const root = process.cwd();
5591
+ const capture2 = {
5592
+ stagedNotCaptured: await new HookStagingStore(root).pending(),
5593
+ journalBehind: (await journalLag(root)).filter((entry) => entry.behindBytes !== 0)
5594
+ };
5595
+ let database;
5596
+ try {
5597
+ const runtime = await createRuntime(await loadRuntimeConfig());
5598
+ try {
5599
+ database = await runtime.memory.storageReport();
5600
+ } finally {
5601
+ await runtime.close();
5602
+ }
5603
+ } catch (error) {
5604
+ database = { unavailable: error.message };
5605
+ }
5606
+ if (opts.json) {
5607
+ console.log(JSON.stringify({ database, capture: capture2 }, null, 2));
5608
+ return;
5609
+ }
5610
+ printStorage(database, capture2);
5611
+ if ("unavailable" in database || database.markdownOnly.length + database.dbOnly.length > 0) {
5612
+ process.exitCode = 1;
5613
+ }
5614
+ });
5615
+ });
5205
5616
  program.command("doctor").description("Check CodeBuddy environment health.").option("--skip-model-check", "Do not call Hugging Face models.").action(async (opts) => {
5206
5617
  await runSafely(async () => {
5207
5618
  const report = await runDoctor({ skipModelCheck: opts.skipModelCheck ?? true });
@@ -5228,6 +5639,8 @@ function createCli() {
5228
5639
  registerLearnCommand(program, runSafely);
5229
5640
  registerMemoryCommands(program, runSafely);
5230
5641
  registerWorklogCommands(program, runSafely);
5642
+ registerJournalCommands(program, runSafely);
5643
+ registerOutputCommands(program, runSafely);
5231
5644
  registerZvecCommands(program, runSafely);
5232
5645
  registerLlmCommands(program, runSafely);
5233
5646
  registerSuggestCommand(program, runSafely);
@@ -5255,7 +5668,20 @@ var HELP_GROUPS = [
5255
5668
  ["Get started:", ["use", "init", "doctor", "hooks", "clients", "models", "dashboard"]],
5256
5669
  [
5257
5670
  "Daily work:",
5258
- ["context", "plan", "change", "risk", "suggest", "memory", "worklog", "map", "symbols"]
5671
+ [
5672
+ "context",
5673
+ "plan",
5674
+ "change",
5675
+ "risk",
5676
+ "suggest",
5677
+ "memory",
5678
+ "worklog",
5679
+ "journal",
5680
+ "run",
5681
+ "output",
5682
+ "map",
5683
+ "symbols"
5684
+ ]
5259
5685
  ],
5260
5686
  ["Indexing and search:", ["index", "watch", "reindex", "zvec", "graphify", "llm"]],
5261
5687
  [
@@ -5265,6 +5691,7 @@ var HELP_GROUPS = [
5265
5691
  "db",
5266
5692
  "migrate",
5267
5693
  "stats",
5694
+ "storage",
5268
5695
  "inspect",
5269
5696
  "namespaces",
5270
5697
  "list-facts",
@@ -5315,7 +5742,7 @@ async function runSafely(fn) {
5315
5742
  try {
5316
5743
  await fn();
5317
5744
  } catch (error) {
5318
- console.error(pc23.red(String(redactSecrets(error.message))));
5745
+ console.error(pc25.red(String(redactSecrets(error.message))));
5319
5746
  process.exitCode = 1;
5320
5747
  }
5321
5748
  }
@@ -5329,9 +5756,9 @@ function parseDuration(value) {
5329
5756
  return amount * 60 * 1e3;
5330
5757
  }
5331
5758
  function printDoctor(report) {
5332
- const ok2 = pc23.green("ok");
5333
- const bad = pc23.red("fail");
5334
- const warn2 = pc23.yellow("warn");
5759
+ const ok2 = pc25.green("ok");
5760
+ const bad = pc25.red("fail");
5761
+ const warn2 = pc25.yellow("warn");
5335
5762
  console.log(`scaffold root: ${report.scaffold.codebuddyDir.ok ? ok2 : bad}`);
5336
5763
  if (report.scaffold.codebuddyDir.warning)
5337
5764
  console.log(` ${report.scaffold.codebuddyDir.warning}`);
@@ -5357,7 +5784,7 @@ function printDoctor(report) {
5357
5784
  );
5358
5785
  console.log(`estimated daily cap remaining: ${report.usage.estimatedDailyCapRemaining}`);
5359
5786
  if (report.usage.warning80Percent)
5360
- console.log(pc23.yellow("usage warning: daily cap is over 80% used"));
5787
+ console.log(pc25.yellow("usage warning: daily cap is over 80% used"));
5361
5788
  console.log(
5362
5789
  `config permissions: ${report.config.exists ? report.config.mode : "missing"} ${report.config.ok ? ok2 : warn2}`
5363
5790
  );
@@ -5367,9 +5794,9 @@ function printDoctor(report) {
5367
5794
  }
5368
5795
  }
5369
5796
  function printDbDoctor(report) {
5370
- const ok2 = pc23.green("ok");
5371
- const bad = pc23.red("fail");
5372
- const warn2 = pc23.yellow("warn");
5797
+ const ok2 = pc25.green("ok");
5798
+ const bad = pc25.red("fail");
5799
+ const warn2 = pc25.yellow("warn");
5373
5800
  console.log(`DB connectivity: ${report.db.ok ? ok2 : bad}`);
5374
5801
  if (report.db.postgresUrlSource) console.log(` url source: ${report.db.postgresUrlSource}`);
5375
5802
  if (report.db.error) console.log(` ${report.db.error}`);
@@ -5382,7 +5809,42 @@ function printDbDoctor(report) {
5382
5809
  }
5383
5810
  if (report.namespace.warning) console.log(` ${report.namespace.warning}`);
5384
5811
  }
5812
+ function printStorage(database, capture2) {
5813
+ if ("unavailable" in database) {
5814
+ console.log(pc25.yellow(`database: unavailable (${database.unavailable})`));
5815
+ } else {
5816
+ const drift = database.markdownOnly.length + database.dbOnly.length;
5817
+ console.log(
5818
+ `facts: ${database.markdownFacts} Markdown, ${database.databaseFacts} database${database.invalidFiles > 0 ? `, ${database.invalidFiles} unreadable file(s)` : ""}`
5819
+ );
5820
+ console.log(
5821
+ drift === 0 ? pc25.green("sync: Markdown and database agree") : pc25.yellow(
5822
+ `sync: ${database.markdownOnly.length} file(s) not in the database, ${database.dbOnly.length} row(s) without a file (run \`codebuddy reindex\`)`
5823
+ )
5824
+ );
5825
+ if (database.unbacked.length > 0) {
5826
+ console.log(
5827
+ pc25.yellow(
5828
+ `backup: ${database.unbacked.length} database-only fact(s) have no Markdown copy`
5829
+ )
5830
+ );
5831
+ }
5832
+ const e = database.embeddings;
5833
+ console.log(
5834
+ `embeddings: ${e.ready} ready, ${e.pending} pending, ${e.processing} processing, ${e.failed} failed${database.oldestPendingAt ? ` (oldest pending ${database.oldestPendingAt})` : ""}`
5835
+ );
5836
+ for (const space of database.spaces) {
5837
+ console.log(` space ${space.model} ${space.space ?? "(legacy)"}: ${space.count}`);
5838
+ }
5839
+ if (database.spaces.length > 1) {
5840
+ console.log(pc25.yellow(" more than one embedding space: run `codebuddy reindex --respace`"));
5841
+ }
5842
+ }
5843
+ console.log(
5844
+ capture2.stagedNotCaptured.length === 0 && capture2.journalBehind.length === 0 ? pc25.green("capture: nothing pending") : `capture: ${capture2.stagedNotCaptured.length} session(s) with uncaptured edits, ${capture2.journalBehind.length} journal(s) behind their transcript`
5845
+ );
5846
+ }
5385
5847
  export {
5386
5848
  createCli
5387
5849
  };
5388
- //# sourceMappingURL=main-YJKSYFPT.js.map
5850
+ //# sourceMappingURL=main-SFC5LV4E.js.map