cclaw-cli 7.7.1 → 8.1.1

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 (284) hide show
  1. package/README.md +211 -134
  2. package/dist/artifact-frontmatter.d.ts +51 -0
  3. package/dist/artifact-frontmatter.js +131 -0
  4. package/dist/artifact-paths.d.ts +7 -27
  5. package/dist/artifact-paths.js +20 -249
  6. package/dist/cancel.d.ts +16 -0
  7. package/dist/cancel.js +66 -0
  8. package/dist/cli.d.ts +2 -27
  9. package/dist/cli.js +107 -511
  10. package/dist/compound.d.ts +26 -0
  11. package/dist/compound.js +96 -0
  12. package/dist/config.d.ts +14 -51
  13. package/dist/config.js +23 -359
  14. package/dist/constants.d.ts +11 -18
  15. package/dist/constants.js +19 -106
  16. package/dist/content/antipatterns.d.ts +1 -0
  17. package/dist/content/antipatterns.js +109 -0
  18. package/dist/content/artifact-templates.d.ts +10 -0
  19. package/dist/content/artifact-templates.js +550 -0
  20. package/dist/content/cancel-command.d.ts +2 -2
  21. package/dist/content/cancel-command.js +25 -17
  22. package/dist/content/core-agents.d.ts +9 -233
  23. package/dist/content/core-agents.js +39 -768
  24. package/dist/content/decision-protocol.d.ts +1 -12
  25. package/dist/content/decision-protocol.js +27 -20
  26. package/dist/content/examples.d.ts +8 -42
  27. package/dist/content/examples.js +293 -425
  28. package/dist/content/idea-command.d.ts +2 -0
  29. package/dist/content/idea-command.js +38 -0
  30. package/dist/content/iron-laws.d.ts +4 -138
  31. package/dist/content/iron-laws.js +18 -197
  32. package/dist/content/meta-skill.d.ts +1 -3
  33. package/dist/content/meta-skill.js +57 -134
  34. package/dist/content/node-hooks.d.ts +12 -8
  35. package/dist/content/node-hooks.js +188 -838
  36. package/dist/content/recovery.d.ts +8 -0
  37. package/dist/content/recovery.js +179 -0
  38. package/dist/content/reference-patterns.d.ts +4 -13
  39. package/dist/content/reference-patterns.js +260 -389
  40. package/dist/content/research-playbooks.d.ts +8 -8
  41. package/dist/content/research-playbooks.js +108 -121
  42. package/dist/content/review-loop.d.ts +6 -192
  43. package/dist/content/review-loop.js +29 -731
  44. package/dist/content/skills.d.ts +8 -38
  45. package/dist/content/skills.js +681 -732
  46. package/dist/content/specialist-prompts/architect.d.ts +1 -0
  47. package/dist/content/specialist-prompts/architect.js +225 -0
  48. package/dist/content/specialist-prompts/brainstormer.d.ts +1 -0
  49. package/dist/content/specialist-prompts/brainstormer.js +168 -0
  50. package/dist/content/specialist-prompts/index.d.ts +2 -0
  51. package/dist/content/specialist-prompts/index.js +14 -0
  52. package/dist/content/specialist-prompts/planner.d.ts +1 -0
  53. package/dist/content/specialist-prompts/planner.js +182 -0
  54. package/dist/content/specialist-prompts/reviewer.d.ts +1 -0
  55. package/dist/content/specialist-prompts/reviewer.js +193 -0
  56. package/dist/content/specialist-prompts/security-reviewer.d.ts +1 -0
  57. package/dist/content/specialist-prompts/security-reviewer.js +133 -0
  58. package/dist/content/specialist-prompts/slice-builder.d.ts +1 -0
  59. package/dist/content/specialist-prompts/slice-builder.js +232 -0
  60. package/dist/content/stage-playbooks.d.ts +8 -0
  61. package/dist/content/stage-playbooks.js +404 -0
  62. package/dist/content/start-command.d.ts +2 -12
  63. package/dist/content/start-command.js +221 -207
  64. package/dist/flow-state.d.ts +21 -178
  65. package/dist/flow-state.js +67 -170
  66. package/dist/fs-utils.d.ts +6 -26
  67. package/dist/fs-utils.js +29 -162
  68. package/dist/gitignore.d.ts +2 -1
  69. package/dist/gitignore.js +51 -34
  70. package/dist/harness-detect.d.ts +10 -0
  71. package/dist/harness-detect.js +29 -0
  72. package/dist/harness-prompt.d.ts +26 -0
  73. package/dist/harness-prompt.js +142 -0
  74. package/dist/install.d.ts +35 -15
  75. package/dist/install.js +238 -1347
  76. package/dist/knowledge-store.d.ts +19 -163
  77. package/dist/knowledge-store.js +56 -590
  78. package/dist/logger.d.ts +8 -3
  79. package/dist/logger.js +13 -4
  80. package/dist/orchestrator-routing.d.ts +29 -0
  81. package/dist/orchestrator-routing.js +156 -0
  82. package/dist/run-persistence.d.ts +7 -118
  83. package/dist/run-persistence.js +29 -845
  84. package/dist/runtime/run-hook.entry.d.ts +1 -3
  85. package/dist/runtime/run-hook.entry.js +19 -4
  86. package/dist/runtime/run-hook.mjs +13 -1024
  87. package/dist/types.d.ts +25 -261
  88. package/dist/types.js +8 -36
  89. package/package.json +6 -3
  90. package/dist/artifact-linter/brainstorm.d.ts +0 -2
  91. package/dist/artifact-linter/brainstorm.js +0 -353
  92. package/dist/artifact-linter/design.d.ts +0 -18
  93. package/dist/artifact-linter/design.js +0 -444
  94. package/dist/artifact-linter/findings-dedup.d.ts +0 -56
  95. package/dist/artifact-linter/findings-dedup.js +0 -232
  96. package/dist/artifact-linter/plan.d.ts +0 -2
  97. package/dist/artifact-linter/plan.js +0 -826
  98. package/dist/artifact-linter/review-army.d.ts +0 -49
  99. package/dist/artifact-linter/review-army.js +0 -520
  100. package/dist/artifact-linter/review.d.ts +0 -2
  101. package/dist/artifact-linter/review.js +0 -113
  102. package/dist/artifact-linter/scope.d.ts +0 -2
  103. package/dist/artifact-linter/scope.js +0 -158
  104. package/dist/artifact-linter/shared.d.ts +0 -637
  105. package/dist/artifact-linter/shared.js +0 -2163
  106. package/dist/artifact-linter/ship.d.ts +0 -2
  107. package/dist/artifact-linter/ship.js +0 -250
  108. package/dist/artifact-linter/spec.d.ts +0 -2
  109. package/dist/artifact-linter/spec.js +0 -176
  110. package/dist/artifact-linter/tdd.d.ts +0 -118
  111. package/dist/artifact-linter/tdd.js +0 -1404
  112. package/dist/artifact-linter.d.ts +0 -15
  113. package/dist/artifact-linter.js +0 -517
  114. package/dist/codex-feature-flag.d.ts +0 -58
  115. package/dist/codex-feature-flag.js +0 -193
  116. package/dist/content/closeout-guidance.d.ts +0 -14
  117. package/dist/content/closeout-guidance.js +0 -44
  118. package/dist/content/diff-command.d.ts +0 -1
  119. package/dist/content/diff-command.js +0 -43
  120. package/dist/content/harness-doc.d.ts +0 -1
  121. package/dist/content/harness-doc.js +0 -65
  122. package/dist/content/hook-events.d.ts +0 -9
  123. package/dist/content/hook-events.js +0 -23
  124. package/dist/content/hook-manifest.d.ts +0 -81
  125. package/dist/content/hook-manifest.js +0 -156
  126. package/dist/content/hooks.d.ts +0 -11
  127. package/dist/content/hooks.js +0 -1972
  128. package/dist/content/idea.d.ts +0 -60
  129. package/dist/content/idea.js +0 -416
  130. package/dist/content/language-policy.d.ts +0 -2
  131. package/dist/content/language-policy.js +0 -13
  132. package/dist/content/learnings.d.ts +0 -6
  133. package/dist/content/learnings.js +0 -141
  134. package/dist/content/observe.d.ts +0 -19
  135. package/dist/content/observe.js +0 -86
  136. package/dist/content/opencode-plugin.d.ts +0 -1
  137. package/dist/content/opencode-plugin.js +0 -635
  138. package/dist/content/review-prompts.d.ts +0 -1
  139. package/dist/content/review-prompts.js +0 -104
  140. package/dist/content/runtime-shared-snippets.d.ts +0 -8
  141. package/dist/content/runtime-shared-snippets.js +0 -80
  142. package/dist/content/session-hooks.d.ts +0 -7
  143. package/dist/content/session-hooks.js +0 -107
  144. package/dist/content/skills-elicitation.d.ts +0 -1
  145. package/dist/content/skills-elicitation.js +0 -167
  146. package/dist/content/stage-command.d.ts +0 -2
  147. package/dist/content/stage-command.js +0 -17
  148. package/dist/content/stage-schema.d.ts +0 -117
  149. package/dist/content/stage-schema.js +0 -955
  150. package/dist/content/stages/_lint-metadata/index.d.ts +0 -2
  151. package/dist/content/stages/_lint-metadata/index.js +0 -97
  152. package/dist/content/stages/brainstorm.d.ts +0 -2
  153. package/dist/content/stages/brainstorm.js +0 -184
  154. package/dist/content/stages/design.d.ts +0 -2
  155. package/dist/content/stages/design.js +0 -288
  156. package/dist/content/stages/index.d.ts +0 -8
  157. package/dist/content/stages/index.js +0 -11
  158. package/dist/content/stages/plan.d.ts +0 -2
  159. package/dist/content/stages/plan.js +0 -191
  160. package/dist/content/stages/review.d.ts +0 -2
  161. package/dist/content/stages/review.js +0 -240
  162. package/dist/content/stages/schema-types.d.ts +0 -203
  163. package/dist/content/stages/schema-types.js +0 -1
  164. package/dist/content/stages/scope.d.ts +0 -2
  165. package/dist/content/stages/scope.js +0 -254
  166. package/dist/content/stages/ship.d.ts +0 -2
  167. package/dist/content/stages/ship.js +0 -159
  168. package/dist/content/stages/spec.d.ts +0 -2
  169. package/dist/content/stages/spec.js +0 -170
  170. package/dist/content/stages/tdd.d.ts +0 -4
  171. package/dist/content/stages/tdd.js +0 -273
  172. package/dist/content/state-contracts.d.ts +0 -1
  173. package/dist/content/state-contracts.js +0 -63
  174. package/dist/content/status-command.d.ts +0 -4
  175. package/dist/content/status-command.js +0 -109
  176. package/dist/content/subagent-context-skills.d.ts +0 -4
  177. package/dist/content/subagent-context-skills.js +0 -279
  178. package/dist/content/subagents.d.ts +0 -3
  179. package/dist/content/subagents.js +0 -997
  180. package/dist/content/templates.d.ts +0 -26
  181. package/dist/content/templates.js +0 -1692
  182. package/dist/content/track-render-context.d.ts +0 -18
  183. package/dist/content/track-render-context.js +0 -53
  184. package/dist/content/tree-command.d.ts +0 -1
  185. package/dist/content/tree-command.js +0 -64
  186. package/dist/content/utility-skills.d.ts +0 -30
  187. package/dist/content/utility-skills.js +0 -160
  188. package/dist/content/view-command.d.ts +0 -2
  189. package/dist/content/view-command.js +0 -92
  190. package/dist/delegation.d.ts +0 -649
  191. package/dist/delegation.js +0 -1539
  192. package/dist/early-loop.d.ts +0 -70
  193. package/dist/early-loop.js +0 -302
  194. package/dist/execution-topology.d.ts +0 -44
  195. package/dist/execution-topology.js +0 -95
  196. package/dist/gate-evidence.d.ts +0 -85
  197. package/dist/gate-evidence.js +0 -631
  198. package/dist/harness-adapters.d.ts +0 -151
  199. package/dist/harness-adapters.js +0 -756
  200. package/dist/harness-selection.d.ts +0 -31
  201. package/dist/harness-selection.js +0 -214
  202. package/dist/hook-schema.d.ts +0 -6
  203. package/dist/hook-schema.js +0 -114
  204. package/dist/hook-schemas/claude-hooks.v1.json +0 -10
  205. package/dist/hook-schemas/codex-hooks.v1.json +0 -10
  206. package/dist/hook-schemas/cursor-hooks.v1.json +0 -13
  207. package/dist/init-detect.d.ts +0 -2
  208. package/dist/init-detect.js +0 -50
  209. package/dist/internal/advance-stage/advance.d.ts +0 -89
  210. package/dist/internal/advance-stage/advance.js +0 -655
  211. package/dist/internal/advance-stage/cancel-run.d.ts +0 -8
  212. package/dist/internal/advance-stage/cancel-run.js +0 -19
  213. package/dist/internal/advance-stage/flow-state-coercion.d.ts +0 -3
  214. package/dist/internal/advance-stage/flow-state-coercion.js +0 -81
  215. package/dist/internal/advance-stage/helpers.d.ts +0 -14
  216. package/dist/internal/advance-stage/helpers.js +0 -145
  217. package/dist/internal/advance-stage/hook.d.ts +0 -8
  218. package/dist/internal/advance-stage/hook.js +0 -40
  219. package/dist/internal/advance-stage/parsers.d.ts +0 -72
  220. package/dist/internal/advance-stage/parsers.js +0 -357
  221. package/dist/internal/advance-stage/proactive-delegation-trace.d.ts +0 -24
  222. package/dist/internal/advance-stage/proactive-delegation-trace.js +0 -56
  223. package/dist/internal/advance-stage/review-loop.d.ts +0 -16
  224. package/dist/internal/advance-stage/review-loop.js +0 -199
  225. package/dist/internal/advance-stage/rewind.d.ts +0 -14
  226. package/dist/internal/advance-stage/rewind.js +0 -108
  227. package/dist/internal/advance-stage/start-flow.d.ts +0 -13
  228. package/dist/internal/advance-stage/start-flow.js +0 -241
  229. package/dist/internal/advance-stage/verify.d.ts +0 -21
  230. package/dist/internal/advance-stage/verify.js +0 -185
  231. package/dist/internal/advance-stage.d.ts +0 -7
  232. package/dist/internal/advance-stage.js +0 -138
  233. package/dist/internal/cohesion-contract-stub.d.ts +0 -24
  234. package/dist/internal/cohesion-contract-stub.js +0 -148
  235. package/dist/internal/compound-readiness.d.ts +0 -23
  236. package/dist/internal/compound-readiness.js +0 -102
  237. package/dist/internal/detect-public-api-changes.d.ts +0 -5
  238. package/dist/internal/detect-public-api-changes.js +0 -45
  239. package/dist/internal/detect-supply-chain-changes.d.ts +0 -6
  240. package/dist/internal/detect-supply-chain-changes.js +0 -138
  241. package/dist/internal/early-loop-status.d.ts +0 -7
  242. package/dist/internal/early-loop-status.js +0 -93
  243. package/dist/internal/envelope-validate.d.ts +0 -7
  244. package/dist/internal/envelope-validate.js +0 -66
  245. package/dist/internal/flow-state-repair.d.ts +0 -20
  246. package/dist/internal/flow-state-repair.js +0 -104
  247. package/dist/internal/plan-split-waves.d.ts +0 -190
  248. package/dist/internal/plan-split-waves.js +0 -764
  249. package/dist/internal/runtime-integrity.d.ts +0 -7
  250. package/dist/internal/runtime-integrity.js +0 -268
  251. package/dist/internal/slice-commit.d.ts +0 -7
  252. package/dist/internal/slice-commit.js +0 -619
  253. package/dist/internal/tdd-loop-status.d.ts +0 -14
  254. package/dist/internal/tdd-loop-status.js +0 -68
  255. package/dist/internal/tdd-red-evidence.d.ts +0 -7
  256. package/dist/internal/tdd-red-evidence.js +0 -153
  257. package/dist/internal/waiver-grant.d.ts +0 -62
  258. package/dist/internal/waiver-grant.js +0 -294
  259. package/dist/internal/wave-status.d.ts +0 -74
  260. package/dist/internal/wave-status.js +0 -506
  261. package/dist/managed-resources.d.ts +0 -53
  262. package/dist/managed-resources.js +0 -313
  263. package/dist/policy.d.ts +0 -10
  264. package/dist/policy.js +0 -167
  265. package/dist/retro-gate.d.ts +0 -9
  266. package/dist/retro-gate.js +0 -47
  267. package/dist/run-archive.d.ts +0 -61
  268. package/dist/run-archive.js +0 -391
  269. package/dist/runs.d.ts +0 -2
  270. package/dist/runs.js +0 -2
  271. package/dist/stack-detection.d.ts +0 -116
  272. package/dist/stack-detection.js +0 -489
  273. package/dist/streaming/event-stream.d.ts +0 -31
  274. package/dist/streaming/event-stream.js +0 -114
  275. package/dist/tdd-cycle.d.ts +0 -107
  276. package/dist/tdd-cycle.js +0 -289
  277. package/dist/tdd-verification-evidence.d.ts +0 -17
  278. package/dist/tdd-verification-evidence.js +0 -122
  279. package/dist/track-heuristics.d.ts +0 -27
  280. package/dist/track-heuristics.js +0 -154
  281. package/dist/util/slice-id.d.ts +0 -58
  282. package/dist/util/slice-id.js +0 -89
  283. package/dist/worktree-manager.d.ts +0 -20
  284. package/dist/worktree-manager.js +0 -108
