hippo-memory 1.61.0 → 1.63.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 (377) hide show
  1. package/README.md +37 -53
  2. package/dist/agent-memories/apply.d.ts +3 -1
  3. package/dist/agent-memories/apply.js +23 -1
  4. package/dist/agent-memories/claude-code.d.ts +4 -1
  5. package/dist/agent-memories/claude-code.js +53 -9
  6. package/dist/agent-memories/report.d.ts +1 -0
  7. package/dist/agent-memories/report.js +2 -2
  8. package/dist/agent-memories/sync.d.ts +3 -3
  9. package/dist/agent-memories/sync.js +46 -24
  10. package/dist/agent-memories/types.d.ts +0 -2
  11. package/dist/ambient-store.d.ts +4 -4
  12. package/dist/ambient-store.js +4 -3
  13. package/dist/api/assemble.d.ts +7 -10
  14. package/dist/api/assemble.js +62 -66
  15. package/dist/api/audit.d.ts +2 -2
  16. package/dist/api/audit.js +2 -2
  17. package/dist/api/auth.d.ts +6 -6
  18. package/dist/api/auth.js +5 -7
  19. package/dist/api/context-select.d.ts +50 -0
  20. package/dist/api/context-select.js +342 -0
  21. package/dist/api/context-types.d.ts +12 -17
  22. package/dist/api/context.d.ts +5 -4
  23. package/dist/api/context.js +208 -535
  24. package/dist/api/dormant.js +1 -1
  25. package/dist/api/drill-down.d.ts +4 -4
  26. package/dist/api/drill-down.js +56 -41
  27. package/dist/api/outcome.d.ts +8 -13
  28. package/dist/api/outcome.js +13 -17
  29. package/dist/api/promote.d.ts +5 -8
  30. package/dist/api/promote.js +55 -66
  31. package/dist/api/quarantine.js +3 -2
  32. package/dist/api/recall-types.d.ts +43 -55
  33. package/dist/api/recall.d.ts +3 -3
  34. package/dist/api/recall.js +310 -456
  35. package/dist/api/remember.d.ts +2 -2
  36. package/dist/api/sleep.d.ts +7 -35
  37. package/dist/api/sleep.js +206 -220
  38. package/dist/api/tokens.d.ts +2 -2
  39. package/dist/api/tokens.js +2 -2
  40. package/dist/api/types.d.ts +6 -10
  41. package/dist/api/types.js +2 -4
  42. package/dist/audit-prune.d.ts +4 -6
  43. package/dist/audit-prune.js +3 -5
  44. package/dist/audit.js +11 -33
  45. package/dist/auth.d.ts +8 -9
  46. package/dist/auth.js +5 -7
  47. package/dist/autolearn.d.ts +1 -1
  48. package/dist/autolearn.js +1 -1
  49. package/dist/availability.js +3 -5
  50. package/dist/capture/command.d.ts +5 -4
  51. package/dist/capture/command.js +8 -22
  52. package/dist/capture/compact.d.ts +2 -2
  53. package/dist/capture/compact.js +11 -15
  54. package/dist/capture/extract.js +37 -115
  55. package/dist/capture-error.d.ts +1 -1
  56. package/dist/capture-error.js +1 -1
  57. package/dist/churn-git.d.ts +1 -1
  58. package/dist/churn-git.js +1 -1
  59. package/dist/cli/audit.js +3 -4
  60. package/dist/cli/auth.js +3 -6
  61. package/dist/cli/briefs.js +324 -306
  62. package/dist/cli/context.js +44 -34
  63. package/dist/cli/continuity.js +283 -271
  64. package/dist/cli/curate.d.ts +1 -1
  65. package/dist/cli/curate.js +43 -58
  66. package/dist/cli/dag.js +5 -9
  67. package/dist/cli/decisions.js +334 -345
  68. package/dist/cli/explain.js +68 -61
  69. package/dist/cli/goals.js +1 -1
  70. package/dist/cli/init.js +1 -1
  71. package/dist/cli/maintenance.js +62 -51
  72. package/dist/cli/playbooks.js +391 -379
  73. package/dist/cli/projects.js +11 -6
  74. package/dist/cli/recall.js +30 -44
  75. package/dist/cli/remember.js +118 -87
  76. package/dist/cli/session-hooks.js +106 -115
  77. package/dist/cli/setup.d.ts +1 -1
  78. package/dist/cli/setup.js +267 -250
  79. package/dist/cli/shared.js +3 -3
  80. package/dist/cli/slack.js +1 -1
  81. package/dist/cli/sleep.js +27 -1
  82. package/dist/cli/status.d.ts +4 -4
  83. package/dist/cli/status.js +80 -76
  84. package/dist/cli/transfer.js +88 -107
  85. package/dist/cli/usage.js +9 -6
  86. package/dist/cli.d.ts +1 -1
  87. package/dist/cli.js +4 -9
  88. package/dist/compaction-record.d.ts +2 -2
  89. package/dist/compaction-record.js +89 -68
  90. package/dist/compare.d.ts +11 -16
  91. package/dist/compare.js +11 -16
  92. package/dist/config.d.ts +19 -18
  93. package/dist/config.js +90 -67
  94. package/dist/connectors/github/backfill.d.ts +2 -2
  95. package/dist/connectors/github/backfill.js +8 -15
  96. package/dist/connectors/github/cli-impl.js +3 -8
  97. package/dist/connectors/github/deletion.d.ts +5 -12
  98. package/dist/connectors/github/deletion.js +5 -12
  99. package/dist/connectors/github/dlq.d.ts +6 -9
  100. package/dist/connectors/github/dlq.js +2 -3
  101. package/dist/connectors/github/ingest.d.ts +5 -7
  102. package/dist/connectors/github/ingest.js +8 -12
  103. package/dist/connectors/github/octokit-client.d.ts +3 -5
  104. package/dist/connectors/github/octokit-client.js +5 -6
  105. package/dist/connectors/github/signature.d.ts +9 -39
  106. package/dist/connectors/github/signature.js +9 -39
  107. package/dist/connectors/github/tenant-routing.d.ts +1 -1
  108. package/dist/connectors/github/tenant-routing.js +1 -1
  109. package/dist/connectors/github/transform.js +2 -2
  110. package/dist/connectors/github/types.d.ts +2 -10
  111. package/dist/connectors/github/types.js +1 -3
  112. package/dist/connectors/slack/deletion.d.ts +3 -8
  113. package/dist/connectors/slack/deletion.js +3 -8
  114. package/dist/connectors/slack/dlq.d.ts +1 -1
  115. package/dist/connectors/slack/ingest.d.ts +1 -1
  116. package/dist/connectors/slack/ingest.js +7 -16
  117. package/dist/connectors/slack/signature.d.ts +1 -1
  118. package/dist/connectors/slack/tenant-routing.d.ts +3 -5
  119. package/dist/connectors/slack/tenant-routing.js +3 -5
  120. package/dist/connectors/slack/transform.d.ts +5 -6
  121. package/dist/connectors/slack/transform.js +5 -6
  122. package/dist/connectors/slack/types.d.ts +2 -6
  123. package/dist/connectors/slack/types.js +1 -3
  124. package/dist/connectors/slack/web-client.js +10 -3
  125. package/dist/connectors/slack/workspaces.d.ts +3 -5
  126. package/dist/connectors/slack/workspaces.js +3 -5
  127. package/dist/consolidate/conflicts.js +3 -14
  128. package/dist/consolidate/decay.js +9 -29
  129. package/dist/consolidate/llm-passes.js +4 -5
  130. package/dist/consolidate/merge.js +8 -23
  131. package/dist/consolidate/run.d.ts +1 -8
  132. package/dist/consolidate/run.js +3 -25
  133. package/dist/consolidate/sleep.js +5 -17
  134. package/dist/consolidate/traces.js +9 -21
  135. package/dist/customer-notes.d.ts +5 -7
  136. package/dist/customer-notes.js +82 -76
  137. package/dist/dag.d.ts +10 -21
  138. package/dist/dag.js +189 -203
  139. package/dist/db/continuity.js +2 -2
  140. package/dist/db/migrations/v14.js +1 -1
  141. package/dist/db/migrations/v15.js +1 -2
  142. package/dist/db/migrations/v16.js +3 -4
  143. package/dist/db/migrations/v17.js +2 -3
  144. package/dist/db/migrations/v19.js +1 -1
  145. package/dist/db/migrations/v20.js +1 -1
  146. package/dist/db/migrations/v21.js +2 -6
  147. package/dist/db/migrations/v22.js +2 -4
  148. package/dist/db/migrations/v23.js +1 -1
  149. package/dist/db/migrations/v24.js +4 -6
  150. package/dist/db/migrations/v25.js +2 -3
  151. package/dist/db/migrations/v26.js +3 -3
  152. package/dist/db/migrations/v27.js +2 -10
  153. package/dist/db/migrations/v28.js +5 -8
  154. package/dist/db/migrations/v29.js +3 -4
  155. package/dist/db/migrations/v30.js +2 -2
  156. package/dist/db/migrations/v31.js +1 -1
  157. package/dist/db/migrations/v32.js +1 -1
  158. package/dist/db/migrations/v33.js +3 -3
  159. package/dist/db/migrations/v34.js +1 -1
  160. package/dist/db/migrations/v35.js +3 -4
  161. package/dist/db/migrations/v36.js +3 -4
  162. package/dist/db/migrations/v37.js +5 -5
  163. package/dist/db/migrations/v38.js +7 -8
  164. package/dist/db/migrations/v39.js +1 -1
  165. package/dist/db/migrations/v40.js +4 -16
  166. package/dist/db/migrations/v41.js +3 -4
  167. package/dist/db/migrations/v42.js +3 -4
  168. package/dist/db/migrations/v45.js +1 -1
  169. package/dist/db/migrations/v46.js +1 -1
  170. package/dist/db/migrations/v47.js +1 -1
  171. package/dist/db/migrations/v48.js +1 -1
  172. package/dist/decisions.d.ts +2 -2
  173. package/dist/decisions.js +97 -80
  174. package/dist/dedupe.js +86 -61
  175. package/dist/delivery-recorder.js +154 -135
  176. package/dist/doctor.js +129 -110
  177. package/dist/dormant.js +1 -4
  178. package/dist/embedding-provider.d.ts +4 -8
  179. package/dist/embedding-provider.js +4 -8
  180. package/dist/embeddings.js +55 -47
  181. package/dist/env.d.ts +1 -1
  182. package/dist/env.js +12 -12
  183. package/dist/escape.d.ts +5 -0
  184. package/dist/escape.js +10 -0
  185. package/dist/eval-stats.d.ts +1 -2
  186. package/dist/eval-stats.js +1 -2
  187. package/dist/eval-suite.js +27 -21
  188. package/dist/extract.js +4 -9
  189. package/dist/failure-log.d.ts +3 -3
  190. package/dist/failure-log.js +1 -1
  191. package/dist/forward-claim-detector.d.ts +2 -4
  192. package/dist/forward-claim-detector.js +6 -11
  193. package/dist/goals.d.ts +3 -3
  194. package/dist/goals.js +103 -91
  195. package/dist/graph/read.d.ts +2 -2
  196. package/dist/graph/read.js +5 -6
  197. package/dist/graph/types.d.ts +8 -8
  198. package/dist/graph/write.d.ts +7 -14
  199. package/dist/graph/write.js +16 -23
  200. package/dist/graph-extract.d.ts +7 -8
  201. package/dist/graph-extract.js +62 -72
  202. package/dist/graph-recall.d.ts +2 -2
  203. package/dist/graph-recall.js +55 -49
  204. package/dist/graph-stream.d.ts +5 -6
  205. package/dist/graph-stream.js +66 -57
  206. package/dist/graph-view.d.ts +2 -2
  207. package/dist/graph-view.js +7 -7
  208. package/dist/half-life-migration.d.ts +1 -2
  209. package/dist/half-life-migration.js +2 -3
  210. package/dist/hooks/codex-session.js +1 -1
  211. package/dist/hooks/codex-wrapper.d.ts +1 -1
  212. package/dist/hooks/codex-wrapper.js +3 -2
  213. package/dist/hooks/json-hooks.d.ts +2 -2
  214. package/dist/hooks/json-hooks.js +5 -4
  215. package/dist/hooks/opencode.d.ts +1 -1
  216. package/dist/hooks/opencode.js +5 -4
  217. package/dist/hooks/shared.d.ts +3 -7
  218. package/dist/hooks/shared.js +1 -8
  219. package/dist/http-util.d.ts +2 -3
  220. package/dist/http-util.js +3 -0
  221. package/dist/importers/core.d.ts +2 -9
  222. package/dist/importers/core.js +15 -30
  223. package/dist/importers/sources.js +2 -1
  224. package/dist/importers/vault.js +2 -20
  225. package/dist/incidents.d.ts +1 -1
  226. package/dist/incidents.js +46 -39
  227. package/dist/instruction-detect.d.ts +1 -1
  228. package/dist/instruction-detect.js +1 -1
  229. package/dist/invalidation.d.ts +3 -0
  230. package/dist/invalidation.js +160 -114
  231. package/dist/json.d.ts +5 -0
  232. package/dist/json.js +4 -0
  233. package/dist/judgment.js +1 -2
  234. package/dist/local-embedding.js +1 -1
  235. package/dist/mcp/admin-tools.js +7 -17
  236. package/dist/mcp/format.js +1 -1
  237. package/dist/mcp/framing.js +3 -6
  238. package/dist/mcp/protocol.d.ts +2 -5
  239. package/dist/mcp/protocol.js +1 -3
  240. package/dist/mcp/recall-tools.js +12 -15
  241. package/dist/mcp/request.js +4 -3
  242. package/dist/mcp/session-state.js +2 -3
  243. package/dist/mcp/stdio.js +2 -1
  244. package/dist/mcp/tools.js +9 -6
  245. package/dist/memory-value-weights.d.ts +5 -8
  246. package/dist/memory-value-weights.js +5 -8
  247. package/dist/memory-value.d.ts +13 -13
  248. package/dist/memory-value.js +26 -37
  249. package/dist/memory.d.ts +20 -22
  250. package/dist/memory.js +24 -48
  251. package/dist/multihop.d.ts +1 -1
  252. package/dist/multihop.js +3 -2
  253. package/dist/owner-validation.d.ts +4 -5
  254. package/dist/owner-validation.js +4 -5
  255. package/dist/physics.d.ts +4 -4
  256. package/dist/physics.js +7 -9
  257. package/dist/policies.d.ts +9 -10
  258. package/dist/policies.js +96 -81
  259. package/dist/postinstall.js +3 -6
  260. package/dist/predictions/planning-fallacy.d.ts +9 -14
  261. package/dist/predictions/planning-fallacy.js +10 -16
  262. package/dist/predictions/store.d.ts +15 -23
  263. package/dist/predictions/store.js +36 -33
  264. package/dist/processes.d.ts +2 -7
  265. package/dist/processes.js +88 -72
  266. package/dist/project-briefs.d.ts +2 -3
  267. package/dist/project-briefs.js +141 -118
  268. package/dist/project-identity.d.ts +22 -9
  269. package/dist/project-identity.js +47 -12
  270. package/dist/project-merge.d.ts +28 -5
  271. package/dist/project-merge.js +213 -46
  272. package/dist/project-remote.d.ts +12 -0
  273. package/dist/project-remote.js +138 -0
  274. package/dist/prompt-recall.js +1 -2
  275. package/dist/rate-limit.d.ts +1 -1
  276. package/dist/rate-limit.js +1 -1
  277. package/dist/raw-archive.d.ts +9 -0
  278. package/dist/raw-archive.js +70 -53
  279. package/dist/recall-history.d.ts +19 -20
  280. package/dist/recall-history.js +24 -42
  281. package/dist/recall-pipeline.js +4 -28
  282. package/dist/recall-scope.d.ts +7 -8
  283. package/dist/recall-scope.js +7 -8
  284. package/dist/recall-trace.d.ts +5 -9
  285. package/dist/recall-trace.js +6 -10
  286. package/dist/refine-llm.d.ts +1 -1
  287. package/dist/refine-llm.js +2 -2
  288. package/dist/reject-flow.d.ts +3 -4
  289. package/dist/reject-flow.js +122 -117
  290. package/dist/rejection.d.ts +5 -6
  291. package/dist/rejection.js +7 -15
  292. package/dist/rerankers/clef.d.ts +1 -1
  293. package/dist/rerankers/jev.d.ts +1 -2
  294. package/dist/rerankers/jev.js +4 -5
  295. package/dist/rerankers/llm.d.ts +1 -2
  296. package/dist/rerankers/llm.js +1 -2
  297. package/dist/rerankers/types.d.ts +1 -2
  298. package/dist/rrf.d.ts +2 -2
  299. package/dist/rrf.js +2 -2
  300. package/dist/search/bm25-search.d.ts +1 -1
  301. package/dist/search/bm25-search.js +2 -1
  302. package/dist/search/boosts.js +2 -1
  303. package/dist/search/hybrid.d.ts +1 -1
  304. package/dist/search/hybrid.js +2 -1
  305. package/dist/search/physics-search.d.ts +1 -1
  306. package/dist/search/physics-search.js +2 -1
  307. package/dist/search/types.d.ts +2 -0
  308. package/dist/search/types.js +3 -1
  309. package/dist/secret-detect.d.ts +4 -5
  310. package/dist/secret-detect.js +6 -10
  311. package/dist/server/auth.js +5 -5
  312. package/dist/server/client-ip.js +1 -1
  313. package/dist/server/cursor.js +2 -1
  314. package/dist/server/mcp-http.js +4 -4
  315. package/dist/server/request.d.ts +3 -6
  316. package/dist/server/request.js +6 -7
  317. package/dist/server/routes/admin.js +5 -4
  318. package/dist/server/routes/customer-notes.js +6 -5
  319. package/dist/server/routes/decisions.js +4 -3
  320. package/dist/server/routes/incidents.js +7 -5
  321. package/dist/server/routes/memories.js +7 -7
  322. package/dist/server/routes/policies.js +3 -2
  323. package/dist/server/routes/predictions.js +12 -15
  324. package/dist/server/routes/processes.js +3 -2
  325. package/dist/server/routes/project-briefs.js +8 -7
  326. package/dist/server/routes/recall.js +95 -93
  327. package/dist/server/routes/skills.js +6 -5
  328. package/dist/server/types.d.ts +1 -1
  329. package/dist/server/validation.d.ts +1 -2
  330. package/dist/server/validation.js +7 -14
  331. package/dist/server-detect.js +72 -58
  332. package/dist/server.d.ts +2 -2
  333. package/dist/server.js +131 -117
  334. package/dist/shared.d.ts +26 -17
  335. package/dist/shared.js +102 -104
  336. package/dist/skills.d.ts +3 -3
  337. package/dist/skills.js +88 -72
  338. package/dist/store/audit-event.d.ts +2 -2
  339. package/dist/store/audit-event.js +1 -1
  340. package/dist/store/candidates.d.ts +2 -2
  341. package/dist/store/candidates.js +4 -3
  342. package/dist/store/conflicts.js +30 -22
  343. package/dist/store/delete-and-batch.d.ts +11 -14
  344. package/dist/store/delete-and-batch.js +40 -91
  345. package/dist/store/entry-reads.d.ts +17 -25
  346. package/dist/store/entry-reads.js +59 -40
  347. package/dist/store/entry-row.d.ts +6 -24
  348. package/dist/store/entry-row.js +6 -24
  349. package/dist/store/entry-writes.d.ts +6 -7
  350. package/dist/store/entry-writes.js +13 -11
  351. package/dist/store/handoffs.d.ts +1 -1
  352. package/dist/store/handoffs.js +7 -10
  353. package/dist/store/index-and-stats.d.ts +2 -6
  354. package/dist/store/index-and-stats.js +4 -10
  355. package/dist/store/mirrors.d.ts +6 -19
  356. package/dist/store/mirrors.js +14 -39
  357. package/dist/store/open.js +9 -31
  358. package/dist/store/rows.d.ts +5 -11
  359. package/dist/store/rows.js +6 -11
  360. package/dist/store/search-rows.d.ts +17 -34
  361. package/dist/store/search-rows.js +34 -56
  362. package/dist/store/sessions.d.ts +4 -5
  363. package/dist/store/sessions.js +5 -6
  364. package/dist/store/summaries.d.ts +13 -17
  365. package/dist/store/summaries.js +26 -70
  366. package/dist/support-bundle.js +4 -8
  367. package/dist/tenant.d.ts +1 -5
  368. package/dist/token-ledger.d.ts +1 -1
  369. package/dist/token-ledger.js +3 -5
  370. package/dist/trace.js +1 -3
  371. package/dist/version.d.ts +1 -1
  372. package/dist/version.js +1 -1
  373. package/dist/working-memory.d.ts +1 -1
  374. package/extensions/openclaw-plugin/openclaw.plugin.json +1 -1
  375. package/extensions/openclaw-plugin/package.json +1 -1
  376. package/openclaw.plugin.json +1 -1
  377. package/package.json +1 -1
