cclaw-cli 0.49.0 → 0.51.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 (183) hide show
  1. package/README.md +57 -84
  2. package/dist/artifact-linter.d.ts +4 -0
  3. package/dist/artifact-linter.js +24 -3
  4. package/dist/cli.d.ts +1 -19
  5. package/dist/cli.js +49 -491
  6. package/dist/constants.d.ts +2 -13
  7. package/dist/constants.js +1 -43
  8. package/dist/content/closeout-guidance.d.ts +14 -0
  9. package/dist/content/closeout-guidance.js +42 -0
  10. package/dist/content/core-agents.js +55 -17
  11. package/dist/content/decision-protocol.d.ts +12 -0
  12. package/dist/content/decision-protocol.js +20 -0
  13. package/dist/content/diff-command.d.ts +1 -2
  14. package/dist/content/diff-command.js +8 -94
  15. package/dist/content/examples.d.ts +4 -10
  16. package/dist/content/examples.js +10 -20
  17. package/dist/content/hook-events.js +2 -2
  18. package/dist/content/hook-inline-snippets.d.ts +5 -2
  19. package/dist/content/hook-inline-snippets.js +33 -1
  20. package/dist/content/hook-manifest.d.ts +3 -4
  21. package/dist/content/hook-manifest.js +11 -12
  22. package/dist/content/hooks.js +44 -21
  23. package/dist/content/ideate-command.d.ts +2 -0
  24. package/dist/content/ideate-command.js +34 -25
  25. package/dist/content/iron-laws.d.ts +5 -5
  26. package/dist/content/iron-laws.js +5 -5
  27. package/dist/content/language-policy.d.ts +2 -0
  28. package/dist/content/language-policy.js +13 -0
  29. package/dist/content/learnings.d.ts +3 -4
  30. package/dist/content/learnings.js +26 -50
  31. package/dist/content/meta-skill.js +33 -22
  32. package/dist/content/next-command.js +41 -38
  33. package/dist/content/node-hooks.js +17 -345
  34. package/dist/content/opencode-plugin.js +5 -103
  35. package/dist/content/research-playbooks.js +14 -14
  36. package/dist/content/review-loop.d.ts +2 -0
  37. package/dist/content/review-loop.js +8 -0
  38. package/dist/content/session-hooks.js +15 -47
  39. package/dist/content/skills.d.ts +0 -5
  40. package/dist/content/skills.js +55 -128
  41. package/dist/content/stage-common-guidance.d.ts +0 -1
  42. package/dist/content/stage-common-guidance.js +17 -14
  43. package/dist/content/stage-schema.d.ts +26 -1
  44. package/dist/content/stage-schema.js +121 -40
  45. package/dist/content/stages/_lint-metadata/index.js +9 -15
  46. package/dist/content/stages/brainstorm.js +22 -43
  47. package/dist/content/stages/design.js +37 -57
  48. package/dist/content/stages/plan.js +22 -13
  49. package/dist/content/stages/review.js +24 -27
  50. package/dist/content/stages/scope.js +34 -46
  51. package/dist/content/stages/ship.js +7 -4
  52. package/dist/content/stages/spec.js +20 -9
  53. package/dist/content/stages/tdd.js +64 -44
  54. package/dist/content/start-command.js +13 -12
  55. package/dist/content/status-command.d.ts +2 -7
  56. package/dist/content/status-command.js +19 -146
  57. package/dist/content/subagents.d.ts +0 -5
  58. package/dist/content/subagents.js +51 -28
  59. package/dist/content/templates.d.ts +1 -1
  60. package/dist/content/templates.js +126 -135
  61. package/dist/content/track-render-context.d.ts +17 -0
  62. package/dist/content/track-render-context.js +44 -0
  63. package/dist/content/tree-command.d.ts +1 -2
  64. package/dist/content/tree-command.js +4 -87
  65. package/dist/content/utility-skills.d.ts +2 -29
  66. package/dist/content/utility-skills.js +2 -1534
  67. package/dist/content/view-command.js +31 -11
  68. package/dist/delegation.d.ts +1 -1
  69. package/dist/delegation.js +5 -15
  70. package/dist/doctor-registry.js +20 -21
  71. package/dist/doctor.js +88 -344
  72. package/dist/flow-state.d.ts +3 -0
  73. package/dist/flow-state.js +2 -0
  74. package/dist/harness-adapters.d.ts +1 -1
  75. package/dist/harness-adapters.js +51 -58
  76. package/dist/install.js +128 -358
  77. package/dist/internal/advance-stage.js +3 -9
  78. package/dist/internal/compound-readiness.d.ts +1 -1
  79. package/dist/internal/compound-readiness.js +1 -1
  80. package/dist/internal/tdd-loop-status.d.ts +1 -1
  81. package/dist/internal/tdd-loop-status.js +1 -1
  82. package/dist/knowledge-store.d.ts +16 -10
  83. package/dist/knowledge-store.js +51 -15
  84. package/dist/policy.js +16 -105
  85. package/dist/run-archive.d.ts +4 -6
  86. package/dist/run-archive.js +15 -20
  87. package/dist/run-persistence.d.ts +2 -2
  88. package/dist/run-persistence.js +3 -9
  89. package/package.json +1 -2
  90. package/dist/content/archive-command.d.ts +0 -2
  91. package/dist/content/archive-command.js +0 -124
  92. package/dist/content/compound-command.d.ts +0 -5
  93. package/dist/content/compound-command.js +0 -193
  94. package/dist/content/contexts.d.ts +0 -18
  95. package/dist/content/contexts.js +0 -24
  96. package/dist/content/contracts.d.ts +0 -2
  97. package/dist/content/contracts.js +0 -51
  98. package/dist/content/doctor-references.d.ts +0 -2
  99. package/dist/content/doctor-references.js +0 -150
  100. package/dist/content/eval-scaffold.d.ts +0 -15
  101. package/dist/content/eval-scaffold.js +0 -370
  102. package/dist/content/feature-command.d.ts +0 -2
  103. package/dist/content/feature-command.js +0 -123
  104. package/dist/content/flow-map.d.ts +0 -23
  105. package/dist/content/flow-map.js +0 -134
  106. package/dist/content/harness-doc.d.ts +0 -2
  107. package/dist/content/harness-doc.js +0 -202
  108. package/dist/content/harness-playbooks.d.ts +0 -24
  109. package/dist/content/harness-playbooks.js +0 -393
  110. package/dist/content/harness-tool-refs.d.ts +0 -20
  111. package/dist/content/harness-tool-refs.js +0 -268
  112. package/dist/content/ops-command.d.ts +0 -2
  113. package/dist/content/ops-command.js +0 -71
  114. package/dist/content/protocols.d.ts +0 -7
  115. package/dist/content/protocols.js +0 -215
  116. package/dist/content/retro-command.d.ts +0 -2
  117. package/dist/content/retro-command.js +0 -165
  118. package/dist/content/rewind-command.d.ts +0 -2
  119. package/dist/content/rewind-command.js +0 -106
  120. package/dist/content/tdd-log-command.d.ts +0 -2
  121. package/dist/content/tdd-log-command.js +0 -85
  122. package/dist/eval/agents/single-shot.d.ts +0 -27
  123. package/dist/eval/agents/single-shot.js +0 -79
  124. package/dist/eval/agents/with-tools.d.ts +0 -44
  125. package/dist/eval/agents/with-tools.js +0 -261
  126. package/dist/eval/agents/workflow.d.ts +0 -31
  127. package/dist/eval/agents/workflow.js +0 -155
  128. package/dist/eval/baseline.d.ts +0 -38
  129. package/dist/eval/baseline.js +0 -282
  130. package/dist/eval/config-loader.d.ts +0 -14
  131. package/dist/eval/config-loader.js +0 -395
  132. package/dist/eval/corpus.d.ts +0 -30
  133. package/dist/eval/corpus.js +0 -330
  134. package/dist/eval/cost-guard.d.ts +0 -102
  135. package/dist/eval/cost-guard.js +0 -190
  136. package/dist/eval/diff.d.ts +0 -64
  137. package/dist/eval/diff.js +0 -323
  138. package/dist/eval/llm-client.d.ts +0 -176
  139. package/dist/eval/llm-client.js +0 -267
  140. package/dist/eval/mode.d.ts +0 -28
  141. package/dist/eval/mode.js +0 -61
  142. package/dist/eval/progress.d.ts +0 -83
  143. package/dist/eval/progress.js +0 -59
  144. package/dist/eval/report.d.ts +0 -11
  145. package/dist/eval/report.js +0 -181
  146. package/dist/eval/rubric-loader.d.ts +0 -20
  147. package/dist/eval/rubric-loader.js +0 -143
  148. package/dist/eval/runner.d.ts +0 -81
  149. package/dist/eval/runner.js +0 -746
  150. package/dist/eval/runs.d.ts +0 -41
  151. package/dist/eval/runs.js +0 -114
  152. package/dist/eval/sandbox.d.ts +0 -38
  153. package/dist/eval/sandbox.js +0 -137
  154. package/dist/eval/tools/glob.d.ts +0 -2
  155. package/dist/eval/tools/glob.js +0 -163
  156. package/dist/eval/tools/grep.d.ts +0 -2
  157. package/dist/eval/tools/grep.js +0 -152
  158. package/dist/eval/tools/index.d.ts +0 -7
  159. package/dist/eval/tools/index.js +0 -35
  160. package/dist/eval/tools/read.d.ts +0 -2
  161. package/dist/eval/tools/read.js +0 -122
  162. package/dist/eval/tools/types.d.ts +0 -49
  163. package/dist/eval/tools/types.js +0 -41
  164. package/dist/eval/tools/write.d.ts +0 -2
  165. package/dist/eval/tools/write.js +0 -92
  166. package/dist/eval/types.d.ts +0 -561
  167. package/dist/eval/types.js +0 -47
  168. package/dist/eval/verifiers/judge.d.ts +0 -40
  169. package/dist/eval/verifiers/judge.js +0 -256
  170. package/dist/eval/verifiers/rules.d.ts +0 -24
  171. package/dist/eval/verifiers/rules.js +0 -218
  172. package/dist/eval/verifiers/structural.d.ts +0 -14
  173. package/dist/eval/verifiers/structural.js +0 -171
  174. package/dist/eval/verifiers/traceability.d.ts +0 -23
  175. package/dist/eval/verifiers/traceability.js +0 -84
  176. package/dist/eval/verifiers/workflow-consistency.d.ts +0 -21
  177. package/dist/eval/verifiers/workflow-consistency.js +0 -225
  178. package/dist/eval/workflow-corpus.d.ts +0 -7
  179. package/dist/eval/workflow-corpus.js +0 -207
  180. package/dist/feature-system.d.ts +0 -42
  181. package/dist/feature-system.js +0 -432
  182. package/dist/internal/knowledge-digest.d.ts +0 -7
  183. package/dist/internal/knowledge-digest.js +0 -93