@@ -1,603 +1,69 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { DEFAULT_COMPOUND_RECURRENCE_THRESHOLD } from "./config.js";
4
- import { RUNTIME_ROOT } from "./constants.js";
5
- import { stripBom, withDirectoryLock } from "./fs-utils.js";
6
- import { FLOW_STAGES } from "./types.js";
7
- const DEFAULT_COMPOUND_READINESS_MAX_READY = 10;
8
- /**
9
- * Single source of truth for the small-project relaxation rule.
10
- *
11
- * Kept exported so the inline hook mirror and CLI/runtime paths all agree on
12
- * the same numbers.
13
- */
14
- export const SMALL_PROJECT_ARCHIVE_RUNS_THRESHOLD = 5;
15
- export const SMALL_PROJECT_RECURRENCE_THRESHOLD = 2;
16
- export function effectiveCompoundThreshold(baseThreshold, archivedRunsCount) {
17
- if (typeof archivedRunsCount === "number" &&
18
- Number.isFinite(archivedRunsCount) &&
19
- archivedRunsCount < SMALL_PROJECT_ARCHIVE_RUNS_THRESHOLD &&
20
- baseThreshold > SMALL_PROJECT_RECURRENCE_THRESHOLD) {
21
- return {
22
- threshold: SMALL_PROJECT_RECURRENCE_THRESHOLD,
23
- relaxationApplied: true
24
- };
25
- }
26
- return { threshold: baseThreshold, relaxationApplied: false };
27
- }
28
- /**
29
- * Pure function — no filesystem side effects. Callers pass entries from
30
- * `readKnowledgeSafely` and get a derived readiness snapshot suitable
31
- * for persisting to `.cclaw/state/compound-readiness.json`.
32
- *
33
- * Clustering key: `(type, normalizeText(trigger), normalizeText(action))`
34
- * which mirrors the compound readiness clustering in runtime state.
35
- * The readiness surface intentionally stays simple: no maturity/supersede
36
- * suppression logic in this pass.
37
- */
38
- export function computeCompoundReadiness(entries, options = {}) {
39
- const thresholdRaw = options.threshold ?? DEFAULT_COMPOUND_RECURRENCE_THRESHOLD;
40
- const baseThreshold = Number.isInteger(thresholdRaw) && thresholdRaw >= 1
41
- ? thresholdRaw
42
- : DEFAULT_COMPOUND_RECURRENCE_THRESHOLD;
43
- const maxReadyRaw = options.maxReady ?? DEFAULT_COMPOUND_READINESS_MAX_READY;
44
- const maxReady = Number.isInteger(maxReadyRaw) && maxReadyRaw >= 1
45
- ? maxReadyRaw
46
- : DEFAULT_COMPOUND_READINESS_MAX_READY;
47
- const now = options.now ?? new Date();
48
- const archivedRunsCount = typeof options.archivedRunsCount === "number" &&
49
- Number.isFinite(options.archivedRunsCount) &&
50
- options.archivedRunsCount >= 0
51
- ? Math.floor(options.archivedRunsCount)
52
- : undefined;
53
- const { threshold, relaxationApplied } = effectiveCompoundThreshold(baseThreshold, archivedRunsCount);
54
- const buckets = new Map();
55
- for (const entry of entries) {
56
- const key = [
57
- entry.type,
58
- normalizeText(entry.trigger),
59
- normalizeText(entry.action)
60
- ].join("||");
61
- const frequency = Math.max(1, Math.floor(entry.frequency));
62
- const bucket = buckets.get(key);
63
- if (!bucket) {
64
- buckets.set(key, {
65
- trigger: entry.trigger,
66
- action: entry.action,
67
- recurrence: frequency,
68
- entryCount: 1,
69
- severity: entry.severity,
70
- lastSeenTs: entry.last_seen_ts,
71
- types: new Set([entry.type])
72
- });
73
- continue;
74
- }
75
- bucket.recurrence += frequency;
76
- bucket.entryCount += 1;
77
- bucket.types.add(entry.type);
78
- if (entry.severity === "critical") {
79
- bucket.severity = "critical";
80
- }
81
- else if (entry.severity === "important" && bucket.severity !== "critical") {
82
- bucket.severity = "important";
83
- }
84
- if (Date.parse(entry.last_seen_ts) > Date.parse(bucket.lastSeenTs)) {
85
- bucket.lastSeenTs = entry.last_seen_ts;
86
- }
87
- }
88
- const ready = [];
89
- for (const bucket of buckets.values()) {
90
- const criticalOverride = bucket.severity === "critical";
91
- const meetsRecurrence = bucket.recurrence >= threshold;
92
- if (!criticalOverride && !meetsRecurrence)
93
- continue;
94
- ready.push({
95
- trigger: bucket.trigger,
96
- action: bucket.action,
97
- recurrence: bucket.recurrence,
98
- entryCount: bucket.entryCount,
99
- qualification: criticalOverride && !meetsRecurrence ? "critical_override" : "recurrence",
100
- ...(bucket.severity ? { severity: bucket.severity } : {}),
101
- lastSeenTs: bucket.lastSeenTs,
102
- types: Array.from(bucket.types).sort()
103
- });
104
- }
105
- ready.sort((a, b) => {
106
- const severityWeight = (sev) => {
107
- if (sev === "critical")
108
- return 3;
109
- if (sev === "important")
110
- return 2;
111
- if (sev === "suggestion")
112
- return 1;
113
- return 0;
114
- };
115
- const severityDiff = severityWeight(b.severity) - severityWeight(a.severity);
116
- if (severityDiff !== 0)
117
- return severityDiff;
118
- if (b.recurrence !== a.recurrence)
119
- return b.recurrence - a.recurrence;
120
- const recencyDiff = Date.parse(b.lastSeenTs) - Date.parse(a.lastSeenTs);
121
- if (!Number.isNaN(recencyDiff) && recencyDiff !== 0)
122
- return recencyDiff;
123
- return a.trigger.localeCompare(b.trigger);
124
- });
125
- return {
126
- schemaVersion: 2,
127
- threshold,
128
- baseThreshold,
129
- ...(archivedRunsCount !== undefined ? { archivedRunsCount } : {}),
130
- smallProjectRelaxationApplied: relaxationApplied,
131
- clusterCount: buckets.size,
132
- readyCount: ready.length,
133
- ready: ready.slice(0, maxReady),
134
- lastUpdatedAt: normalizeUtcIso(now.toISOString())
135
- };
3
+ import { KNOWLEDGE_LOG_REL_PATH } from "./constants.js";
4
+ import { exists, writeFileSafe } from "./fs-utils.js";
5
+ export class KnowledgeStoreError extends Error {
136
6
  }
137
- const KNOWLEDGE_TYPE_SET = new Set(["rule", "pattern", "lesson", "compound"]);
138
- const KNOWLEDGE_CONFIDENCE_SET = new Set(["high", "medium", "low"]);
139
- const KNOWLEDGE_SEVERITY_SET = new Set(["critical", "important", "suggestion"]);
140
- const LEGACY_KNOWLEDGE_UNIVERSALITY_SET = new Set(["project", "personal", "universal"]);
141
- const LEGACY_KNOWLEDGE_MATURITY_SET = new Set(["raw", "lifted-to-rule", "lifted-to-enforcement"]);
142
- const KNOWLEDGE_SOURCE_SET = new Set([
143
- "stage",
144
- "retro",
145
- "compound",
146
- "idea",
147
- "manual"
148
- ]);
149
- const FLOW_STAGE_SET = new Set(FLOW_STAGES);
150
- const KNOWLEDGE_REQUIRED_KEYS = [
151
- "type",
152
- "trigger",
153
- "action",
154
- "confidence",
155
- "stage",
156
- "origin_stage",
157
- "frequency",
158
- "created",
159
- "first_seen_ts",
160
- "last_seen_ts",
161
- "project"
162
- ];
163
- const KNOWLEDGE_ALLOWED_KEYS = new Set(KNOWLEDGE_REQUIRED_KEYS);
164
- // Legacy keys are accepted for backwards compatibility when reading historical
165
- // knowledge entries, but no longer required for newly materialized rows.
166
- KNOWLEDGE_ALLOWED_KEYS.add("domain");
167
- KNOWLEDGE_ALLOWED_KEYS.add("origin_run");
168
- KNOWLEDGE_ALLOWED_KEYS.add("universality");
169
- KNOWLEDGE_ALLOWED_KEYS.add("maturity");
170
- KNOWLEDGE_ALLOWED_KEYS.add("source");
171
- KNOWLEDGE_ALLOWED_KEYS.add("severity");
172
- KNOWLEDGE_ALLOWED_KEYS.add("supersedes");
173
- KNOWLEDGE_ALLOWED_KEYS.add("superseded_by");
174
- function keyAllowedInKnowledgeEntry(key) {
175
- return KNOWLEDGE_ALLOWED_KEYS.has(key);
7
+ export function knowledgeLogPath(projectRoot) {
8
+ return path.join(projectRoot, KNOWLEDGE_LOG_REL_PATH);
176
9
  }
177
- function normalizeParsedKnowledgeEntry(obj) {
178
- const normalized = {
179
- type: obj.type,
180
- trigger: obj.trigger,
181
- action: obj.action,
182
- confidence: obj.confidence,
183
- stage: obj.stage,
184
- origin_stage: obj.origin_stage,
185
- frequency: obj.frequency,
186
- created: obj.created,
187
- first_seen_ts: obj.first_seen_ts,
188
- last_seen_ts: obj.last_seen_ts,
189
- project: obj.project
190
- };
191
- if (obj.severity !== undefined) {
192
- normalized.severity = obj.severity;
10
+ function assertEntry(value) {
11
+ if (typeof value !== "object" || value === null) {
12
+ throw new KnowledgeStoreError("Knowledge entry must be an object.");
193
13
  }
194
- if (obj.source !== undefined) {
195
- normalized.source = obj.source;
196
- }
197
- return normalized;
198
- }
199
- function knowledgePath(projectRoot) {
200
- return path.join(projectRoot, RUNTIME_ROOT, "knowledge.jsonl");
201
- }
202
- function knowledgeLockPath(projectRoot) {
203
- return path.join(projectRoot, RUNTIME_ROOT, "state", ".knowledge.lock");
204
- }
205
- function normalizeUtcIso(iso) {
206
- return iso.replace(/\.\d{3}Z$/u, "Z");
207
- }
208
- function nowUtcIso() {
209
- return normalizeUtcIso(new Date().toISOString());
210
- }
211
- function normalizeText(value) {
212
- return value.trim().replace(/\s+/gu, " ").toLowerCase();
213
- }
214
- function dedupeKey(entry) {
215
- return [
216
- entry.type,
217
- normalizeText(entry.trigger),
218
- normalizeText(entry.action),
219
- entry.stage ?? "null",
220
- entry.origin_stage ?? "null",
221
- entry.project === null ? "null" : normalizeText(entry.project),
222
- entry.source === undefined || entry.source === null ? "null" : entry.source,
223
- entry.severity === undefined ? "none" : entry.severity
224
- ].join("|");
225
- }
226
- function emptyKnowledgeSnapshot() {
227
- return {
228
- lines: [],
229
- entries: [],
230
- malformedLines: 0,
231
- keyToIndex: new Map(),
232
- entryByIndex: new Map()
233
- };
234
- }
235
- function parseKnowledgeSnapshot(raw) {
236
- const lines = stripBom(raw).split(/\r?\n/u);
237
- const entries = [];
238
- const keyToIndex = new Map();
239
- const entryByIndex = new Map();
240
- let malformedLines = 0;
241
- for (let i = 0; i < lines.length; i += 1) {
242
- const trimmed = lines[i].trim();
243
- if (trimmed.length === 0)
244
- continue;
245
- try {
246
- const parsed = JSON.parse(trimmed);
247
- const validated = validateKnowledgeEntry(parsed);
248
- if (!validated.ok) {
249
- malformedLines += 1;
250
- continue;
251
- }
252
- const entry = normalizeParsedKnowledgeEntry(parsed);
253
- entries.push(entry);
254
- const key = dedupeKey(entry);
255
- if (!keyToIndex.has(key)) {
256
- keyToIndex.set(key, i);
257
- }
258
- entryByIndex.set(i, entry);
259
- }
260
- catch {
261
- malformedLines += 1;
14
+ const entry = value;
15
+ for (const key of ["slug", "ship_commit", "shipped_at"]) {
16
+ if (typeof entry[key] !== "string" || entry[key].length === 0) {
17
+ throw new KnowledgeStoreError(`Knowledge entry must include string ${key}.`);
262
18
  }
263
19
  }
264
- return {
265
- lines,
266
- entries,
267
- malformedLines,
268
- keyToIndex,
269
- entryByIndex
270
- };
271
- }
272
- async function readKnowledgeSnapshot(filePath) {
273
- try {
274
- const raw = await fs.readFile(filePath, "utf8");
275
- return parseKnowledgeSnapshot(raw);
276
- }
277
- catch (error) {
278
- const code = error?.code;
279
- if (code === "ENOENT") {
280
- return emptyKnowledgeSnapshot();
281
- }
282
- throw error;
20
+ if (typeof entry.signals !== "object" || entry.signals === null) {
21
+ throw new KnowledgeStoreError("Knowledge entry must include a `signals` object.");
283
22
  }
284
23
  }
285
- function mergeKnowledgeOccurrence(target, incoming) {
286
- const mergedFrequency = target.frequency + Math.max(1, incoming.frequency);
287
- const mergedLastSeen = target.last_seen_ts >= incoming.last_seen_ts
288
- ? target.last_seen_ts
289
- : incoming.last_seen_ts;
290
- return {
291
- ...target,
292
- frequency: mergedFrequency,
293
- last_seen_ts: mergedLastSeen
294
- };
295
- }
296
- function isIsoUtcTimestamp(value) {
297
- return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$/u.test(value);
298
- }
299
- function isNullableString(value) {
300
- return value === null || typeof value === "string";
301
- }
302
- function isNullableStage(value) {
303
- return value === null || (typeof value === "string" && FLOW_STAGE_SET.has(value));
304
- }
305
- export function validateKnowledgeEntry(entry) {
306
- const errors = [];
307
- if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
308
- return { ok: false, errors: ["Knowledge entry must be a JSON object."] };
309
- }
310
- const obj = entry;
311
- for (const key of Object.keys(obj)) {
312
- if (!keyAllowedInKnowledgeEntry(key)) {
313
- errors.push(`Unknown key "${key}" in knowledge entry.`);
314
- }
315
- }
316
- for (const key of KNOWLEDGE_REQUIRED_KEYS) {
317
- if (!Object.prototype.hasOwnProperty.call(obj, key)) {
318
- errors.push(`Missing required key "${key}".`);
319
- }
320
- }
321
- if (!KNOWLEDGE_TYPE_SET.has(obj.type)) {
322
- errors.push("type must be one of: rule, pattern, lesson, compound.");
323
- }
324
- if (typeof obj.trigger !== "string" || obj.trigger.trim().length === 0) {
325
- errors.push("trigger must be a non-empty string.");
326
- }
327
- if (typeof obj.action !== "string" || obj.action.trim().length === 0) {
328
- errors.push("action must be a non-empty string.");
329
- }
330
- if (!KNOWLEDGE_CONFIDENCE_SET.has(obj.confidence)) {
331
- errors.push("confidence must be one of: high, medium, low.");
332
- }
333
- if (obj.severity !== undefined &&
334
- (typeof obj.severity !== "string" || !KNOWLEDGE_SEVERITY_SET.has(obj.severity))) {
335
- errors.push("severity must be one of: critical, important, suggestion.");
336
- }
337
- if (obj.domain !== undefined && !isNullableString(obj.domain)) {
338
- errors.push("domain must be string or null.");
339
- }
340
- if (!isNullableStage(obj.stage)) {
341
- errors.push(`stage must be one of ${FLOW_STAGES.join(", ")} or null.`);
342
- }
343
- if (!isNullableStage(obj.origin_stage)) {
344
- errors.push(`origin_stage must be one of ${FLOW_STAGES.join(", ")} or null.`);
345
- }
346
- const originRun = obj.origin_run;
347
- if (originRun !== undefined && !isNullableString(originRun)) {
348
- errors.push("origin_run must be string or null.");
24
+ export async function appendKnowledgeEntry(projectRoot, entry) {
25
+ assertEntry(entry);
26
+ const target = knowledgeLogPath(projectRoot);
27
+ const line = `${JSON.stringify(entry)}\n`;
28
+ if (!(await exists(target))) {
29
+ await writeFileSafe(target, line);
30
+ return;
349
31
  }
350
- if (typeof obj.frequency !== "number" ||
351
- !Number.isInteger(obj.frequency) ||
352
- obj.frequency < 1) {
353
- errors.push("frequency must be an integer >= 1.");
354
- }
355
- if (obj.universality !== undefined &&
356
- (typeof obj.universality !== "string" || !LEGACY_KNOWLEDGE_UNIVERSALITY_SET.has(obj.universality))) {
357
- errors.push("universality must be one of: project, personal, universal.");
358
- }
359
- if (obj.maturity !== undefined &&
360
- (typeof obj.maturity !== "string" || !LEGACY_KNOWLEDGE_MATURITY_SET.has(obj.maturity))) {
361
- errors.push("maturity must be one of: raw, lifted-to-rule, lifted-to-enforcement.");
362
- }
363
- for (const timestampField of ["created", "first_seen_ts", "last_seen_ts"]) {
364
- const value = obj[timestampField];
365
- if (typeof value !== "string" || !isIsoUtcTimestamp(value)) {
366
- errors.push(`${timestampField} must be ISO UTC (YYYY-MM-DDTHH:MM:SSZ).`);
367
- }
368
- }
369
- if (!isNullableString(obj.project)) {
370
- errors.push("project must be string or null.");
371
- }
372
- if (obj.supersedes !== undefined) {
373
- if (!Array.isArray(obj.supersedes) ||
374
- obj.supersedes.length === 0 ||
375
- obj.supersedes.some((value) => typeof value !== "string" || value.trim().length === 0)) {
376
- errors.push("supersedes must be a non-empty array of strings when present.");
377
- }
378
- }
379
- if (obj.superseded_by !== undefined &&
380
- (typeof obj.superseded_by !== "string" || obj.superseded_by.trim().length === 0)) {
381
- errors.push("superseded_by must be a non-empty string when present.");
382
- }
383
- const sourceAllowed = obj.source === undefined ||
384
- obj.source === null ||
385
- (typeof obj.source === "string" &&
386
- KNOWLEDGE_SOURCE_SET.has(obj.source));
387
- if (!sourceAllowed) {
388
- errors.push("source must be one of: stage, retro, compound, idea, manual, or null.");
389
- }
390
- return { ok: errors.length === 0, errors };
32
+ await fs.appendFile(target, line, "utf8");
391
33
  }
392
- export function materializeKnowledgeEntry(seed, defaults = {}) {
393
- const now = normalizeUtcIso(defaults.nowIso ?? nowUtcIso());
394
- const stage = seed.stage ?? defaults.stage ?? null;
395
- const originStage = seed.origin_stage ?? defaults.originStage ?? stage ?? null;
396
- const source = seed.source ?? defaults.source ?? null;
397
- const entry = {
398
- type: seed.type,
399
- trigger: seed.trigger.trim(),
400
- action: seed.action.trim(),
401
- confidence: seed.confidence,
402
- stage,
403
- origin_stage: originStage,
404
- frequency: seed.frequency ?? 1,
405
- created: normalizeUtcIso(seed.created ?? now),
406
- first_seen_ts: normalizeUtcIso(seed.first_seen_ts ?? now),
407
- last_seen_ts: normalizeUtcIso(seed.last_seen_ts ?? now),
408
- project: seed.project ?? defaults.project ?? null
409
- };
410
- if (seed.severity !== undefined) {
411
- entry.severity = seed.severity;
412
- }
413
- if (source !== null) {
414
- entry.source = source;
415
- }
416
- return entry;
417
- }
418
- export async function readKnowledgeSafely(projectRoot, options = {}) {
419
- const filePath = knowledgePath(projectRoot);
420
- const read = async () => {
421
- const snapshot = await readKnowledgeSnapshot(filePath);
422
- return {
423
- entries: snapshot.entries,
424
- malformedLines: snapshot.malformedLines
425
- };
426
- };
427
- if (options.lockAware === false) {
428
- return read();
429
- }
430
- return withDirectoryLock(knowledgeLockPath(projectRoot), read);
431
- }
432
- export async function appendKnowledge(projectRoot, seeds, defaults = {}) {
433
- if (seeds.length === 0) {
434
- return { appended: 0, skippedDuplicates: 0, invalid: 0, errors: [], appendedEntries: [] };
435
- }
436
- const filePath = knowledgePath(projectRoot);
437
- const errors = [];
438
- const materialized = [];
439
- for (let i = 0; i < seeds.length; i += 1) {
440
- const seed = seeds[i];
441
- const entry = materializeKnowledgeEntry(seed, defaults);
442
- const validated = validateKnowledgeEntry(entry);
443
- if (!validated.ok) {
444
- errors.push(`entry #${i + 1}: ${validated.errors.join(" ")}`);
445
- continue;
446
- }
447
- materialized.push(entry);
448
- }
449
- let skippedDuplicates = 0;
450
- const appendedEntries = [];
451
- await withDirectoryLock(knowledgeLockPath(projectRoot), async () => {
452
- await fs.mkdir(path.dirname(filePath), { recursive: true });
453
- const snapshot = await readKnowledgeSnapshot(filePath);
454
- const updatedByIndex = new Map();
455
- const batchEntries = new Map();
456
- for (const entry of materialized) {
457
- const key = dedupeKey(entry);
458
- const existingIndex = snapshot.keyToIndex.get(key);
459
- if (existingIndex !== undefined) {
460
- skippedDuplicates += 1;
461
- const base = updatedByIndex.get(existingIndex) ?? snapshot.entryByIndex.get(existingIndex);
462
- if (base) {
463
- updatedByIndex.set(existingIndex, mergeKnowledgeOccurrence(base, entry));
464
- }
465
- continue;
466
- }
467
- const existingBatchEntry = batchEntries.get(key);
468
- if (existingBatchEntry) {
469
- skippedDuplicates += 1;
470
- batchEntries.set(key, mergeKnowledgeOccurrence(existingBatchEntry, entry));
471
- continue;
472
- }
473
- batchEntries.set(key, { ...entry });
474
- }
475
- appendedEntries.push(...batchEntries.values());
476
- if (updatedByIndex.size === 0 && batchEntries.size === 0) {
477
- return;
478
- }
479
- const rewrittenLines = snapshot.lines.map((line, index) => {
480
- const updated = updatedByIndex.get(index);
481
- return updated ? JSON.stringify(updated) : line;
482
- }).filter((line) => line.trim().length > 0);
483
- const linesToWrite = [
484
- ...rewrittenLines,
485
- ...Array.from(batchEntries.values(), (entry) => JSON.stringify(entry))
486
- ];
487
- if (linesToWrite.length > 0) {
488
- await fs.writeFile(filePath, `${linesToWrite.join("\n")}\n`, "utf8");
489
- }
490
- });
491
- return {
492
- appended: appendedEntries.length,
493
- skippedDuplicates,
494
- invalid: errors.length,
495
- errors,
496
- appendedEntries
497
- };
498
- }
499
- const SHORT_TECHNICAL_TOKEN_SET = new Set(["ci", "db", "ui", "qa", "ux"]);
500
- function tokenizeText(value) {
501
- if (!value)
502
- return [];
503
- const tokens = [];
504
- const matches = value.matchAll(/[A-Za-z0-9]+/gu);
505
- for (const match of matches) {
506
- const raw = match[0] ?? "";
507
- const normalized = raw.toLowerCase();
508
- if (normalized.length >= 3) {
509
- tokens.push(normalized);
510
- continue;
511
- }
512
- if (/^[A-Z]{2}$/u.test(raw) || SHORT_TECHNICAL_TOKEN_SET.has(normalized)) {
513
- tokens.push(normalized);
514
- }
515
- }
516
- return tokens;
517
- }
518
- function uniqueTokens(values) {
519
- return [...new Set(values)];
520
- }
521
- function pathTokens(paths) {
522
- if (!Array.isArray(paths) || paths.length === 0)
34
+ export async function readKnowledgeLog(projectRoot) {
35
+ const target = knowledgeLogPath(projectRoot);
36
+ if (!(await exists(target)))
523
37
  return [];
524
- const tokens = [];
525
- for (const filePath of paths) {
526
- tokens.push(...tokenizeText(filePath));
527
- }
528
- return uniqueTokens(tokens);
529
- }
530
- export async function selectRelevantLearnings(projectRoot, options = {}) {
531
- const { entries } = await readKnowledgeSafely(projectRoot);
532
- if (entries.length === 0) {
533
- return [];
534
- }
535
- const stage = options.stage ?? null;
536
- const branchTokens = tokenizeText(options.branch ?? null);
537
- const diffTokens = pathTokens(options.diffFiles);
538
- const gateTokens = pathTokens(options.openGates);
539
- const limit = typeof options.limit === "number" && Number.isFinite(options.limit) && options.limit > 0
540
- ? Math.floor(options.limit)
541
- : 8;
542
- const ranked = entries.map((entry, index) => {
543
- let score = 0;
544
- let stageScore = 0;
545
- if (stage) {
546
- if (entry.stage === stage) {
547
- stageScore = 4;
548
- }
549
- else if (entry.origin_stage === stage) {
550
- stageScore = 3;
551
- }
552
- score += stageScore;
553
- }
554
- if (entry.confidence === "high")
555
- score += 2;
556
- if (entry.confidence === "medium")
557
- score += 1;
558
- if (entry.frequency >= 3)
559
- score += 1;
560
- const searchable = [
561
- ...tokenizeText(entry.trigger),
562
- ...tokenizeText(entry.action),
563
- ...tokenizeText(entry.project)
564
- ];
565
- const searchSet = new Set(searchable);
566
- let contextualScore = 0;
567
- for (const token of branchTokens) {
568
- if (searchSet.has(token))
569
- contextualScore += 2;
570
- }
571
- for (const token of diffTokens) {
572
- if (searchSet.has(token))
573
- contextualScore += 2;
574
- }
575
- for (const token of gateTokens) {
576
- if (searchSet.has(token))
577
- contextualScore += 2;
578
- }
579
- score += contextualScore;
580
- if (stage && entry.stage === null && stageScore === 0 && contextualScore < 4) {
581
- score = 0;
582
- }
583
- return {
584
- index,
585
- score,
586
- entry
587
- };
588
- });
589
- ranked.sort((a, b) => {
590
- if (b.score !== a.score)
591
- return b.score - a.score;
592
- const bySeen = Date.parse(b.entry.last_seen_ts) - Date.parse(a.entry.last_seen_ts);
593
- if (!Number.isNaN(bySeen) && bySeen !== 0)
594
- return bySeen;
595
- if (b.entry.frequency !== a.entry.frequency)
596
- return b.entry.frequency - a.entry.frequency;
597
- return b.index - a.index;
598
- });
599
- return ranked
600
- .filter((row) => row.score > 0)
601
- .slice(0, limit)
602
- .map((row) => row.entry);
38
+ const raw = await fs.readFile(target, "utf8");
39
+ const lines = raw.split(/\r?\n/u).filter((line) => line.trim().length > 0);
40
+ const entries = [];
41
+ for (const line of lines) {
42
+ let parsed;
43
+ try {
44
+ parsed = JSON.parse(line);
45
+ }
46
+ catch (err) {
47
+ throw new KnowledgeStoreError(`Invalid JSON line in knowledge.jsonl: ${err.message}`);
48
+ }
49
+ assertEntry(parsed);
50
+ entries.push(parsed);
51
+ }
52
+ return entries;
53
+ }
54
+ export async function findRefiningChain(projectRoot, slug) {
55
+ const all = await readKnowledgeLog(projectRoot);
56
+ const bySlug = new Map();
57
+ for (const entry of all)
58
+ bySlug.set(entry.slug, entry);
59
+ const chain = [];
60
+ let cursor = slug;
61
+ const seen = new Set();
62
+ while (cursor !== null && cursor !== undefined && bySlug.has(cursor) && !seen.has(cursor)) {
63
+ const found = bySlug.get(cursor);
64
+ chain.push(found);
65
+ seen.add(cursor);
66
+ cursor = found.refines ?? null;
67
+ }
68
+ return chain;
603
69
  }
package/dist/logger.d.ts CHANGED
@@ -1,3 +1,8 @@
1
- import type { CliContext } from "./types.js";
2
- export declare function info(ctx: CliContext, message: string): void;
3
- export declare function error(ctx: CliContext, message: string): void;
1
+ type Stream = NodeJS.WriteStream | {
2
+ write: (chunk: string) => unknown;
3
+ };
4
+ export declare function configureLogger(out: Stream, err: Stream): void;
5
+ export declare function info(message: string): void;
6
+ export declare function warn(message: string): void;
7
+ export declare function error(message: string): void;
8
+ export {};