@@ -6,11 +6,8 @@ import * as skillsModule from '../skills.js';
6
6
  import { resolveTenantId } from '../tenant.js';
7
7
  import { printError } from './output.js';
8
8
  import { requireInit } from './shared.js';
9
- // Strict positive-integer id parse for the mutating process subcommands.
10
- // parseInt alone accepts trailing junk ('1abc' -> 1), which would let
11
- // `process close 1abc` / `supersede 1abc` silently hit the wrong row; require
12
- // the whole arg to be digits. (Mirrors parsePositiveIncidentId; codex P2,
13
- // 2026-05-29.)
9
+ // parseInt alone accepts trailing junk ('1abc' -> 1), which would let `process close 1abc`
10
+ // silently hit the wrong row; require the whole arg to be digits.
14
11
  function parsePositiveProcessId(idRaw) {
15
12
  const s = String(idRaw ?? '').trim();
16
13
  const id = parseInt(s, 10);
@@ -33,135 +30,144 @@ function collectProcessSteps(stepRaw) {
33
30
  }
34
31
  return [];
35
32
  }
36
- export function cmdProcess(hippoRoot, args, flags) {
37
- requireInit(hippoRoot);
38
- const tenantId = resolveTenantId({});
39
- const subcommand = args[0] ?? '';
40
- if (subcommand === 'list') {
41
- const statusRaw = flags['status'];
42
- const status = typeof statusRaw === 'string' ? statusRaw.trim() : 'all';
43
- const limitRaw = flags['limit'];
44
- const limit = limitRaw !== undefined ? parseInt(String(limitRaw), 10) : 100;
45
- if (!Number.isFinite(limit) || limit <= 0) {
46
- printError(`Invalid --limit: "${limitRaw}". Must be a positive integer.`);
47
- process.exit(1);
48
- }
49
- let results;
50
- if (status === 'all') {
51
- results = processesModule.loadProcesses(hippoRoot, tenantId, { limit });
52
- }
53
- else {
54
- if (!processesModule.VALID_PROCESS_STATES.has(status)) {
55
- printError(`Invalid --status: "${status}". Must be one of: active | superseded | closed | all.`);
56
- process.exit(1);
57
- }
58
- results = processesModule.loadProcesses(hippoRoot, tenantId, {
59
- status: status,
60
- limit,
61
- });
62
- }
63
- if (results.length === 0) {
64
- console.log('No processes.');
65
- return;
66
- }
67
- console.log(`Found ${results.length} processes:\n`);
68
- for (const proc of results) {
69
- console.log(`#${proc.id} [${proc.status}] v${proc.version} steps=${proc.steps.length} memory=${proc.memoryId ?? '-'}`);
70
- console.log(` ${proc.processName}`);
71
- if (proc.changeSummary)
72
- console.log(` change: ${proc.changeSummary}`);
73
- }
74
- return;
33
+ function parseListLimit(flags) {
34
+ const limitRaw = flags['limit'];
35
+ const limit = limitRaw !== undefined ? parseInt(String(limitRaw), 10) : 100;
36
+ if (!Number.isFinite(limit) || limit <= 0) {
37
+ printError(`Invalid --limit: "${limitRaw}". Must be a positive integer.`);
38
+ process.exit(1);
75
39
  }
76
- if (subcommand === 'get') {
77
- const idRaw = args[1];
78
- if (!idRaw) {
79
- printError('Usage: hippo process get <id>');
80
- process.exit(1);
81
- }
82
- const id = parsePositiveProcessId(idRaw);
83
- const proc = processesModule.loadProcessById(hippoRoot, tenantId, id);
84
- if (!proc) {
85
- printError(`Process ${id} not found.`);
86
- process.exit(1);
87
- }
88
- console.log(`Process #${proc.id}`);
89
- console.log(` name: ${proc.processName}`);
90
- console.log(` status: ${proc.status}`);
91
- console.log(` version: ${proc.version}`);
92
- if (proc.description)
93
- console.log(` description: ${proc.description}`);
94
- if (proc.steps.length > 0) {
95
- console.log(` steps:`);
96
- proc.steps.forEach((s, i) => console.log(` ${i + 1}. ${s}`));
97
- }
98
- if (proc.changeSummary)
99
- console.log(` change_summary: ${proc.changeSummary}`);
100
- if (proc.supersededBy !== null)
101
- console.log(` superseded_by: #${proc.supersededBy}`);
102
- if (proc.supersededAt)
103
- console.log(` superseded_at: ${proc.supersededAt}`);
104
- if (proc.closedAt)
105
- console.log(` closed_at: ${proc.closedAt}`);
106
- if (proc.memoryId)
107
- console.log(` memory: ${proc.memoryId}`);
108
- console.log(` created: ${proc.createdAt}`);
109
- return;
40
+ return limit;
41
+ }
42
+ function processList(hippoRoot, tenantId, flags) {
43
+ const statusRaw = flags['status'];
44
+ const status = typeof statusRaw === 'string' ? statusRaw.trim() : 'all';
45
+ const limit = parseListLimit(flags);
46
+ let results;
47
+ if (status === 'all') {
48
+ results = processesModule.loadProcesses(hippoRoot, tenantId, { limit });
110
49
  }
111
- if (subcommand === 'supersede') {
112
- const idRaw = args[1];
113
- if (!idRaw) {
114
- printError('Usage: hippo process supersede <id> --step "<text>" [--step ...] [--change "<summary>"] [--description "<text>"]');
50
+ else {
51
+ if (!processesModule.VALID_PROCESS_STATES.has(status)) {
52
+ printError(`Invalid --status: "${status}". Must be one of: active | superseded | closed | all.`);
115
53
  process.exit(1);
116
54
  }
117
- const id = parsePositiveProcessId(idRaw);
118
- const steps = collectProcessSteps(flags['step']);
119
- if (steps.length === 0) {
120
- printError('hippo process supersede requires at least one --step "<text>" for the new version.');
121
- process.exit(1);
122
- }
123
- // A supersession is a new version of the SAME process, so the new row reuses
124
- // the predecessor's name (stable identity across versions). loadProcessById
125
- // gives an early not-found before the write; saveProcess's in-SAVEPOINT
126
- // preflight is the authoritative active-state check.
127
- const existing = processesModule.loadProcessById(hippoRoot, tenantId, id);
128
- if (!existing) {
129
- printError(`Process ${id} not found.`);
130
- process.exit(1);
131
- }
132
- const changeRaw = flags['change'];
133
- const changeSummary = typeof changeRaw === 'string' && changeRaw ? changeRaw : undefined;
134
- const descRaw = flags['description'];
135
- const description = typeof descRaw === 'string' && descRaw ? descRaw : undefined;
136
- const procPathTags = extractPathTags(process.cwd());
137
- const created = processesModule.saveProcess(hippoRoot, tenantId, {
138
- processName: existing.processName,
139
- steps,
140
- description,
141
- changeSummary,
142
- supersedesProcessId: id,
143
- extraTags: procPathTags,
55
+ results = processesModule.loadProcesses(hippoRoot, tenantId, {
56
+ status: status,
57
+ limit,
144
58
  });
145
- console.log(`Process #${created.id} recorded (v${created.version}), superseding #${id}.`);
146
- if (created.memoryId)
147
- console.log(` memory: ${created.memoryId}`);
148
- return;
149
59
  }
150
- if (subcommand === 'close') {
151
- const idRaw = args[1];
152
- if (!idRaw) {
153
- printError('Usage: hippo process close <id>');
154
- process.exit(1);
155
- }
156
- const id = parsePositiveProcessId(idRaw);
157
- const closed = processesModule.closeProcess(hippoRoot, tenantId, id);
158
- console.log(`Process #${closed.id} closed.`);
60
+ if (results.length === 0) {
61
+ console.log('No processes.');
159
62
  return;
160
63
  }
64
+ console.log(`Found ${results.length} processes:\n`);
65
+ for (const proc of results) {
66
+ console.log(`#${proc.id} [${proc.status}] v${proc.version} steps=${proc.steps.length} memory=${proc.memoryId ?? '-'}`);
67
+ console.log(` ${proc.processName}`);
68
+ if (proc.changeSummary)
69
+ console.log(` change: ${proc.changeSummary}`);
70
+ }
71
+ }
72
+ function processGet(hippoRoot, tenantId, args) {
73
+ const idRaw = args[1];
74
+ if (!idRaw) {
75
+ printError('Usage: hippo process get <id>');
76
+ process.exit(1);
77
+ }
78
+ const id = parsePositiveProcessId(idRaw);
79
+ const proc = processesModule.loadProcessById(hippoRoot, tenantId, id);
80
+ if (!proc) {
81
+ printError(`Process ${id} not found.`);
82
+ process.exit(1);
83
+ }
84
+ console.log(`Process #${proc.id}`);
85
+ console.log(` name: ${proc.processName}`);
86
+ console.log(` status: ${proc.status}`);
87
+ console.log(` version: ${proc.version}`);
88
+ if (proc.description)
89
+ console.log(` description: ${proc.description}`);
90
+ if (proc.steps.length > 0) {
91
+ console.log(` steps:`);
92
+ proc.steps.forEach((s, i) => console.log(` ${i + 1}. ${s}`));
93
+ }
94
+ if (proc.changeSummary)
95
+ console.log(` change_summary: ${proc.changeSummary}`);
96
+ if (proc.supersededBy !== null)
97
+ console.log(` superseded_by: #${proc.supersededBy}`);
98
+ if (proc.supersededAt)
99
+ console.log(` superseded_at: ${proc.supersededAt}`);
100
+ if (proc.closedAt)
101
+ console.log(` closed_at: ${proc.closedAt}`);
102
+ if (proc.memoryId)
103
+ console.log(` memory: ${proc.memoryId}`);
104
+ console.log(` created: ${proc.createdAt}`);
105
+ }
106
+ function processSupersede(hippoRoot, tenantId, args, flags) {
107
+ const idRaw = args[1];
108
+ if (!idRaw) {
109
+ printError('Usage: hippo process supersede <id> --step "<text>" [--step ...] [--change "<summary>"] [--description "<text>"]');
110
+ process.exit(1);
111
+ }
112
+ const id = parsePositiveProcessId(idRaw);
113
+ const steps = collectProcessSteps(flags['step']);
114
+ if (steps.length === 0) {
115
+ printError('hippo process supersede requires at least one --step "<text>" for the new version.');
116
+ process.exit(1);
117
+ }
118
+ // A supersession is a new version of the SAME process, so the new row reuses the predecessor's name (stable identity
119
+ // across versions). loadProcessById gives an early not-found before the write; saveProcess's in-SAVEPOINT preflight
120
+ // is the authoritative active-state check.
121
+ const existing = processesModule.loadProcessById(hippoRoot, tenantId, id);
122
+ if (!existing) {
123
+ printError(`Process ${id} not found.`);
124
+ process.exit(1);
125
+ }
126
+ const changeRaw = flags['change'];
127
+ const changeSummary = typeof changeRaw === 'string' && changeRaw ? changeRaw : undefined;
128
+ const descRaw = flags['description'];
129
+ const description = typeof descRaw === 'string' && descRaw ? descRaw : undefined;
130
+ const procPathTags = extractPathTags(process.cwd());
131
+ const created = processesModule.saveProcess(hippoRoot, tenantId, {
132
+ processName: existing.processName,
133
+ steps,
134
+ description,
135
+ changeSummary,
136
+ supersedesProcessId: id,
137
+ extraTags: procPathTags,
138
+ });
139
+ console.log(`Process #${created.id} recorded (v${created.version}), superseding #${id}.`);
140
+ if (created.memoryId)
141
+ console.log(` memory: ${created.memoryId}`);
142
+ }
143
+ function processClose(hippoRoot, tenantId, args) {
144
+ const idRaw = args[1];
145
+ if (!idRaw) {
146
+ printError('Usage: hippo process close <id>');
147
+ process.exit(1);
148
+ }
149
+ const id = parsePositiveProcessId(idRaw);
150
+ const closed = processesModule.closeProcess(hippoRoot, tenantId, id);
151
+ console.log(`Process #${closed.id} closed.`);
152
+ }
153
+ export function cmdProcess(hippoRoot, args, flags) {
154
+ requireInit(hippoRoot);
155
+ const tenantId = resolveTenantId({});
156
+ const subcommand = args[0] ?? '';
157
+ if (subcommand === 'list')
158
+ return processList(hippoRoot, tenantId, flags);
159
+ if (subcommand === 'get')
160
+ return processGet(hippoRoot, tenantId, args);
161
+ if (subcommand === 'supersede')
162
+ return processSupersede(hippoRoot, tenantId, args, flags);
163
+ if (subcommand === 'close')
164
+ return processClose(hippoRoot, tenantId, args);
161
165
  // Default subcommand: new (create). Accept both the documented
162
166
  // `process new "<name>"` form and the bare `process "<name>"` form: for the
163
167
  // `new` keyword the name is args[1], otherwise args[0] IS the name.
164
- const processName = subcommand === 'new' ? (args[1] ?? '') : subcommand;
168
+ processCreate(hippoRoot, tenantId, subcommand === 'new' ? (args[1] ?? '') : subcommand, flags);
169
+ }
170
+ function processCreate(hippoRoot, tenantId, processName, flags) {
165
171
  if (!processName) {
166
172
  printError('Usage: hippo process new "<name>" --step "<text>" [--step ...] [--description "<text>"]');
167
173
  printError(' hippo process list [--status active|superseded|closed|all] [--limit N]');
@@ -184,8 +190,7 @@ export function cmdProcess(hippoRoot, args, flags) {
184
190
  if (created.memoryId)
185
191
  console.log(` memory: ${created.memoryId}`);
186
192
  }
187
- // Strict positive-integer id parse for the mutating policy subcommands (mirrors
188
- // parsePositiveProcessId; codex P2 class - parseInt alone accepts '1abc' -> 1).
193
+ // Strict positive-integer id parse for the mutating policy subcommands: parseInt alone accepts '1abc' -> 1.
189
194
  function parsePositivePolicyId(idRaw) {
190
195
  const s = String(idRaw ?? '').trim();
191
196
  const id = parseInt(s, 10);
@@ -202,150 +207,154 @@ function printPolicyRow(p) {
202
207
  if (p.changeSummary)
203
208
  console.log(` change: ${p.changeSummary}`);
204
209
  }
205
- export function cmdPolicy(hippoRoot, args, flags) {
206
- requireInit(hippoRoot);
207
- const tenantId = resolveTenantId({});
208
- const subcommand = args[0] ?? '';
209
- if (subcommand === 'list') {
210
- const statusRaw = flags['status'];
211
- const status = typeof statusRaw === 'string' ? statusRaw.trim() : 'all';
212
- const limitRaw = flags['limit'];
213
- const limit = limitRaw !== undefined ? parseInt(String(limitRaw), 10) : 100;
214
- if (!Number.isFinite(limit) || limit <= 0) {
215
- printError(`Invalid --limit: "${limitRaw}". Must be a positive integer.`);
216
- process.exit(1);
217
- }
218
- let results;
219
- if (status === 'all') {
220
- results = policiesModule.loadPolicies(hippoRoot, tenantId, { limit });
221
- }
222
- else {
223
- if (!policiesModule.VALID_POLICY_STATES.has(status)) {
224
- printError(`Invalid --status: "${status}". Must be one of: active | superseded | closed | all.`);
225
- process.exit(1);
226
- }
227
- results = policiesModule.loadPolicies(hippoRoot, tenantId, {
228
- status: status,
229
- limit,
230
- });
231
- }
232
- if (results.length === 0) {
233
- console.log('No policies.');
234
- return;
235
- }
236
- console.log(`Found ${results.length} policies:\n`);
237
- for (const p of results)
238
- printPolicyRow(p);
239
- return;
210
+ function policyList(hippoRoot, tenantId, flags) {
211
+ const statusRaw = flags['status'];
212
+ const status = typeof statusRaw === 'string' ? statusRaw.trim() : 'all';
213
+ const limit = parseListLimit(flags);
214
+ let results;
215
+ if (status === 'all') {
216
+ results = policiesModule.loadPolicies(hippoRoot, tenantId, { limit });
240
217
  }
241
- if (subcommand === 'asof') {
242
- const dateRaw = args[1];
243
- if (!dateRaw) {
244
- printError('Usage: hippo policy asof <iso-date> [--name "<policy>"]');
245
- process.exit(1);
246
- }
247
- const nameRaw = flags['name'];
248
- const name = typeof nameRaw === 'string' && nameRaw ? nameRaw : undefined;
249
- let results;
250
- try {
251
- results = policiesModule.loadPoliciesAsOf(hippoRoot, tenantId, dateRaw, { name });
252
- }
253
- catch (e) {
254
- printError(e.message);
218
+ else {
219
+ if (!policiesModule.VALID_POLICY_STATES.has(status)) {
220
+ printError(`Invalid --status: "${status}". Must be one of: active | superseded | closed | all.`);
255
221
  process.exit(1);
256
222
  }
257
- if (results.length === 0) {
258
- console.log(`No active policies in force at ${dateRaw}${name ? ` for "${name}"` : ''}.`);
259
- return;
260
- }
261
- console.log(`Policies in force at ${dateRaw}:\n`);
262
- for (const p of results)
263
- printPolicyRow(p);
264
- return;
223
+ results = policiesModule.loadPolicies(hippoRoot, tenantId, {
224
+ status: status,
225
+ limit,
226
+ });
265
227
  }
266
- if (subcommand === 'get') {
267
- const idRaw = args[1];
268
- if (!idRaw) {
269
- printError('Usage: hippo policy get <id>');
270
- process.exit(1);
271
- }
272
- const id = parsePositivePolicyId(idRaw);
273
- const p = policiesModule.loadPolicyById(hippoRoot, tenantId, id);
274
- if (!p) {
275
- printError(`Policy ${id} not found.`);
276
- process.exit(1);
277
- }
278
- console.log(`Policy #${p.id}`);
279
- console.log(` name: ${p.policyName}`);
280
- console.log(` text: ${p.policyText}`);
281
- console.log(` status: ${p.status}`);
282
- console.log(` version: ${p.version}`);
283
- console.log(` valid_from: ${p.validFrom}`);
284
- console.log(` valid_to: ${p.validTo ?? '(open-ended)'}`);
285
- if (p.changeSummary)
286
- console.log(` change_summary: ${p.changeSummary}`);
287
- if (p.supersededBy !== null)
288
- console.log(` superseded_by: #${p.supersededBy}`);
289
- if (p.supersededAt)
290
- console.log(` superseded_at: ${p.supersededAt}`);
291
- if (p.closedAt)
292
- console.log(` closed_at: ${p.closedAt}`);
293
- if (p.memoryId)
294
- console.log(` memory: ${p.memoryId}`);
295
- console.log(` created: ${p.createdAt}`);
228
+ if (results.length === 0) {
229
+ console.log('No policies.');
296
230
  return;
297
231
  }
298
- if (subcommand === 'supersede') {
299
- const idRaw = args[1];
300
- if (!idRaw) {
301
- printError('Usage: hippo policy supersede <id> --text "<rule>" [--from <iso>] [--to <iso>] [--change "<summary>"]');
302
- process.exit(1);
303
- }
304
- const id = parsePositivePolicyId(idRaw);
305
- const textRaw = flags['text'];
306
- if (typeof textRaw !== 'string' || !textRaw.trim()) {
307
- printError('hippo policy supersede requires --text "<rule>" for the new version.');
308
- process.exit(1);
309
- }
310
- const existing = policiesModule.loadPolicyById(hippoRoot, tenantId, id);
311
- if (!existing) {
312
- printError(`Policy ${id} not found.`);
313
- process.exit(1);
314
- }
315
- const fromRaw = flags['from'];
316
- const toRaw = flags['to'];
317
- const changeRaw = flags['change'];
318
- try {
319
- const created = policiesModule.savePolicy(hippoRoot, tenantId, {
320
- policyName: existing.policyName,
321
- policyText: textRaw,
322
- validFrom: typeof fromRaw === 'string' && fromRaw ? fromRaw : undefined,
323
- validTo: typeof toRaw === 'string' && toRaw ? toRaw : undefined,
324
- changeSummary: typeof changeRaw === 'string' && changeRaw ? changeRaw : undefined,
325
- supersedesPolicyId: id,
326
- extraTags: extractPathTags(process.cwd()),
327
- });
328
- console.log(`Policy #${created.id} recorded (v${created.version}), superseding #${id}.`);
329
- if (created.memoryId)
330
- console.log(` memory: ${created.memoryId}`);
331
- }
332
- catch (e) {
333
- printError(e.message);
334
- process.exit(1);
335
- }
336
- return;
232
+ console.log(`Found ${results.length} policies:\n`);
233
+ for (const p of results)
234
+ printPolicyRow(p);
235
+ }
236
+ function policyAsOf(hippoRoot, tenantId, args, flags) {
237
+ const dateRaw = args[1];
238
+ if (!dateRaw) {
239
+ printError('Usage: hippo policy asof <iso-date> [--name "<policy>"]');
240
+ process.exit(1);
337
241
  }
338
- if (subcommand === 'close') {
339
- const idRaw = args[1];
340
- if (!idRaw) {
341
- printError('Usage: hippo policy close <id>');
342
- process.exit(1);
343
- }
344
- const id = parsePositivePolicyId(idRaw);
345
- const closed = policiesModule.closePolicy(hippoRoot, tenantId, id);
346
- console.log(`Policy #${closed.id} closed.`);
242
+ const nameRaw = flags['name'];
243
+ const name = typeof nameRaw === 'string' && nameRaw ? nameRaw : undefined;
244
+ let results;
245
+ try {
246
+ results = policiesModule.loadPoliciesAsOf(hippoRoot, tenantId, dateRaw, { name });
247
+ }
248
+ catch (e) {
249
+ printError(e.message);
250
+ process.exit(1);
251
+ }
252
+ if (results.length === 0) {
253
+ console.log(`No active policies in force at ${dateRaw}${name ? ` for "${name}"` : ''}.`);
347
254
  return;
348
255
  }
256
+ console.log(`Policies in force at ${dateRaw}:\n`);
257
+ for (const p of results)
258
+ printPolicyRow(p);
259
+ }
260
+ function policyGet(hippoRoot, tenantId, args) {
261
+ const idRaw = args[1];
262
+ if (!idRaw) {
263
+ printError('Usage: hippo policy get <id>');
264
+ process.exit(1);
265
+ }
266
+ const id = parsePositivePolicyId(idRaw);
267
+ const p = policiesModule.loadPolicyById(hippoRoot, tenantId, id);
268
+ if (!p) {
269
+ printError(`Policy ${id} not found.`);
270
+ process.exit(1);
271
+ }
272
+ console.log(`Policy #${p.id}`);
273
+ console.log(` name: ${p.policyName}`);
274
+ console.log(` text: ${p.policyText}`);
275
+ console.log(` status: ${p.status}`);
276
+ console.log(` version: ${p.version}`);
277
+ console.log(` valid_from: ${p.validFrom}`);
278
+ console.log(` valid_to: ${p.validTo ?? '(open-ended)'}`);
279
+ if (p.changeSummary)
280
+ console.log(` change_summary: ${p.changeSummary}`);
281
+ if (p.supersededBy !== null)
282
+ console.log(` superseded_by: #${p.supersededBy}`);
283
+ if (p.supersededAt)
284
+ console.log(` superseded_at: ${p.supersededAt}`);
285
+ if (p.closedAt)
286
+ console.log(` closed_at: ${p.closedAt}`);
287
+ if (p.memoryId)
288
+ console.log(` memory: ${p.memoryId}`);
289
+ console.log(` created: ${p.createdAt}`);
290
+ }
291
+ function policySupersede(hippoRoot, tenantId, args, flags) {
292
+ const idRaw = args[1];
293
+ if (!idRaw) {
294
+ printError('Usage: hippo policy supersede <id> --text "<rule>" [--from <iso>] [--to <iso>] [--change "<summary>"]');
295
+ process.exit(1);
296
+ }
297
+ const id = parsePositivePolicyId(idRaw);
298
+ const textRaw = flags['text'];
299
+ if (typeof textRaw !== 'string' || !textRaw.trim()) {
300
+ printError('hippo policy supersede requires --text "<rule>" for the new version.');
301
+ process.exit(1);
302
+ }
303
+ const existing = policiesModule.loadPolicyById(hippoRoot, tenantId, id);
304
+ if (!existing) {
305
+ printError(`Policy ${id} not found.`);
306
+ process.exit(1);
307
+ }
308
+ const fromRaw = flags['from'];
309
+ const toRaw = flags['to'];
310
+ const changeRaw = flags['change'];
311
+ try {
312
+ const created = policiesModule.savePolicy(hippoRoot, tenantId, {
313
+ policyName: existing.policyName,
314
+ policyText: textRaw,
315
+ validFrom: typeof fromRaw === 'string' && fromRaw ? fromRaw : undefined,
316
+ validTo: typeof toRaw === 'string' && toRaw ? toRaw : undefined,
317
+ changeSummary: typeof changeRaw === 'string' && changeRaw ? changeRaw : undefined,
318
+ supersedesPolicyId: id,
319
+ extraTags: extractPathTags(process.cwd()),
320
+ });
321
+ console.log(`Policy #${created.id} recorded (v${created.version}), superseding #${id}.`);
322
+ if (created.memoryId)
323
+ console.log(` memory: ${created.memoryId}`);
324
+ }
325
+ catch (e) {
326
+ printError(e.message);
327
+ process.exit(1);
328
+ }
329
+ }
330
+ function policyClose(hippoRoot, tenantId, args) {
331
+ const idRaw = args[1];
332
+ if (!idRaw) {
333
+ printError('Usage: hippo policy close <id>');
334
+ process.exit(1);
335
+ }
336
+ const id = parsePositivePolicyId(idRaw);
337
+ const closed = policiesModule.closePolicy(hippoRoot, tenantId, id);
338
+ console.log(`Policy #${closed.id} closed.`);
339
+ }
340
+ export function cmdPolicy(hippoRoot, args, flags) {
341
+ requireInit(hippoRoot);
342
+ const tenantId = resolveTenantId({});
343
+ const subcommand = args[0] ?? '';
344
+ if (subcommand === 'list')
345
+ return policyList(hippoRoot, tenantId, flags);
346
+ if (subcommand === 'asof')
347
+ return policyAsOf(hippoRoot, tenantId, args, flags);
348
+ if (subcommand === 'get')
349
+ return policyGet(hippoRoot, tenantId, args);
350
+ if (subcommand === 'supersede')
351
+ return policySupersede(hippoRoot, tenantId, args, flags);
352
+ if (subcommand === 'close')
353
+ return policyClose(hippoRoot, tenantId, args);
354
+ policyCreate(hippoRoot, tenantId, args, flags);
355
+ }
356
+ function policyCreate(hippoRoot, tenantId, args, flags) {
357
+ const subcommand = args[0] ?? '';
349
358
  // Default subcommand: new (create). Accept both `policy new "<name>"` and the
350
359
  // bare `policy "<name>"` form: for the `new` keyword the name is args[1].
351
360
  const policyName = subcommand === 'new' ? (args[1] ?? '') : subcommand;
@@ -379,8 +388,7 @@ export function cmdPolicy(hippoRoot, args, flags) {
379
388
  process.exit(1);
380
389
  }
381
390
  }
382
- // Strict positive-integer id parse for the mutating skill subcommands (mirrors
383
- // parsePositivePolicyId; codex P2 class - parseInt accepts '1abc' -> 1).
391
+ // Strict positive-integer id parse for the mutating skill subcommands: parseInt accepts '1abc' -> 1.
384
392
  function parsePositiveSkillId(idRaw) {
385
393
  const s = String(idRaw ?? '').trim();
386
394
  const id = parseInt(s, 10);
@@ -397,132 +405,136 @@ function printSkillRow(s) {
397
405
  if (s.changeSummary)
398
406
  console.log(` change: ${s.changeSummary}`);
399
407
  }
400
- export function cmdSkill(hippoRoot, args, flags) {
401
- requireInit(hippoRoot);
402
- const tenantId = resolveTenantId({});
403
- const subcommand = args[0] ?? '';
404
- if (subcommand === 'list') {
405
- const statusRaw = flags['status'];
406
- const status = typeof statusRaw === 'string' ? statusRaw.trim() : 'all';
407
- const limitRaw = flags['limit'];
408
- const limit = limitRaw !== undefined ? parseInt(String(limitRaw), 10) : 100;
409
- if (!Number.isFinite(limit) || limit <= 0) {
410
- printError(`Invalid --limit: "${limitRaw}". Must be a positive integer.`);
408
+ function skillList(hippoRoot, tenantId, flags) {
409
+ const statusRaw = flags['status'];
410
+ const status = typeof statusRaw === 'string' ? statusRaw.trim() : 'all';
411
+ const limit = parseListLimit(flags);
412
+ let results;
413
+ if (status === 'all') {
414
+ results = skillsModule.loadSkills(hippoRoot, tenantId, { limit });
415
+ }
416
+ else {
417
+ if (!skillsModule.VALID_SKILL_STATES.has(status)) {
418
+ printError(`Invalid --status: "${status}". Must be one of: active | superseded | closed | all.`);
411
419
  process.exit(1);
412
420
  }
413
- let results;
414
- if (status === 'all') {
415
- results = skillsModule.loadSkills(hippoRoot, tenantId, { limit });
416
- }
417
- else {
418
- if (!skillsModule.VALID_SKILL_STATES.has(status)) {
419
- printError(`Invalid --status: "${status}". Must be one of: active | superseded | closed | all.`);
420
- process.exit(1);
421
- }
422
- results = skillsModule.loadSkills(hippoRoot, tenantId, {
423
- status: status,
424
- limit,
425
- });
426
- }
427
- if (results.length === 0) {
428
- console.log('No skills.');
429
- return;
430
- }
431
- console.log(`Found ${results.length} skills:\n`);
432
- for (const s of results)
433
- printSkillRow(s);
434
- return;
421
+ results = skillsModule.loadSkills(hippoRoot, tenantId, {
422
+ status: status,
423
+ limit,
424
+ });
435
425
  }
436
- if (subcommand === 'export') {
437
- const md = skillsModule.exportSkills(hippoRoot, tenantId);
438
- if (!md) {
439
- console.log('No active skills.');
440
- return;
441
- }
442
- console.log(md);
426
+ if (results.length === 0) {
427
+ console.log('No skills.');
443
428
  return;
444
429
  }
445
- if (subcommand === 'get') {
446
- const idRaw = args[1];
447
- if (!idRaw) {
448
- printError('Usage: hippo skill get <id>');
449
- process.exit(1);
450
- }
451
- const id = parsePositiveSkillId(idRaw);
452
- const s = skillsModule.loadSkillById(hippoRoot, tenantId, id);
453
- if (!s) {
454
- printError(`Skill ${id} not found.`);
455
- process.exit(1);
456
- }
457
- console.log(`Skill #${s.id}`);
458
- console.log(` name: ${s.skillName}`);
459
- console.log(` status: ${s.status}`);
460
- console.log(` version: ${s.version}`);
461
- if (s.trigger)
462
- console.log(` when: ${s.trigger}`);
463
- console.log(` instructions: ${s.instructions}`);
464
- if (s.changeSummary)
465
- console.log(` change_summary: ${s.changeSummary}`);
466
- if (s.supersededBy !== null)
467
- console.log(` superseded_by: #${s.supersededBy}`);
468
- if (s.supersededAt)
469
- console.log(` superseded_at: ${s.supersededAt}`);
470
- if (s.closedAt)
471
- console.log(` closed_at: ${s.closedAt}`);
472
- if (s.memoryId)
473
- console.log(` memory: ${s.memoryId}`);
474
- console.log(` created: ${s.createdAt}`);
430
+ console.log(`Found ${results.length} skills:\n`);
431
+ for (const s of results)
432
+ printSkillRow(s);
433
+ }
434
+ function skillExport(hippoRoot, tenantId) {
435
+ const md = skillsModule.exportSkills(hippoRoot, tenantId);
436
+ if (!md) {
437
+ console.log('No active skills.');
475
438
  return;
476
439
  }
477
- if (subcommand === 'supersede') {
478
- const idRaw = args[1];
479
- if (!idRaw) {
480
- printError('Usage: hippo skill supersede <id> --instructions "<text>" [--trigger "<when>"] [--change "<summary>"]');
481
- process.exit(1);
482
- }
483
- const id = parsePositiveSkillId(idRaw);
484
- const instrRaw = flags['instructions'];
485
- if (typeof instrRaw !== 'string' || !instrRaw.trim()) {
486
- printError('hippo skill supersede requires --instructions "<text>" for the new version.');
487
- process.exit(1);
488
- }
489
- const existing = skillsModule.loadSkillById(hippoRoot, tenantId, id);
490
- if (!existing) {
491
- printError(`Skill ${id} not found.`);
492
- process.exit(1);
493
- }
494
- const trigRaw = flags['trigger'];
495
- const changeRaw = flags['change'];
496
- try {
497
- const created = skillsModule.saveSkill(hippoRoot, tenantId, {
498
- skillName: existing.skillName,
499
- instructions: instrRaw,
500
- trigger: typeof trigRaw === 'string' && trigRaw ? trigRaw : undefined,
501
- changeSummary: typeof changeRaw === 'string' && changeRaw ? changeRaw : undefined,
502
- supersedesSkillId: id,
503
- extraTags: extractPathTags(process.cwd()),
504
- });
505
- console.log(`Skill #${created.id} recorded (v${created.version}), superseding #${id}.`);
506
- if (created.memoryId)
507
- console.log(` memory: ${created.memoryId}`);
508
- }
509
- catch (e) {
510
- printError(e.message);
511
- process.exit(1);
512
- }
513
- return;
440
+ console.log(md);
441
+ }
442
+ function skillGet(hippoRoot, tenantId, args) {
443
+ const idRaw = args[1];
444
+ if (!idRaw) {
445
+ printError('Usage: hippo skill get <id>');
446
+ process.exit(1);
514
447
  }
515
- if (subcommand === 'close') {
516
- const idRaw = args[1];
517
- if (!idRaw) {
518
- printError('Usage: hippo skill close <id>');
519
- process.exit(1);
520
- }
521
- const id = parsePositiveSkillId(idRaw);
522
- const closed = skillsModule.closeSkill(hippoRoot, tenantId, id);
523
- console.log(`Skill #${closed.id} closed.`);
524
- return;
448
+ const id = parsePositiveSkillId(idRaw);
449
+ const s = skillsModule.loadSkillById(hippoRoot, tenantId, id);
450
+ if (!s) {
451
+ printError(`Skill ${id} not found.`);
452
+ process.exit(1);
453
+ }
454
+ console.log(`Skill #${s.id}`);
455
+ console.log(` name: ${s.skillName}`);
456
+ console.log(` status: ${s.status}`);
457
+ console.log(` version: ${s.version}`);
458
+ if (s.trigger)
459
+ console.log(` when: ${s.trigger}`);
460
+ console.log(` instructions: ${s.instructions}`);
461
+ if (s.changeSummary)
462
+ console.log(` change_summary: ${s.changeSummary}`);
463
+ if (s.supersededBy !== null)
464
+ console.log(` superseded_by: #${s.supersededBy}`);
465
+ if (s.supersededAt)
466
+ console.log(` superseded_at: ${s.supersededAt}`);
467
+ if (s.closedAt)
468
+ console.log(` closed_at: ${s.closedAt}`);
469
+ if (s.memoryId)
470
+ console.log(` memory: ${s.memoryId}`);
471
+ console.log(` created: ${s.createdAt}`);
472
+ }
473
+ function skillSupersede(hippoRoot, tenantId, args, flags) {
474
+ const idRaw = args[1];
475
+ if (!idRaw) {
476
+ printError('Usage: hippo skill supersede <id> --instructions "<text>" [--trigger "<when>"] [--change "<summary>"]');
477
+ process.exit(1);
478
+ }
479
+ const id = parsePositiveSkillId(idRaw);
480
+ const instrRaw = flags['instructions'];
481
+ if (typeof instrRaw !== 'string' || !instrRaw.trim()) {
482
+ printError('hippo skill supersede requires --instructions "<text>" for the new version.');
483
+ process.exit(1);
525
484
  }
485
+ const existing = skillsModule.loadSkillById(hippoRoot, tenantId, id);
486
+ if (!existing) {
487
+ printError(`Skill ${id} not found.`);
488
+ process.exit(1);
489
+ }
490
+ const trigRaw = flags['trigger'];
491
+ const changeRaw = flags['change'];
492
+ try {
493
+ const created = skillsModule.saveSkill(hippoRoot, tenantId, {
494
+ skillName: existing.skillName,
495
+ instructions: instrRaw,
496
+ trigger: typeof trigRaw === 'string' && trigRaw ? trigRaw : undefined,
497
+ changeSummary: typeof changeRaw === 'string' && changeRaw ? changeRaw : undefined,
498
+ supersedesSkillId: id,
499
+ extraTags: extractPathTags(process.cwd()),
500
+ });
501
+ console.log(`Skill #${created.id} recorded (v${created.version}), superseding #${id}.`);
502
+ if (created.memoryId)
503
+ console.log(` memory: ${created.memoryId}`);
504
+ }
505
+ catch (e) {
506
+ printError(e.message);
507
+ process.exit(1);
508
+ }
509
+ }
510
+ function skillClose(hippoRoot, tenantId, args) {
511
+ const idRaw = args[1];
512
+ if (!idRaw) {
513
+ printError('Usage: hippo skill close <id>');
514
+ process.exit(1);
515
+ }
516
+ const id = parsePositiveSkillId(idRaw);
517
+ const closed = skillsModule.closeSkill(hippoRoot, tenantId, id);
518
+ console.log(`Skill #${closed.id} closed.`);
519
+ }
520
+ export function cmdSkill(hippoRoot, args, flags) {
521
+ requireInit(hippoRoot);
522
+ const tenantId = resolveTenantId({});
523
+ const subcommand = args[0] ?? '';
524
+ if (subcommand === 'list')
525
+ return skillList(hippoRoot, tenantId, flags);
526
+ if (subcommand === 'export')
527
+ return skillExport(hippoRoot, tenantId);
528
+ if (subcommand === 'get')
529
+ return skillGet(hippoRoot, tenantId, args);
530
+ if (subcommand === 'supersede')
531
+ return skillSupersede(hippoRoot, tenantId, args, flags);
532
+ if (subcommand === 'close')
533
+ return skillClose(hippoRoot, tenantId, args);
534
+ skillCreate(hippoRoot, tenantId, args, flags);
535
+ }
536
+ function skillCreate(hippoRoot, tenantId, args, flags) {
537
+ const subcommand = args[0] ?? '';
526
538
  // Default subcommand: new (create). Accept both `skill new "<name>"` and the
527
539
  // bare `skill "<name>"` form: for the `new` keyword the name is args[1].
528
540
  const skillName = subcommand === 'new' ? (args[1] ?? '') : subcommand;