cclaw-cli 0.47.0 → 0.48.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.
- package/README.md +3 -1
- package/dist/artifact-linter.d.ts +7 -0
- package/dist/artifact-linter.js +43 -0
- package/dist/config.d.ts +6 -6
- package/dist/config.js +22 -0
- package/dist/constants.d.ts +10 -1
- package/dist/constants.js +19 -10
- package/dist/content/contracts.d.ts +1 -1
- package/dist/content/contracts.js +1 -1
- package/dist/content/{harnesses-doc.js → harness-doc.js} +32 -1
- package/dist/content/harness-playbooks.js +4 -4
- package/dist/content/ideate-command.js +19 -19
- package/dist/content/skills.js +2 -2
- package/dist/content/stage-schema.js +34 -8
- package/dist/content/stages/design.js +2 -2
- package/dist/content/stages/review.js +1 -1
- package/dist/content/stages/ship.js +2 -0
- package/dist/content/stages/tdd.js +8 -4
- package/dist/content/templates.js +4 -3
- package/dist/delegation.js +99 -33
- package/dist/doctor.js +77 -9
- package/dist/flow-state.d.ts +8 -0
- package/dist/flow-state.js +11 -8
- package/dist/gate-evidence.js +20 -1
- package/dist/harness-adapters.d.ts +2 -2
- package/dist/harness-adapters.js +2 -2
- package/dist/install.js +28 -6
- package/dist/internal/detect-public-api-changes.d.ts +5 -0
- package/dist/internal/detect-public-api-changes.js +45 -0
- package/dist/policy.js +3 -2
- package/dist/retro-gate.js +19 -2
- package/dist/run-persistence.js +5 -4
- package/dist/types.d.ts +6 -1
- package/package.json +4 -1
- /package/dist/content/{harnesses-doc.d.ts → harness-doc.d.ts} +0 -0
package/README.md
CHANGED
|
@@ -174,7 +174,7 @@ inside `/cc-ops` subcommands.
|
|
|
174
174
|
|---|---|
|
|
175
175
|
| **`/cc <idea>`** | Classify the task, discover origin docs (`docs/prd/**`, ADRs, root `PRD.md`, …), sniff the stack, recommend a track, then start the first stage of that track. `/cc` without arguments resumes the current flow. |
|
|
176
176
|
| **`/cc-next`** | The one progression primitive. Reads `flow-state.json`, checks gates + mandatory subagent delegations, and either resumes the current stage or advances to the next. `/cc-next` in a new session is how you **resume**. |
|
|
177
|
-
| **`/cc-ideate`** | Repository improvement
|
|
177
|
+
| **`/cc-ideate`** | Repository improvement ideate mode. Scans for TODOs, flaky tests, oversized modules, docs drift, and recurring knowledge-store lessons, **persists the ranked backlog** to `.cclaw/artifacts/ideate-<date>-<slug>.md`, and ends with a concrete handoff: launch `/cc` on the selected candidate in the same session, save-and-close, or discard. Resume check on next run reuses any ideate artifact younger than 30 days. Never mutates `flow-state.json`. |
|
|
178
178
|
| **`/cc-view`** | Read-only flow visibility. `/cc-view status` (default) shows stage progress, mandatory delegations with their fulfillment mode (isolated / generic-dispatch / role-switch), the ship closeout substate (retro → compound → archive), and the active harness parity row. `/cc-view tree` renders the same picture as a tree with a closeout sub-branch under ship and a per-harness playbook summary. `/cc-view diff` shows stage/gate/closeout/delegation deltas since the last run. Never mutates state (except diff's snapshot baseline). |
|
|
179
179
|
|
|
180
180
|
> Power-user surface: `/cc-ops` is an operational router for manual
|
|
@@ -458,6 +458,8 @@ CCLAW_EVAL_MODEL=glm-5.1 # default
|
|
|
458
458
|
|
|
459
459
|
Full details, corpus format, and the eval contract live in
|
|
460
460
|
[`docs/evals.md`](./docs/evals.md).
|
|
461
|
+
Mutation-testing setup lives in `stryker.config.mjs` and
|
|
462
|
+
`.github/workflows/mutation.yml` (manual + weekly run).
|
|
461
463
|
|
|
462
464
|
---
|
|
463
465
|
|
|
@@ -58,9 +58,16 @@ export interface ReviewVerdictConsistencyResult {
|
|
|
58
58
|
openCriticalCount: number;
|
|
59
59
|
shipBlockerCount: number;
|
|
60
60
|
}
|
|
61
|
+
export interface ReviewSecurityNoChangeAttestationResult {
|
|
62
|
+
ok: boolean;
|
|
63
|
+
errors: string[];
|
|
64
|
+
hasSecurityFinding: boolean;
|
|
65
|
+
hasNoChangeAttestation: boolean;
|
|
66
|
+
}
|
|
61
67
|
/**
|
|
62
68
|
* Ensure the narrative verdict in 07-review.md is consistent with the
|
|
63
69
|
* structured review-army reconciliation. A review cannot declare
|
|
64
70
|
* APPROVED while open Critical findings or shipBlockers remain.
|
|
65
71
|
*/
|
|
66
72
|
export declare function checkReviewVerdictConsistency(projectRoot: string): Promise<ReviewVerdictConsistencyResult>;
|
|
73
|
+
export declare function checkReviewSecurityNoChangeAttestation(projectRoot: string): Promise<ReviewSecurityNoChangeAttestationResult>;
|
package/dist/artifact-linter.js
CHANGED
|
@@ -1324,3 +1324,46 @@ export async function checkReviewVerdictConsistency(projectRoot) {
|
|
|
1324
1324
|
shipBlockerCount
|
|
1325
1325
|
};
|
|
1326
1326
|
}
|
|
1327
|
+
export async function checkReviewSecurityNoChangeAttestation(projectRoot) {
|
|
1328
|
+
const reviewMdPath = path.join(projectRoot, RUNTIME_ROOT, "artifacts", "07-review.md");
|
|
1329
|
+
if (!(await exists(reviewMdPath))) {
|
|
1330
|
+
return {
|
|
1331
|
+
ok: true,
|
|
1332
|
+
errors: [],
|
|
1333
|
+
hasSecurityFinding: false,
|
|
1334
|
+
hasNoChangeAttestation: false
|
|
1335
|
+
};
|
|
1336
|
+
}
|
|
1337
|
+
const errors = [];
|
|
1338
|
+
const raw = await fs.readFile(reviewMdPath, "utf8");
|
|
1339
|
+
const sections = extractH2Sections(raw);
|
|
1340
|
+
const securityBody = sectionBodyByName(sections, "Layer 2 Security")
|
|
1341
|
+
?? sectionBodyByName(sections, "Layer 2b: Security")
|
|
1342
|
+
?? sectionBodyByName(sections, "Layer 2 Findings");
|
|
1343
|
+
if (!securityBody) {
|
|
1344
|
+
errors.push('07-review.md is missing a Layer 2 security section.');
|
|
1345
|
+
return {
|
|
1346
|
+
ok: false,
|
|
1347
|
+
errors,
|
|
1348
|
+
hasSecurityFinding: false,
|
|
1349
|
+
hasNoChangeAttestation: false
|
|
1350
|
+
};
|
|
1351
|
+
}
|
|
1352
|
+
const securityTableRowPattern = /^\|\s*[^|\n]+\|\s*[^|\n]+\|\s*security\s*\|\s*[^|\n]+\|\s*[^|\n]+\|/imu;
|
|
1353
|
+
const securityBulletPattern = /^[*-]\s+.*\b(?:security|auth|injection|secret|credential|permission)\b/imu;
|
|
1354
|
+
const hasSecurityFinding = securityTableRowPattern.test(securityBody) || securityBulletPattern.test(securityBody);
|
|
1355
|
+
const attestationMatch = /NO_CHANGE_ATTESTATION\s*:\s*(.*)/iu.exec(securityBody);
|
|
1356
|
+
const hasNoChangeAttestation = Boolean(attestationMatch && attestationMatch[1]?.trim().length > 0);
|
|
1357
|
+
if (attestationMatch && attestationMatch[1]?.trim().length === 0) {
|
|
1358
|
+
errors.push("NO_CHANGE_ATTESTATION must include a non-empty rationale.");
|
|
1359
|
+
}
|
|
1360
|
+
if (!hasSecurityFinding && !hasNoChangeAttestation) {
|
|
1361
|
+
errors.push("Layer 2 security evidence missing: include at least one security finding or `NO_CHANGE_ATTESTATION: <reason>`.");
|
|
1362
|
+
}
|
|
1363
|
+
return {
|
|
1364
|
+
ok: errors.length === 0,
|
|
1365
|
+
errors,
|
|
1366
|
+
hasSecurityFinding,
|
|
1367
|
+
hasNoChangeAttestation
|
|
1368
|
+
};
|
|
1369
|
+
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FlowTrack, HarnessId, LanguageRulePack
|
|
1
|
+
import type { CclawConfig, FlowTrack, HarnessId, LanguageRulePack } from "./types.js";
|
|
2
2
|
export declare function configPath(projectRoot: string): string;
|
|
3
3
|
/**
|
|
4
4
|
* Default test-path patterns used by workflow-guard.sh to classify TDD writes.
|
|
@@ -20,7 +20,7 @@ export declare const DEFAULT_COMPOUND_RECURRENCE_THRESHOLD = 3;
|
|
|
20
20
|
* regardless of whether the user wrote `strictness`, the legacy keys, both,
|
|
21
21
|
* or neither.
|
|
22
22
|
*/
|
|
23
|
-
export declare function createDefaultConfig(harnesses?: HarnessId[], defaultTrack?: FlowTrack):
|
|
23
|
+
export declare function createDefaultConfig(harnesses?: HarnessId[], defaultTrack?: FlowTrack): CclawConfig;
|
|
24
24
|
/**
|
|
25
25
|
* Probe common project-root manifests to infer which language rule packs the
|
|
26
26
|
* user would reasonably want. Pure-functional best-effort: any filesystem
|
|
@@ -31,9 +31,9 @@ export declare function createDefaultConfig(harnesses?: HarnessId[], defaultTrac
|
|
|
31
31
|
* never surprise a user who intentionally cleared the list.
|
|
32
32
|
*/
|
|
33
33
|
export declare function detectLanguageRulePacks(projectRoot: string): Promise<LanguageRulePack[]>;
|
|
34
|
-
export declare function readConfig(projectRoot: string): Promise<
|
|
34
|
+
export declare function readConfig(projectRoot: string): Promise<CclawConfig>;
|
|
35
35
|
/**
|
|
36
|
-
* Fields that live on the populated runtime `
|
|
36
|
+
* Fields that live on the populated runtime `CclawConfig` but are considered
|
|
37
37
|
* "advanced" — we keep them in the in-memory object so downstream callers
|
|
38
38
|
* don't have to branch, but we do **not** write them to `config.yaml` unless
|
|
39
39
|
* the user set them explicitly. Keeps the default template small and honest:
|
|
@@ -43,7 +43,7 @@ type AdvancedConfigKey = "promptGuardMode" | "tddEnforcement" | "tddTestGlobs" |
|
|
|
43
43
|
/**
|
|
44
44
|
* Options controlling the serialisation shape of `config.yaml`.
|
|
45
45
|
*
|
|
46
|
-
* - `"full"` (default): write every field on the `
|
|
46
|
+
* - `"full"` (default): write every field on the `CclawConfig` object that
|
|
47
47
|
* isn't `undefined`. Preserves existing shapes and keeps legacy callers
|
|
48
48
|
* working without migration.
|
|
49
49
|
* - `"minimal"`: write only the user-facing knobs (`MINIMAL_CONFIG_KEYS`)
|
|
@@ -60,7 +60,7 @@ export interface WriteConfigOptions {
|
|
|
60
60
|
mode?: "full" | "minimal";
|
|
61
61
|
advancedKeysPresent?: ReadonlySet<AdvancedConfigKey>;
|
|
62
62
|
}
|
|
63
|
-
export declare function writeConfig(projectRoot: string, config:
|
|
63
|
+
export declare function writeConfig(projectRoot: string, config: CclawConfig, options?: WriteConfigOptions): Promise<void>;
|
|
64
64
|
/**
|
|
65
65
|
* Enumerate which advanced keys are currently set in the on-disk config.
|
|
66
66
|
* Used by `cclaw upgrade` to preserve the user's existing shape — if they
|
package/dist/config.js
CHANGED
|
@@ -44,6 +44,22 @@ const MINIMAL_CONFIG_KEYS = [
|
|
|
44
44
|
];
|
|
45
45
|
const DEFAULT_SLICE_REVIEW_THRESHOLD = 5;
|
|
46
46
|
const DEFAULT_SLICE_REVIEW_TRACKS = ["standard"];
|
|
47
|
+
const emittedConfigWarnings = new Set();
|
|
48
|
+
function emitConfigWarningOnce(code, message) {
|
|
49
|
+
const key = `${code}:${message}`;
|
|
50
|
+
if (emittedConfigWarnings.has(key)) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
emittedConfigWarnings.add(key);
|
|
54
|
+
process.emitWarning(message, { code });
|
|
55
|
+
}
|
|
56
|
+
function sameStringArray(a, b) {
|
|
57
|
+
if (!a || !b)
|
|
58
|
+
return false;
|
|
59
|
+
if (a.length !== b.length)
|
|
60
|
+
return false;
|
|
61
|
+
return a.every((value, index) => value === b[index]);
|
|
62
|
+
}
|
|
47
63
|
function configFixExample() {
|
|
48
64
|
return `harnesses:
|
|
49
65
|
- claude
|
|
@@ -244,6 +260,12 @@ export async function readConfig(projectRoot) {
|
|
|
244
260
|
explicitTddTestPathPatterns = validateStringArray(tddRaw.testPathPatterns, "tdd.testPathPatterns", fullPath);
|
|
245
261
|
explicitTddProductionPathPatterns = validateStringArray(tddRaw.productionPathPatterns, "tdd.productionPathPatterns", fullPath);
|
|
246
262
|
}
|
|
263
|
+
if (tddTestGlobsRaw !== undefined &&
|
|
264
|
+
explicitTddTestPathPatterns !== undefined &&
|
|
265
|
+
!sameStringArray(tddTestGlobs, explicitTddTestPathPatterns)) {
|
|
266
|
+
emitConfigWarningOnce("CCLAW_CONFIG_DEPRECATED_TDD_TEST_GLOBS", `[cclaw] Both "tddTestGlobs" (deprecated) and "tdd.testPathPatterns" are set in ${fullPath}. ` +
|
|
267
|
+
`Using "tdd.testPathPatterns".`);
|
|
268
|
+
}
|
|
247
269
|
const resolvedTddTestPathPatterns = [
|
|
248
270
|
...(explicitTddTestPathPatterns ?? tddTestGlobs ?? DEFAULT_TDD_TEST_PATH_PATTERNS)
|
|
249
271
|
];
|
package/dist/constants.d.ts
CHANGED
|
@@ -15,7 +15,16 @@ export declare const EVALS_CONFIG_PATH = ".cclaw/evals/config.yaml";
|
|
|
15
15
|
export declare const EVALS_DIRS: readonly [".cclaw/evals", ".cclaw/evals/corpus", ".cclaw/evals/rubrics", ".cclaw/evals/baselines", ".cclaw/evals/reports"];
|
|
16
16
|
export declare const REQUIRED_DIRS: readonly [".cclaw", ".cclaw/commands", ".cclaw/skills", ".cclaw/contexts", ".cclaw/templates", ".cclaw/artifacts", ".cclaw/worktrees", ".cclaw/state", ".cclaw/runs", ".cclaw/rules", ".cclaw/adapters", ".cclaw/agents", ".cclaw/hooks", ".cclaw/custom-skills", ".cclaw/evals", ".cclaw/evals/corpus", ".cclaw/evals/rubrics", ".cclaw/evals/baselines", ".cclaw/evals/reports"];
|
|
17
17
|
export declare const REQUIRED_GITIGNORE_PATTERNS: readonly ["# cclaw generated artifacts", ".cclaw/", "# cclaw evals: user-owned, track in git", "!.cclaw/evals/", "!.cclaw/evals/config.yaml", "!.cclaw/evals/corpus/", "!.cclaw/evals/corpus/**", "!.cclaw/evals/rubrics/", "!.cclaw/evals/rubrics/**", "!.cclaw/evals/baselines/", "!.cclaw/evals/baselines/**", ".claude/commands/cc-*.md", ".claude/commands/cc.md", ".cursor/commands/cc-*.md", ".cursor/commands/cc.md", ".opencode/commands/cc-*.md", ".opencode/commands/cc.md", ".agents/skills/cc/SKILL.md", ".agents/skills/cc-*/SKILL.md", ".claude/hooks/hooks.json", ".cursor/hooks.json", ".codex/hooks.json", ".opencode/plugins/cclaw-plugin.mjs", ".cursor/rules/cclaw-workflow.mdc"];
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Canonical stage -> skill folder mapping.
|
|
20
|
+
*
|
|
21
|
+
* Intentional divergence from stage ids:
|
|
22
|
+
* - stage ids stay short and flow-oriented (`spec`, `tdd`, `ship`)
|
|
23
|
+
* - skill folders stay descriptive and user-facing for `.cclaw/skills/*`.
|
|
24
|
+
*
|
|
25
|
+
* Keep this map as the single source of truth for generated skill paths.
|
|
26
|
+
*/
|
|
27
|
+
export declare const STAGE_TO_SKILL_FOLDER: Record<FlowStage, string>;
|
|
19
28
|
export declare const UTILITY_COMMANDS: readonly ["learn", "next", "ideate", "view", "status", "tree", "diff", "ops", "feature", "tdd-log", "retro", "compound", "archive", "rewind"];
|
|
20
29
|
export declare const SUBAGENT_SKILL_FOLDERS: readonly ["subagent-dev", "parallel-dispatch"];
|
|
21
30
|
export type UtilityCommand = (typeof UTILITY_COMMANDS)[number];
|
package/dist/constants.js
CHANGED
|
@@ -103,16 +103,25 @@ export const REQUIRED_GITIGNORE_PATTERNS = [
|
|
|
103
103
|
".opencode/plugins/cclaw-plugin.mjs",
|
|
104
104
|
".cursor/rules/cclaw-workflow.mdc"
|
|
105
105
|
];
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Canonical stage -> skill folder mapping.
|
|
108
|
+
*
|
|
109
|
+
* Intentional divergence from stage ids:
|
|
110
|
+
* - stage ids stay short and flow-oriented (`spec`, `tdd`, `ship`)
|
|
111
|
+
* - skill folders stay descriptive and user-facing for `.cclaw/skills/*`.
|
|
112
|
+
*
|
|
113
|
+
* Keep this map as the single source of truth for generated skill paths.
|
|
114
|
+
*/
|
|
115
|
+
export const STAGE_TO_SKILL_FOLDER = {
|
|
116
|
+
brainstorm: "brainstorming",
|
|
117
|
+
scope: "scope-shaping",
|
|
118
|
+
design: "engineering-design-lock",
|
|
119
|
+
spec: "specification-authoring",
|
|
120
|
+
plan: "planning-and-task-breakdown",
|
|
121
|
+
tdd: "test-driven-development",
|
|
122
|
+
review: "two-layer-review",
|
|
123
|
+
ship: "shipping-and-handoff"
|
|
124
|
+
};
|
|
116
125
|
export const UTILITY_COMMANDS = [
|
|
117
126
|
"learn",
|
|
118
127
|
"next",
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { FlowStage } from "../types.js";
|
|
2
|
-
export declare function
|
|
2
|
+
export declare function stageCommandContract(stage: FlowStage): string;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { stageSchema } from "./stage-schema.js";
|
|
2
2
|
import { stageSkillFolder } from "./skills.js";
|
|
3
|
-
export function
|
|
3
|
+
export function stageCommandContract(stage) {
|
|
4
4
|
const schema = stageSchema(stage);
|
|
5
5
|
const skillPath = `.cclaw/skills/${stageSkillFolder(stage)}/SKILL.md`;
|
|
6
6
|
const reads = schema.crossStageTrace.readsFrom;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { HARNESS_ADAPTERS, harnessTier } from "../harness-adapters.js";
|
|
2
|
+
import { STAGE_TO_SKILL_FOLDER } from "../constants.js";
|
|
2
3
|
import { HOOK_EVENTS_BY_HARNESS, HOOK_SEMANTIC_EVENTS } from "./hook-events.js";
|
|
3
4
|
import { HARNESS_PLAYBOOKS_DIR, harnessPlaybookFileName } from "./harness-playbooks.js";
|
|
4
5
|
import { HARNESS_TOOL_REFS_DIR } from "./harness-tool-refs.js";
|
|
@@ -23,6 +24,15 @@ function tierDescription(tier) {
|
|
|
23
24
|
}
|
|
24
25
|
export function harnessIntegrationDocMarkdown() {
|
|
25
26
|
const harnesses = Object.keys(HARNESS_ADAPTERS);
|
|
27
|
+
const stageSkillRows = Object.entries(STAGE_TO_SKILL_FOLDER)
|
|
28
|
+
.map(([stage, skillFolder]) => `| \`${stage}\` | \`${skillFolder}\` |`)
|
|
29
|
+
.join("\n");
|
|
30
|
+
const hookCasingRows = [
|
|
31
|
+
"| Claude Code | `claude` | PascalCase (`SessionStart`, `PreToolUse`) |",
|
|
32
|
+
"| Cursor | `cursor` | camelCase (`sessionStart`, `preToolUse`) |",
|
|
33
|
+
"| OpenCode | `opencode` | camelCase (`sessionStart`, `preToolUse`) |",
|
|
34
|
+
"| OpenAI Codex | `codex` | PascalCase (`SessionStart`, `PreToolUse`) |"
|
|
35
|
+
].join("\n");
|
|
26
36
|
const capabilityRows = harnesses
|
|
27
37
|
.map((harness) => {
|
|
28
38
|
const adapter = HARNESS_ADAPTERS[harness];
|
|
@@ -75,6 +85,17 @@ Design-stage research fleet uses the same parity model:
|
|
|
75
85
|
|---|---|---|---|---|
|
|
76
86
|
${hookRows}
|
|
77
87
|
|
|
88
|
+
## Hook event casing
|
|
89
|
+
|
|
90
|
+
Hook keys are intentionally harness-native and must not be normalized:
|
|
91
|
+
|
|
92
|
+
| Harness | ID | Event key casing |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
${hookCasingRows}
|
|
95
|
+
|
|
96
|
+
Use the exact event names from each harness schema. Treating all hooks as one
|
|
97
|
+
shared casing silently breaks generated wiring.
|
|
98
|
+
|
|
78
99
|
## Interpretation
|
|
79
100
|
|
|
80
101
|
- \`tier1\`: full native delegation + structured asks + full hook surface.
|
|
@@ -91,7 +112,7 @@ All harnesses receive the same utility commands:
|
|
|
91
112
|
|
|
92
113
|
- \`/cc\` - flow entry and resume
|
|
93
114
|
- \`/cc-next\` - stage progression
|
|
94
|
-
- \`/cc-ideate\` -
|
|
115
|
+
- \`/cc-ideate\` - ideate mode for ranked repo-improvement backlog
|
|
95
116
|
- \`/cc-view\` - read-only router for status/tree/diff
|
|
96
117
|
- \`/cc-ops\` - operations router for feature/tdd-log/retro/compound/archive/rewind
|
|
97
118
|
|
|
@@ -112,6 +133,16 @@ Operations subcommands:
|
|
|
112
133
|
Stage order remains canonical:
|
|
113
134
|
\`brainstorm -> scope -> design -> spec -> plan -> tdd -> review -> ship\`
|
|
114
135
|
|
|
136
|
+
## Stage -> skill folder mapping
|
|
137
|
+
|
|
138
|
+
| Stage | Skill folder |
|
|
139
|
+
|---|---|
|
|
140
|
+
${stageSkillRows}
|
|
141
|
+
|
|
142
|
+
This map is generated from \`src/constants.ts::STAGE_TO_SKILL_FOLDER\` so
|
|
143
|
+
skill-path naming stays explicit and stable even when stage ids differ from
|
|
144
|
+
folder names.
|
|
145
|
+
|
|
115
146
|
## Install surfaces
|
|
116
147
|
|
|
117
148
|
Always generated:
|
|
@@ -95,9 +95,9 @@ generic dispatcher with a strict role prompt.
|
|
|
95
95
|
## Dispatch pattern
|
|
96
96
|
|
|
97
97
|
1. Pick the mapped \`subagent_type\` from the table above.
|
|
98
|
-
2. Build the \`prompt\` from the cclaw agent
|
|
98
|
+
2. Build the \`prompt\` from the cclaw agent role brief in
|
|
99
99
|
\`.cclaw/agents/<agent>.md\`, prefaced with a single line naming the
|
|
100
|
-
cclaw role (\`You are the cclaw <agent>. Follow the
|
|
100
|
+
cclaw role (\`You are the cclaw <agent>. Follow the role brief below.\`).
|
|
101
101
|
3. Set \`readonly: true\` when the table says yes — Cursor enforces it.
|
|
102
102
|
4. Before dispatch, append a delegation row:
|
|
103
103
|
|
|
@@ -142,7 +142,7 @@ description: "OpenCode has plugin-based dispatch hooks and a native structured-a
|
|
|
142
142
|
**Fallback: role-switch.** OpenCode exposes tool/session event hooks via a
|
|
143
143
|
plugin but does not provide an isolated subagent worker. cclaw closes the
|
|
144
144
|
delegation gate by role-switching inside the same session: the agent
|
|
145
|
-
announces the role, performs the work against the
|
|
145
|
+
announces the role, performs the work against the role brief, and records
|
|
146
146
|
evidence.
|
|
147
147
|
|
|
148
148
|
**Structured ask: native \`question\` tool.** OpenCode ships a first-class
|
|
@@ -168,7 +168,7 @@ artifact decision log. Full mapping:
|
|
|
168
168
|
> Acting as cclaw **<agent>** per \`.cclaw/agents/<agent>.md\`. No other
|
|
169
169
|
> role may be assumed until the delegation row is closed.
|
|
170
170
|
|
|
171
|
-
2. Execute the role's
|
|
171
|
+
2. Execute the role's brief. Do NOT interleave other roles' work.
|
|
172
172
|
3. Write the result into the stage artifact (e.g. TDD work lands in
|
|
173
173
|
\`.cclaw/artifacts/06-tdd.md\`).
|
|
174
174
|
4. Append a delegation row:
|
|
@@ -2,13 +2,13 @@ import { RUNTIME_ROOT } from "../constants.js";
|
|
|
2
2
|
const IDEATE_SKILL_FOLDER = "flow-ideate";
|
|
3
3
|
const IDEATE_SKILL_NAME = "flow-ideate";
|
|
4
4
|
/**
|
|
5
|
-
* Directory + filename convention for
|
|
5
|
+
* Directory + filename convention for ideate artifacts. These are separate
|
|
6
6
|
* from stage artifacts (00-..08-*.md) because `/cc-ideate` runs outside the
|
|
7
7
|
* critical-path flow state machine and must not collide with stage numbering.
|
|
8
8
|
*/
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
9
|
+
const IDEATE_ARTIFACT_GLOB = ".cclaw/artifacts/ideate-*.md";
|
|
10
|
+
const IDEATE_ARTIFACT_PATTERN = ".cclaw/artifacts/ideate-<YYYY-MM-DD-slug>.md";
|
|
11
|
+
const IDEATE_RESUME_WINDOW_DAYS = 30;
|
|
12
12
|
/**
|
|
13
13
|
* Structured-ask tool list reused across cclaw skills. Kept inline here (small
|
|
14
14
|
* enough) to avoid cross-module coupling; larger stage skills cite the shared
|
|
@@ -23,25 +23,25 @@ export function ideateCommandContract() {
|
|
|
23
23
|
|
|
24
24
|
## Purpose
|
|
25
25
|
|
|
26
|
-
Repository-improvement
|
|
26
|
+
Repository-improvement ideate mode. Generate a ranked backlog of
|
|
27
27
|
high-value improvements, persist it as an artifact on disk, and end with
|
|
28
28
|
an explicit handoff — either launch \`/cc\` on a chosen candidate in the
|
|
29
29
|
same session, or save/discard the backlog.
|
|
30
30
|
|
|
31
31
|
## HARD-GATE
|
|
32
32
|
|
|
33
|
-
-
|
|
33
|
+
- Ideate mode only. Never mutate \`.cclaw/state/flow-state.json\`.
|
|
34
34
|
- Every recommendation cites evidence from the current repository
|
|
35
35
|
(file path, command output, or knowledge-store entry id).
|
|
36
36
|
- Always write a persisted artifact to
|
|
37
|
-
\`${
|
|
37
|
+
\`${IDEATE_ARTIFACT_PATTERN}\`. Chat-only output is not acceptable —
|
|
38
38
|
the next session must be able to resume.
|
|
39
39
|
- Always end with a structured handoff prompt, not an open question.
|
|
40
40
|
|
|
41
41
|
## Algorithm
|
|
42
42
|
|
|
43
|
-
1. **Resume check.** Glob \`${
|
|
44
|
-
has been modified within the last ${
|
|
43
|
+
1. **Resume check.** Glob \`${IDEATE_ARTIFACT_GLOB}\`. If any artifact
|
|
44
|
+
has been modified within the last ${IDEATE_RESUME_WINDOW_DAYS} days,
|
|
45
45
|
offer the user: continue that backlog, start fresh, or cancel.
|
|
46
46
|
2. **Scan repo signals:**
|
|
47
47
|
- open TODO/FIXME/XXX/HACK notes,
|
|
@@ -54,7 +54,7 @@ same session, or save/discard the backlog.
|
|
|
54
54
|
per candidate.
|
|
55
55
|
4. **Rank by impact/effort**, recommend the top item.
|
|
56
56
|
5. **Write the artifact** at
|
|
57
|
-
\`${
|
|
57
|
+
\`${IDEATE_ARTIFACT_PATTERN}\` using the schema in the skill.
|
|
58
58
|
6. **Present the handoff prompt** with four concrete options — not A/B/C
|
|
59
59
|
letters. Default = "Start /cc on the top recommendation".
|
|
60
60
|
|
|
@@ -66,7 +66,7 @@ same session, or save/discard the backlog.
|
|
|
66
66
|
export function ideateCommandSkillMarkdown() {
|
|
67
67
|
return `---
|
|
68
68
|
name: ${IDEATE_SKILL_NAME}
|
|
69
|
-
description: "Repository
|
|
69
|
+
description: "Repository ideate mode: detect and rank high-leverage improvements, persist a backlog artifact, and hand off to /cc or save/discard."
|
|
70
70
|
---
|
|
71
71
|
|
|
72
72
|
# /cc-ideate
|
|
@@ -75,12 +75,12 @@ description: "Repository ideation mode: detect and rank high-leverage improvemen
|
|
|
75
75
|
|
|
76
76
|
"Using flow-ideate to identify highest-leverage improvements in this
|
|
77
77
|
repository. Will persist a ranked backlog to
|
|
78
|
-
\`${
|
|
78
|
+
\`${IDEATE_ARTIFACT_PATTERN}\` and end with an explicit handoff."
|
|
79
79
|
|
|
80
80
|
## HARD-GATE
|
|
81
81
|
|
|
82
82
|
- Do not start coding in ideate mode.
|
|
83
|
-
- Do not mutate \`.cclaw/state/flow-state.json\` —
|
|
83
|
+
- Do not mutate \`.cclaw/state/flow-state.json\` — ideate mode sits outside
|
|
84
84
|
the critical-path flow.
|
|
85
85
|
- Always produce the artifact file on disk before presenting the handoff.
|
|
86
86
|
- Always end with a structured handoff that names the concrete follow-up
|
|
@@ -91,12 +91,12 @@ repository. Will persist a ranked backlog to
|
|
|
91
91
|
### Phase 0 — Resume check
|
|
92
92
|
|
|
93
93
|
1. Use the harness's file-glob tool (\`Glob\` pattern
|
|
94
|
-
\`${
|
|
95
|
-
2. Filter to files modified within the last ${
|
|
94
|
+
\`${IDEATE_ARTIFACT_GLOB}\` or equivalent \`ls\`/\`find\`).
|
|
95
|
+
2. Filter to files modified within the last ${IDEATE_RESUME_WINDOW_DAYS} days.
|
|
96
96
|
3. If one or more match, present **one** structured ask using the
|
|
97
97
|
harness's native tool (${STRUCTURED_ASK_TOOLS}) with options:
|
|
98
98
|
- **Continue the existing backlog** — read the most-recent
|
|
99
|
-
|
|
99
|
+
ideate-*.md and work from its candidate list; skip re-scanning.
|
|
100
100
|
- **Start a fresh scan** — proceed to Phase 1; the old artifact stays
|
|
101
101
|
on disk for history.
|
|
102
102
|
- **Cancel** — stop; do not scan or write anything.
|
|
@@ -137,10 +137,10 @@ Aim for 5–10 candidates. Do not invent candidates without evidence.
|
|
|
137
137
|
1. Sort by impact/effort ratio; break ties with confidence.
|
|
138
138
|
2. Compute the artifact filename:
|
|
139
139
|
- \`slug\` = first 3–5 words of the top recommendation, lowercase,
|
|
140
|
-
non-alphanumeric collapsed to \`-\`, trimmed. When
|
|
140
|
+
non-alphanumeric collapsed to \`-\`, trimmed. When ideate mode is
|
|
141
141
|
focus-hinted (user passed an argument), use the focus hint instead.
|
|
142
142
|
- \`date\` = today in \`YYYY-MM-DD\` (local time).
|
|
143
|
-
- Path = \`.cclaw/artifacts/
|
|
143
|
+
- Path = \`.cclaw/artifacts/ideate-<date>-<slug>.md\`.
|
|
144
144
|
3. Use the harness's write-file tool (\`Write\`, \`apply_patch\`, or shell
|
|
145
145
|
\`cat <<EOF > path\`) to create the artifact with this schema:
|
|
146
146
|
|
|
@@ -195,7 +195,7 @@ lettered list with the same four labels. Do not invent extra options.
|
|
|
195
195
|
- **Start /cc on I-1** or **different candidate:** announce
|
|
196
196
|
"Handing off to /cc <phrase>" and load the \`using-cclaw\` router
|
|
197
197
|
skill. From there, the normal \`/cc\` classification and stage flow
|
|
198
|
-
takes over. Do not produce a second artifact; the
|
|
198
|
+
takes over. Do not produce a second artifact; the ideate file is
|
|
199
199
|
preserved as the origin document for this run.
|
|
200
200
|
- **Save and close:** reply with the artifact path and stop.
|
|
201
201
|
- **Discard:** delete the artifact file, confirm deletion, stop.
|
package/dist/content/skills.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { RUNTIME_ROOT } from "../constants.js";
|
|
1
|
+
import { RUNTIME_ROOT, STAGE_TO_SKILL_FOLDER } from "../constants.js";
|
|
2
2
|
import { STAGE_EXAMPLES_REFERENCE_DIR, stageDomainExamples, stageExamples, stageGoodBadExamples } from "./examples.js";
|
|
3
3
|
import { STAGE_COMMON_GUIDANCE_REL_PATH } from "./stage-common-guidance.js";
|
|
4
4
|
import { stageAutoSubagentDispatch, stageSchema } from "./stage-schema.js";
|
|
@@ -295,7 +295,7 @@ After T-3 REFACTOR, before declaring Batch 1 done:
|
|
|
295
295
|
- The same RED failure reappears after a GREEN change → **escalate** per the 3-attempts rule; do not keep patching.
|
|
296
296
|
`;
|
|
297
297
|
export function stageSkillFolder(stage) {
|
|
298
|
-
return
|
|
298
|
+
return STAGE_TO_SKILL_FOLDER[stage];
|
|
299
299
|
}
|
|
300
300
|
export function stageSkillMarkdown(stage, track = "standard") {
|
|
301
301
|
const schema = stageSchema(stage, track);
|
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { FLOW_STAGES, FLOW_TRACKS, TRACK_STAGES } from "../types.js";
|
|
2
|
+
import { STAGE_TO_SKILL_FOLDER } from "../constants.js";
|
|
3
3
|
import { BRAINSTORM, SCOPE, DESIGN, SPEC, PLAN, TDD, REVIEW, SHIP } from "./stages/index.js";
|
|
4
4
|
import { tddStageForTrack } from "./stages/tdd.js";
|
|
5
|
+
const ARTIFACT_STAGE_BY_PATH = {
|
|
6
|
+
".cclaw/artifacts/01-brainstorm.md": "brainstorm",
|
|
7
|
+
".cclaw/artifacts/02-scope.md": "scope",
|
|
8
|
+
".cclaw/artifacts/03-design.md": "design",
|
|
9
|
+
".cclaw/artifacts/04-spec.md": "spec",
|
|
10
|
+
".cclaw/artifacts/05-plan.md": "plan",
|
|
11
|
+
".cclaw/artifacts/06-tdd.md": "tdd",
|
|
12
|
+
".cclaw/artifacts/07-review.md": "review",
|
|
13
|
+
".cclaw/artifacts/08-ship.md": "ship"
|
|
14
|
+
};
|
|
5
15
|
const REQUIRED_GATE_IDS = {
|
|
6
16
|
brainstorm: [
|
|
7
17
|
"brainstorm_approaches_compared",
|
|
@@ -92,6 +102,16 @@ function tieredArtifactValidation(stage, rows) {
|
|
|
92
102
|
};
|
|
93
103
|
});
|
|
94
104
|
}
|
|
105
|
+
function readsFromForTrack(readsFrom, track) {
|
|
106
|
+
const stageSet = new Set(TRACK_STAGES[track]);
|
|
107
|
+
return readsFrom.filter((artifactPath) => {
|
|
108
|
+
const stage = ARTIFACT_STAGE_BY_PATH[artifactPath];
|
|
109
|
+
if (!stage) {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
return stageSet.has(stage);
|
|
113
|
+
});
|
|
114
|
+
}
|
|
95
115
|
// ---------------------------------------------------------------------------
|
|
96
116
|
// Stage map and accessors
|
|
97
117
|
// ---------------------------------------------------------------------------
|
|
@@ -199,8 +219,8 @@ const STAGE_AUTO_SUBAGENT_DISPATCH = {
|
|
|
199
219
|
},
|
|
200
220
|
{
|
|
201
221
|
agent: "reviewer",
|
|
202
|
-
mode: "
|
|
203
|
-
when: "
|
|
222
|
+
mode: "mandatory",
|
|
223
|
+
when: "Mandatory when the diff exceeds 100 changed lines, touches more than 10 files, or modifies trust boundaries — dispatch a SECOND, independent reviewer with the adversarial-review skill loaded so the review army has at least two voices on a high-blast-radius change.",
|
|
204
224
|
purpose: "Adversarial second-opinion review on large or trust-sensitive diffs. The second reviewer treats the implementation as hostile and tries to break it (hostile-user, future-maintainer, competitor lenses) instead of sympathetically explaining it.",
|
|
205
225
|
requiresUserGate: false,
|
|
206
226
|
skill: "adversarial-review"
|
|
@@ -233,23 +253,29 @@ const STAGE_AUTO_SUBAGENT_DISPATCH = {
|
|
|
233
253
|
};
|
|
234
254
|
/** Transition guard: agents with `mode: "mandatory"` in auto-subagent dispatch for this stage. */
|
|
235
255
|
export function mandatoryDelegationsForStage(stage) {
|
|
236
|
-
return STAGE_AUTO_SUBAGENT_DISPATCH[stage]
|
|
237
|
-
|
|
238
|
-
|
|
256
|
+
return [...new Set(STAGE_AUTO_SUBAGENT_DISPATCH[stage]
|
|
257
|
+
.filter((d) => d.mode === "mandatory")
|
|
258
|
+
.map((d) => d.agent))];
|
|
239
259
|
}
|
|
240
260
|
export function stageSchema(stage, track = "standard") {
|
|
241
261
|
const base = stage === "tdd" ? tddStageForTrack(track) : STAGE_SCHEMA_MAP[stage];
|
|
242
262
|
const tieredGates = tieredStageGates(stage, base.requiredGates, track);
|
|
243
263
|
const tieredValidation = tieredArtifactValidation(stage, base.artifactValidation);
|
|
264
|
+
const crossStageTrace = {
|
|
265
|
+
...base.crossStageTrace,
|
|
266
|
+
readsFrom: readsFromForTrack(base.crossStageTrace.readsFrom, track)
|
|
267
|
+
};
|
|
244
268
|
return {
|
|
245
269
|
...base,
|
|
270
|
+
skillFolder: STAGE_TO_SKILL_FOLDER[stage],
|
|
271
|
+
crossStageTrace,
|
|
246
272
|
requiredGates: tieredGates,
|
|
247
273
|
artifactValidation: tieredValidation,
|
|
248
274
|
mandatoryDelegations: mandatoryDelegationsForStage(stage)
|
|
249
275
|
};
|
|
250
276
|
}
|
|
251
277
|
export function orderedStageSchemas(track = "standard") {
|
|
252
|
-
return
|
|
278
|
+
return FLOW_STAGES.map((stage) => stageSchema(stage, track));
|
|
253
279
|
}
|
|
254
280
|
export function stageGateIds(stage, track = "standard") {
|
|
255
281
|
return stageSchema(stage, track).requiredGates
|
|
@@ -10,7 +10,7 @@ export const DESIGN = {
|
|
|
10
10
|
ironLaw: "NO DESIGN DECISION WITHOUT A LABELED DIAGRAM, A REJECTED ALTERNATIVE, AND A NAMED FAILURE MODE.",
|
|
11
11
|
purpose: "Lock architecture, data flow, failure modes, and test/performance expectations through rigorous interactive review.",
|
|
12
12
|
whenToUse: [
|
|
13
|
-
"After scope
|
|
13
|
+
"After scope agreement approval",
|
|
14
14
|
"Before writing final spec and execution plan",
|
|
15
15
|
"When architecture risks need explicit treatment"
|
|
16
16
|
],
|
|
@@ -79,7 +79,7 @@ export const DESIGN = {
|
|
|
79
79
|
"What-already-exists section produced.",
|
|
80
80
|
"Completion dashboard lists every review section status, decision count, and unresolved items (or 'None')."
|
|
81
81
|
],
|
|
82
|
-
inputs: ["scope
|
|
82
|
+
inputs: ["scope agreement artifact", "system constraints", "non-functional requirements"],
|
|
83
83
|
requiredContext: [
|
|
84
84
|
"parallel research synthesis from `.cclaw/artifacts/02a-research.md`",
|
|
85
85
|
"existing architecture and boundaries",
|
|
@@ -201,7 +201,7 @@ export const REVIEW = {
|
|
|
201
201
|
},
|
|
202
202
|
artifactValidation: [
|
|
203
203
|
{ section: "Layer 1 Verdict", required: true, validationRule: "Per-criterion pass/fail with references." },
|
|
204
|
-
{ section: "Layer 2 Findings", required: false, validationRule: "Each finding has severity, description, and resolution status." },
|
|
204
|
+
{ section: "Layer 2 Findings", required: false, validationRule: "Each finding has severity, description, and resolution status. Security coverage must include either explicit security findings or `NO_CHANGE_ATTESTATION: <reason>` when no security-relevant changes were found." },
|
|
205
205
|
{ section: "Review Army Contract", required: true, validationRule: "Structured findings include id/severity/confidence/fingerprint/reportedBy/status with dedup reconciliation summary." },
|
|
206
206
|
{ section: "Review Readiness Dashboard", required: false, validationRule: "Includes a per-pass table (Layer 1 / Layer 2 / Adversarial / Schema) with a 'Completed at' column, a Delegation log snapshot block (path .cclaw/state/delegation-log.json with required/completed/waived/pending), a Staleness signal block (commit at last review pass and current commit), and a Headline with open critical blockers + ship recommendation. At minimum, the section text must contain the substrings 'Completed at', 'delegation-log.json', 'commit at last review pass', and 'Ship recommendation'." },
|
|
207
207
|
{ section: "Completeness Score", required: false, validationRule: "Records AC coverage, task coverage, test-slice coverage, and adversarial-review pass status as numeric or boolean values. At minimum, a line like 'AC coverage: N/M' or 'AC coverage: 100%'." },
|
|
@@ -100,6 +100,8 @@ export const SHIP = {
|
|
|
100
100
|
"FINALIZE_NO_VCS"
|
|
101
101
|
],
|
|
102
102
|
artifactFile: "08-ship.md",
|
|
103
|
+
// `done` exits the stage pipeline. Archive semantics are handled by the
|
|
104
|
+
// closeout substate machine (`idle` -> ... -> `archived`) in flow-state.
|
|
103
105
|
next: "done",
|
|
104
106
|
reviewSections: [
|
|
105
107
|
{
|
|
@@ -63,7 +63,8 @@ export const TDD = {
|
|
|
63
63
|
{ id: "tdd_green_full_suite", description: "Full relevant suite passes in GREEN state." },
|
|
64
64
|
{ id: "tdd_refactor_completed", description: "Refactor pass completed with behavior preservation verified." },
|
|
65
65
|
{ id: "tdd_verified_before_complete", description: "Fresh verification evidence includes test command, commit SHA, and explicit pass/fail status." },
|
|
66
|
-
{ id: "tdd_traceable_to_plan", description: "Change traceability to plan slice is explicit." }
|
|
66
|
+
{ id: "tdd_traceable_to_plan", description: "Change traceability to plan slice is explicit." },
|
|
67
|
+
{ id: "tdd_docs_drift_check", description: "When public API/config/CLI surfaces change, docs drift is addressed via a completed doc-updater pass." }
|
|
67
68
|
],
|
|
68
69
|
requiredEvidence: [
|
|
69
70
|
"Artifact updated at `.cclaw/artifacts/06-tdd.md` with RED, GREEN, and REFACTOR sections.",
|
|
@@ -206,9 +207,12 @@ function tddQuickTrackVariant() {
|
|
|
206
207
|
checklist: TDD.checklist.map(quickTrackText),
|
|
207
208
|
interactionProtocol: TDD.interactionProtocol.map(quickTrackText),
|
|
208
209
|
process: TDD.process.map(quickTrackText),
|
|
209
|
-
requiredGates: TDD.requiredGates
|
|
210
|
-
|
|
211
|
-
|
|
210
|
+
requiredGates: TDD.requiredGates
|
|
211
|
+
.filter((gate) => gate.id !== "tdd_traceable_to_plan")
|
|
212
|
+
.map((gate) => ({
|
|
213
|
+
...gate,
|
|
214
|
+
description: quickTrackText(gate.description)
|
|
215
|
+
})),
|
|
212
216
|
requiredEvidence: TDD.requiredEvidence.map(quickTrackText),
|
|
213
217
|
inputs: TDD.inputs.map(quickTrackText),
|
|
214
218
|
requiredContext: ["spec artifact", "existing test patterns"],
|