@@ -1,23 +0,0 @@
1
- /**
2
- * Cross-stage traceability verifier: extract a set of IDs from a source
3
- * fixture (e.g. `D-\d+` decisions declared during scope) and assert every
4
- * ID appears in the artifact-under-test and/or in other linked fixtures.
5
- *
6
- * The verifier is intentionally source-agnostic: the caller passes the
7
- * primary artifact plus a label → text map for any extra fixtures declared
8
- * on the case. `source` and entries in `requireIn` are either the string
9
- * `"self"` (the primary artifact) or labels present in the extras map.
10
- *
11
- * Result ids follow `traceability:<source>->:<target>:<reason>` so baselines
12
- * diff at the per-link granularity. A missing link produces one result with
13
- * a list of missing IDs in its `details` payload.
14
- */
15
- import type { TraceabilityExpected, VerifierResult } from "../types.js";
16
- export declare const SELF_LABEL = "self";
17
- /**
18
- * Run traceability checks. Returns `[]` when expectations are undefined.
19
- * Emits a single "source-missing" result when the declared source fixture
20
- * has zero IDs (authoring error), and one result per `requireIn` target
21
- * listing any IDs absent in that fixture.
22
- */
23
- export declare function verifyTraceability(primaryArtifact: string, extraFixtures: Record<string, string>, expected: TraceabilityExpected | undefined): VerifierResult[];
@@ -1,84 +0,0 @@
1
- import { splitFrontmatter } from "./structural.js";
2
- export const SELF_LABEL = "self";
3
- function result(id, ok, message, details) {
4
- return {
5
- kind: "rules",
6
- id,
7
- ok,
8
- score: ok ? 1 : 0,
9
- message,
10
- ...(details !== undefined ? { details } : {})
11
- };
12
- }
13
- function compileIdRegex(expected) {
14
- const flags = expected.idFlags ?? "g";
15
- const normalized = flags.includes("g") ? flags : `${flags}g`;
16
- try {
17
- return new RegExp(expected.idPattern, normalized);
18
- }
19
- catch (err) {
20
- throw new Error(`Invalid traceability id_pattern ${JSON.stringify(expected.idPattern)} ` +
21
- `(flags=${JSON.stringify(normalized)}): ` +
22
- (err instanceof Error ? err.message : String(err)));
23
- }
24
- }
25
- function bodyOf(text) {
26
- return splitFrontmatter(text).body;
27
- }
28
- function extractIds(text, regex) {
29
- const body = bodyOf(text);
30
- const found = new Set();
31
- for (const match of body.matchAll(regex)) {
32
- found.add(match[0]);
33
- }
34
- return [...found].sort();
35
- }
36
- function resolveFixture(label, primary, extraFixtures) {
37
- if (label === SELF_LABEL)
38
- return primary;
39
- return extraFixtures[label];
40
- }
41
- /**
42
- * Run traceability checks. Returns `[]` when expectations are undefined.
43
- * Emits a single "source-missing" result when the declared source fixture
44
- * has zero IDs (authoring error), and one result per `requireIn` target
45
- * listing any IDs absent in that fixture.
46
- */
47
- export function verifyTraceability(primaryArtifact, extraFixtures, expected) {
48
- if (!expected)
49
- return [];
50
- const regex = compileIdRegex(expected);
51
- const sourceText = resolveFixture(expected.source, primaryArtifact, extraFixtures);
52
- if (sourceText === undefined) {
53
- return [
54
- result(`traceability:source:${expected.source}:missing`, false, `Traceability source fixture "${expected.source}" not loaded.`, { source: expected.source })
55
- ];
56
- }
57
- const sourceIds = extractIds(sourceText, regex);
58
- if (sourceIds.length === 0) {
59
- return [
60
- result(`traceability:source:${expected.source}:empty`, false, `Source "${expected.source}" yielded zero ids for pattern /${expected.idPattern}/.`, { source: expected.source, pattern: expected.idPattern })
61
- ];
62
- }
63
- const results = [];
64
- for (const target of expected.requireIn) {
65
- const targetText = resolveFixture(target, primaryArtifact, extraFixtures);
66
- if (targetText === undefined) {
67
- results.push(result(`traceability:target:${target}:missing`, false, `Traceability target fixture "${target}" not loaded.`, { target }));
68
- continue;
69
- }
70
- const targetBody = bodyOf(targetText);
71
- const missing = sourceIds.filter((id) => !targetBody.includes(id));
72
- const ok = missing.length === 0;
73
- results.push(result(`traceability:${expected.source}->${target}`, ok, ok
74
- ? `Every id (${sourceIds.length}) from "${expected.source}" appears in "${target}".`
75
- : `Target "${target}" is missing ${missing.length}/${sourceIds.length} id(s): ${missing.join(", ")}.`, {
76
- source: expected.source,
77
- target,
78
- sourceIds,
79
- missing,
80
- pattern: expected.idPattern
81
- }));
82
- }
83
- return results;
84
- }
@@ -1,21 +0,0 @@
1
- /**
2
- * Cross-artifact consistency verifier for workflow mode.
3
- *
4
- * Operates over a `{ stage → artifact }` map produced by the workflow
5
- * agent and emits deterministic verifier results for:
6
- *
7
- * - `ids_flow`: every id extracted from `from` must appear in every
8
- * `to` stage. Typical use — `D-\d+` from scope must all land in plan.
9
- * - `placeholder_free`: none of the listed phrases
10
- * (default `TBD`/`TODO`/`placeholder`) appear in any of the named
11
- * stages.
12
- * - `no_contradictions`: for each entry, if `must` is present in the
13
- * declaring stage, `forbid` must not appear in any of the listed
14
- * `stages`.
15
- *
16
- * Each sub-check contributes zero or more `VerifierResult`s with
17
- * `kind: "consistency"`. An empty `WorkflowConsistencyExpected` produces
18
- * zero results so authors can opt in incrementally.
19
- */
20
- import type { VerifierResult, WorkflowConsistencyExpected, WorkflowStageName } from "../types.js";
21
- export declare function verifyWorkflowConsistency(artifacts: Map<WorkflowStageName, string>, expected: WorkflowConsistencyExpected | undefined): VerifierResult[];
@@ -1,225 +0,0 @@
1
- const DEFAULT_PLACEHOLDERS = ["TBD", "TODO", "placeholder"];
2
- export function verifyWorkflowConsistency(artifacts, expected) {
3
- if (!expected)
4
- return [];
5
- const out = [];
6
- if (expected.idsFlow) {
7
- for (const rule of expected.idsFlow) {
8
- out.push(...checkIdsFlow(artifacts, rule));
9
- }
10
- }
11
- if (expected.placeholderFree) {
12
- out.push(...checkPlaceholderFree(artifacts, expected.placeholderFree.stages, expected.placeholderFree.phrases && expected.placeholderFree.phrases.length > 0
13
- ? expected.placeholderFree.phrases
14
- : DEFAULT_PLACEHOLDERS));
15
- }
16
- if (expected.noContradictions) {
17
- for (const rule of expected.noContradictions) {
18
- out.push(...checkNoContradiction(artifacts, rule));
19
- }
20
- }
21
- return out;
22
- }
23
- function slug(value) {
24
- return value
25
- .toLowerCase()
26
- .replace(/[^a-z0-9]+/g, "-")
27
- .replace(/^-+|-+$/g, "")
28
- .slice(0, 48);
29
- }
30
- function missingStage(artifacts, stage, verifierId, label) {
31
- if (artifacts.has(stage))
32
- return undefined;
33
- return {
34
- kind: "consistency",
35
- id: verifierId,
36
- ok: false,
37
- score: 0,
38
- message: `Workflow artifact for stage "${stage}" is missing (${label}).`,
39
- details: { stage, missing: true }
40
- };
41
- }
42
- function extractIds(text, pattern, flags) {
43
- const normalized = flags.includes("g") ? flags : `${flags}g`;
44
- const regex = new RegExp(pattern, normalized);
45
- const hits = new Set();
46
- let match;
47
- while ((match = regex.exec(text)) !== null) {
48
- hits.add(match[0]);
49
- if (regex.lastIndex === match.index)
50
- regex.lastIndex += 1;
51
- }
52
- return [...hits].sort((a, b) => a.localeCompare(b));
53
- }
54
- function checkIdsFlow(artifacts, rule) {
55
- const idTag = slug(rule.idPattern);
56
- const baseId = `consistency:ids-flow:${rule.from}:${idTag}`;
57
- const results = [];
58
- const missingFrom = missingStage(artifacts, rule.from, `${baseId}:source-missing`, "ids-flow source");
59
- if (missingFrom) {
60
- results.push(missingFrom);
61
- return results;
62
- }
63
- const source = artifacts.get(rule.from);
64
- let sourceIds;
65
- try {
66
- sourceIds = extractIds(source, rule.idPattern, rule.idFlags ?? "g");
67
- }
68
- catch (err) {
69
- results.push({
70
- kind: "consistency",
71
- id: `${baseId}:regex`,
72
- ok: false,
73
- score: 0,
74
- message: `Invalid id regex "${rule.idPattern}": ${err instanceof Error ? err.message : String(err)}`,
75
- details: { from: rule.from }
76
- });
77
- return results;
78
- }
79
- if (sourceIds.length === 0) {
80
- results.push({
81
- kind: "consistency",
82
- id: `${baseId}:source-empty`,
83
- ok: false,
84
- score: 0,
85
- message: `No ids matched "${rule.idPattern}" in stage "${rule.from}".`,
86
- details: { from: rule.from, pattern: rule.idPattern }
87
- });
88
- return results;
89
- }
90
- for (const target of rule.to) {
91
- const missingTarget = missingStage(artifacts, target, `${baseId}:${target}:target-missing`, "ids-flow target");
92
- if (missingTarget) {
93
- results.push(missingTarget);
94
- continue;
95
- }
96
- const body = artifacts.get(target);
97
- const missing = sourceIds.filter((id) => !body.includes(id));
98
- const verifierId = `${baseId}:${target}`;
99
- if (missing.length === 0) {
100
- results.push({
101
- kind: "consistency",
102
- id: verifierId,
103
- ok: true,
104
- score: 1,
105
- message: `All ${sourceIds.length} id(s) from "${rule.from}" appear in "${target}".`,
106
- details: { from: rule.from, to: target, ids: sourceIds }
107
- });
108
- }
109
- else {
110
- results.push({
111
- kind: "consistency",
112
- id: verifierId,
113
- ok: false,
114
- score: 0,
115
- message: `Missing in "${target}": ${missing.slice(0, 5).join(", ")}` +
116
- (missing.length > 5 ? ` (+${missing.length - 5} more)` : ""),
117
- details: {
118
- from: rule.from,
119
- to: target,
120
- ids: sourceIds,
121
- missing
122
- }
123
- });
124
- }
125
- }
126
- return results;
127
- }
128
- function checkPlaceholderFree(artifacts, stages, phrases) {
129
- const results = [];
130
- for (const stage of stages) {
131
- const verifierId = `consistency:placeholder-free:${stage}`;
132
- const missing = missingStage(artifacts, stage, verifierId, "placeholder-free");
133
- if (missing) {
134
- results.push(missing);
135
- continue;
136
- }
137
- const body = artifacts.get(stage);
138
- const lower = body.toLowerCase();
139
- const hits = phrases.filter((p) => lower.includes(p.toLowerCase()));
140
- if (hits.length === 0) {
141
- results.push({
142
- kind: "consistency",
143
- id: verifierId,
144
- ok: true,
145
- score: 1,
146
- message: `No placeholder phrases found in "${stage}".`,
147
- details: { stage, phrases }
148
- });
149
- }
150
- else {
151
- results.push({
152
- kind: "consistency",
153
- id: verifierId,
154
- ok: false,
155
- score: 0,
156
- message: `Placeholder phrases in "${stage}": ${hits.join(", ")}.`,
157
- details: { stage, phrases, hits }
158
- });
159
- }
160
- }
161
- return results;
162
- }
163
- function checkNoContradiction(artifacts, rule) {
164
- const tag = `${slug(rule.must)}-vs-${slug(rule.forbid)}`;
165
- const baseId = `consistency:no-contradiction:${rule.stage}:${tag}`;
166
- const results = [];
167
- const missingAnchor = missingStage(artifacts, rule.stage, `${baseId}:anchor-missing`, "no-contradiction anchor");
168
- if (missingAnchor) {
169
- results.push(missingAnchor);
170
- return results;
171
- }
172
- const anchorText = artifacts.get(rule.stage);
173
- if (!anchorText.toLowerCase().includes(rule.must.toLowerCase())) {
174
- // The declaring stage doesn't actually assert `must`, so the rule is vacuously satisfied.
175
- results.push({
176
- kind: "consistency",
177
- id: `${baseId}:anchor-inactive`,
178
- ok: true,
179
- score: 1,
180
- message: `Anchor "${rule.must}" not present in "${rule.stage}"; contradiction check skipped.`,
181
- details: { stage: rule.stage, anchor: rule.must, skipped: true }
182
- });
183
- return results;
184
- }
185
- for (const target of rule.stages) {
186
- const verifierId = `${baseId}:${target}`;
187
- const missingTarget = missingStage(artifacts, target, `${verifierId}:target-missing`, "no-contradiction target");
188
- if (missingTarget) {
189
- results.push(missingTarget);
190
- continue;
191
- }
192
- const body = artifacts.get(target);
193
- if (body.toLowerCase().includes(rule.forbid.toLowerCase())) {
194
- results.push({
195
- kind: "consistency",
196
- id: verifierId,
197
- ok: false,
198
- score: 0,
199
- message: `"${rule.stage}" asserts "${rule.must}" but "${target}" contains "${rule.forbid}".`,
200
- details: {
201
- stage: rule.stage,
202
- anchor: rule.must,
203
- forbid: rule.forbid,
204
- target
205
- }
206
- });
207
- }
208
- else {
209
- results.push({
210
- kind: "consistency",
211
- id: verifierId,
212
- ok: true,
213
- score: 1,
214
- message: `"${target}" does not contradict "${rule.stage}" on "${rule.must}".`,
215
- details: {
216
- stage: rule.stage,
217
- anchor: rule.must,
218
- forbid: rule.forbid,
219
- target
220
- }
221
- });
222
- }
223
- }
224
- return results;
225
- }
@@ -1,7 +0,0 @@
1
- import type { WorkflowCase } from "./types.js";
2
- /**
3
- * Load every workflow-mode case under
4
- * `.cclaw/evals/corpus/workflows/*.yaml`. Returns an empty array when the
5
- * directory is missing — a fresh `cclaw init` has no workflow corpus yet.
6
- */
7
- export declare function loadWorkflowCorpus(projectRoot: string): Promise<WorkflowCase[]>;
@@ -1,207 +0,0 @@
1
- /**
2
- * Workflow corpus loader (workflow mode).
3
- *
4
- * Workflow-mode cases live under `.cclaw/evals/corpus/workflows/<id>.yaml` and
5
- * describe a multi-stage run that chains the with-tools agent across
6
- * `brainstorm → scope → design → spec → plan`. Unlike single-stage
7
- * cases (which are keyed by stage folder), workflow cases ship as a
8
- * single YAML that embeds each stage's prompt + expectations.
9
- *
10
- * The loader is intentionally separate from `loadCorpus` so the
11
- * structural / rules CI paths never walk the workflow directory — those
12
- * paths are single-stage only.
13
- */
14
- import fs from "node:fs/promises";
15
- import path from "node:path";
16
- import { parse } from "yaml";
17
- import { EVALS_ROOT } from "../constants.js";
18
- import { exists } from "../fs-utils.js";
19
- import { WORKFLOW_STAGES } from "./types.js";
20
- const WORKFLOW_STAGE_SET = new Set(WORKFLOW_STAGES);
21
- function workflowCorpusError(filePath, reason) {
22
- return new Error(`Invalid workflow case at ${filePath}: ${reason}\n` +
23
- `Supported workflow stages: ${WORKFLOW_STAGES.join(", ")}`);
24
- }
25
- function isRecord(value) {
26
- return typeof value === "object" && value !== null && !Array.isArray(value);
27
- }
28
- function readStringArray(filePath, context, value) {
29
- if (value === undefined)
30
- return undefined;
31
- if (!Array.isArray(value) || value.some((item) => typeof item !== "string")) {
32
- throw workflowCorpusError(filePath, `"${context}" must be an array of strings`);
33
- }
34
- return value;
35
- }
36
- function parseStageName(filePath, context, value) {
37
- if (typeof value !== "string" || !WORKFLOW_STAGE_SET.has(value)) {
38
- throw workflowCorpusError(filePath, `"${context}" must be one of: ${WORKFLOW_STAGES.join(", ")}`);
39
- }
40
- return value;
41
- }
42
- function parseStageArray(filePath, context, value) {
43
- if (!Array.isArray(value) || value.length === 0) {
44
- throw workflowCorpusError(filePath, `"${context}" must be a non-empty array of stage names`);
45
- }
46
- return value.map((entry, index) => parseStageName(filePath, `${context}[${index}]`, entry));
47
- }
48
- function parseStageStep(filePath, index, raw) {
49
- if (!isRecord(raw)) {
50
- throw workflowCorpusError(filePath, `stages[${index}] must be a mapping`);
51
- }
52
- const name = parseStageName(filePath, `stages[${index}].name`, raw.name);
53
- const inputPrompt = raw.input_prompt ?? raw.inputPrompt;
54
- if (typeof inputPrompt !== "string" || inputPrompt.trim().length === 0) {
55
- throw workflowCorpusError(filePath, `stages[${index}].input_prompt must be a non-empty string`);
56
- }
57
- const step = { name, inputPrompt: inputPrompt.trim() };
58
- if (raw.rubric !== undefined) {
59
- if (typeof raw.rubric !== "string" || raw.rubric.trim().length === 0) {
60
- throw workflowCorpusError(filePath, `stages[${index}].rubric must be a non-empty string`);
61
- }
62
- step.rubric = raw.rubric.trim();
63
- }
64
- const requiredChecks = readStringArray(filePath, `stages[${index}].required_checks`, raw.required_checks ?? raw.requiredChecks);
65
- if (requiredChecks)
66
- step.requiredChecks = requiredChecks;
67
- const minScoresRaw = raw.minimum_scores ?? raw.minimumScores;
68
- if (minScoresRaw !== undefined) {
69
- if (!isRecord(minScoresRaw)) {
70
- throw workflowCorpusError(filePath, `stages[${index}].minimum_scores must be a mapping of check id → number`);
71
- }
72
- const minimumScores = {};
73
- for (const [key, val] of Object.entries(minScoresRaw)) {
74
- if (typeof val !== "number" || !Number.isFinite(val) || val < 1 || val > 5) {
75
- throw workflowCorpusError(filePath, `stages[${index}].minimum_scores.${key} must be a number in [1,5]`);
76
- }
77
- minimumScores[key] = val;
78
- }
79
- step.minimumScores = minimumScores;
80
- }
81
- return step;
82
- }
83
- function parseConsistency(filePath, raw) {
84
- if (raw === undefined)
85
- return undefined;
86
- if (!isRecord(raw)) {
87
- throw workflowCorpusError(filePath, `"consistency" must be a mapping`);
88
- }
89
- const out = {};
90
- const idsFlowRaw = raw.ids_flow ?? raw.idsFlow;
91
- if (idsFlowRaw !== undefined) {
92
- if (!Array.isArray(idsFlowRaw)) {
93
- throw workflowCorpusError(filePath, `"consistency.ids_flow" must be an array`);
94
- }
95
- out.idsFlow = idsFlowRaw.map((entry, index) => {
96
- if (!isRecord(entry)) {
97
- throw workflowCorpusError(filePath, `consistency.ids_flow[${index}] must be a mapping`);
98
- }
99
- const idPattern = entry.id_pattern ?? entry.idPattern;
100
- if (typeof idPattern !== "string" || idPattern.length === 0) {
101
- throw workflowCorpusError(filePath, `consistency.ids_flow[${index}].id_pattern must be a non-empty regex source`);
102
- }
103
- const idFlags = entry.id_flags ?? entry.idFlags;
104
- if (idFlags !== undefined && typeof idFlags !== "string") {
105
- throw workflowCorpusError(filePath, `consistency.ids_flow[${index}].id_flags must be a string`);
106
- }
107
- const from = parseStageName(filePath, `consistency.ids_flow[${index}].from`, entry.from);
108
- const to = parseStageArray(filePath, `consistency.ids_flow[${index}].to`, entry.to);
109
- const result = { idPattern, from, to };
110
- if (idFlags !== undefined)
111
- result.idFlags = idFlags;
112
- return result;
113
- });
114
- }
115
- const placeholderRaw = raw.placeholder_free ?? raw.placeholderFree;
116
- if (placeholderRaw !== undefined) {
117
- if (!isRecord(placeholderRaw)) {
118
- throw workflowCorpusError(filePath, `"consistency.placeholder_free" must be a mapping`);
119
- }
120
- const stages = parseStageArray(filePath, "consistency.placeholder_free.stages", placeholderRaw.stages);
121
- const phrases = readStringArray(filePath, "consistency.placeholder_free.phrases", placeholderRaw.phrases);
122
- const block = { stages };
123
- if (phrases)
124
- block.phrases = phrases;
125
- out.placeholderFree = block;
126
- }
127
- const noContradictionsRaw = raw.no_contradictions ?? raw.noContradictions;
128
- if (noContradictionsRaw !== undefined) {
129
- if (!Array.isArray(noContradictionsRaw)) {
130
- throw workflowCorpusError(filePath, `"consistency.no_contradictions" must be an array`);
131
- }
132
- out.noContradictions = noContradictionsRaw.map((entry, index) => {
133
- if (!isRecord(entry)) {
134
- throw workflowCorpusError(filePath, `consistency.no_contradictions[${index}] must be a mapping`);
135
- }
136
- const stage = parseStageName(filePath, `consistency.no_contradictions[${index}].stage`, entry.stage);
137
- if (typeof entry.must !== "string" || entry.must.length === 0) {
138
- throw workflowCorpusError(filePath, `consistency.no_contradictions[${index}].must must be a non-empty string`);
139
- }
140
- if (typeof entry.forbid !== "string" || entry.forbid.length === 0) {
141
- throw workflowCorpusError(filePath, `consistency.no_contradictions[${index}].forbid must be a non-empty string`);
142
- }
143
- const stages = parseStageArray(filePath, `consistency.no_contradictions[${index}].stages`, entry.stages);
144
- return {
145
- stage,
146
- must: entry.must,
147
- forbid: entry.forbid,
148
- stages
149
- };
150
- });
151
- }
152
- return Object.keys(out).length === 0 ? undefined : out;
153
- }
154
- function validateWorkflowCase(filePath, raw) {
155
- if (!isRecord(raw)) {
156
- throw workflowCorpusError(filePath, `top-level value must be a mapping`);
157
- }
158
- const id = raw.id;
159
- if (typeof id !== "string" || id.trim().length === 0) {
160
- throw workflowCorpusError(filePath, `"id" must be a non-empty string`);
161
- }
162
- const stagesRaw = raw.stages;
163
- if (!Array.isArray(stagesRaw) || stagesRaw.length === 0) {
164
- throw workflowCorpusError(filePath, `"stages" must be a non-empty array`);
165
- }
166
- const stages = stagesRaw.map((entry, index) => parseStageStep(filePath, index, entry));
167
- const contextFiles = readStringArray(filePath, "context_files", raw.context_files ?? raw.contextFiles);
168
- const consistency = parseConsistency(filePath, raw.consistency);
169
- const description = typeof raw.description === "string" ? raw.description.trim() : undefined;
170
- const out = { id: id.trim(), stages };
171
- if (description)
172
- out.description = description;
173
- if (contextFiles)
174
- out.contextFiles = contextFiles;
175
- if (consistency)
176
- out.consistency = consistency;
177
- return out;
178
- }
179
- /**
180
- * Load every workflow-mode case under
181
- * `.cclaw/evals/corpus/workflows/*.yaml`. Returns an empty array when the
182
- * directory is missing — a fresh `cclaw init` has no workflow corpus yet.
183
- */
184
- export async function loadWorkflowCorpus(projectRoot) {
185
- const dir = path.join(projectRoot, EVALS_ROOT, "corpus", "workflows");
186
- if (!(await exists(dir)))
187
- return [];
188
- const entries = await fs.readdir(dir, { withFileTypes: true });
189
- const out = [];
190
- for (const entry of entries) {
191
- if (!entry.isFile())
192
- continue;
193
- if (!entry.name.endsWith(".yaml") && !entry.name.endsWith(".yml"))
194
- continue;
195
- const filePath = path.join(dir, entry.name);
196
- let parsed;
197
- try {
198
- parsed = parse(await fs.readFile(filePath, "utf8"));
199
- }
200
- catch (err) {
201
- throw workflowCorpusError(filePath, err instanceof Error ? err.message : String(err));
202
- }
203
- out.push(validateWorkflowCase(filePath, parsed));
204
- }
205
- out.sort((a, b) => a.id.localeCompare(b.id));
206
- return out;
207
- }
@@ -1,42 +0,0 @@
1
- export interface ActiveFeatureMeta {
2
- activeFeature: string;
3
- updatedAt: string;
4
- }
5
- export type FeatureWorkspaceSource = "git-worktree" | "workspace" | "legacy-snapshot";
6
- export interface FeatureWorkspaceEntry {
7
- featureId: string;
8
- branch: string;
9
- path: string;
10
- source: FeatureWorkspaceSource;
11
- createdAt: string;
12
- }
13
- export interface FeatureWorktreeRegistry {
14
- schemaVersion: 1;
15
- updatedAt: string;
16
- entries: FeatureWorkspaceEntry[];
17
- }
18
- export interface CreateFeatureOptions {
19
- cloneActive?: boolean;
20
- switchTo?: boolean;
21
- }
22
- export interface FeatureSystemAccessOptions {
23
- /**
24
- * When false, read metadata without auto-repair writes. Useful for pure
25
- * diagnostics (doctor) that should not mutate state as a side effect.
26
- */
27
- repair?: boolean;
28
- }
29
- export declare function activeFeatureMetaPath(projectRoot: string): string;
30
- export declare function worktreeRegistryPath(projectRoot: string): string;
31
- export declare function featureRootPath(projectRoot: string, featureId: string): string;
32
- export declare function featureArtifactsPath(projectRoot: string, featureId: string): string;
33
- export declare function featureStatePath(projectRoot: string, featureId: string): string;
34
- export declare function resolveFeatureWorkspacePath(projectRoot: string, entry: FeatureWorkspaceEntry): string;
35
- export declare function ensureFeatureSystem(projectRoot: string, options?: FeatureSystemAccessOptions): Promise<ActiveFeatureMeta>;
36
- export declare function readFeatureWorktreeRegistry(projectRoot: string, options?: FeatureSystemAccessOptions): Promise<FeatureWorktreeRegistry>;
37
- export declare function readActiveFeature(projectRoot: string, options?: FeatureSystemAccessOptions): Promise<string>;
38
- export declare function listFeatures(projectRoot: string, options?: FeatureSystemAccessOptions): Promise<string[]>;
39
- export declare function syncActiveFeatureSnapshot(projectRoot: string): Promise<void>;
40
- export declare function switchActiveFeature(projectRoot: string, featureId: string): Promise<ActiveFeatureMeta>;
41
- export declare function createFeature(projectRoot: string, rawFeatureId: string, options?: CreateFeatureOptions): Promise<string>;
42
- export declare function activeFeatureWorkspacePath(projectRoot: string): Promise<string>;