cclaw-cli 0.12.0 → 0.13.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/dist/cli.d.ts +2 -0
- package/dist/cli.js +14 -1
- package/dist/config.js +19 -0
- package/dist/constants.d.ts +2 -2
- package/dist/constants.js +13 -1
- package/dist/content/diff-command.d.ts +2 -0
- package/dist/content/diff-command.js +83 -0
- package/dist/content/feature-command.d.ts +2 -0
- package/dist/content/feature-command.js +120 -0
- package/dist/content/harnesses-doc.js +8 -0
- package/dist/content/hooks.js +47 -1
- package/dist/content/meta-skill.js +3 -2
- package/dist/content/next-command.js +8 -6
- package/dist/content/observe.d.ts +5 -1
- package/dist/content/observe.js +134 -2
- package/dist/content/retro-command.d.ts +2 -0
- package/dist/content/retro-command.js +77 -0
- package/dist/content/rewind-command.d.ts +3 -0
- package/dist/content/rewind-command.js +120 -0
- package/dist/content/status-command.js +43 -35
- package/dist/content/tdd-log-command.d.ts +2 -0
- package/dist/content/tdd-log-command.js +75 -0
- package/dist/content/templates.js +35 -5
- package/dist/content/tree-command.d.ts +2 -0
- package/dist/content/tree-command.js +91 -0
- package/dist/doctor.js +149 -3
- package/dist/feature-system.d.ts +18 -0
- package/dist/feature-system.js +247 -0
- package/dist/flow-state.d.ts +25 -0
- package/dist/flow-state.js +8 -1
- package/dist/harness-adapters.js +74 -4
- package/dist/install.js +35 -2
- package/dist/policy.js +22 -0
- package/dist/runs.d.ts +33 -1
- package/dist/runs.js +365 -6
- package/dist/tdd-cycle.d.ts +22 -0
- package/dist/tdd-cycle.js +82 -0
- package/dist/types.d.ts +4 -0
- package/package.json +1 -1
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -39,6 +39,8 @@ Commands:
|
|
|
39
39
|
--quiet Print only failing checks (and totals).
|
|
40
40
|
archive Move .cclaw/artifacts into .cclaw/runs/<date>-<slug> and reset flow state.
|
|
41
41
|
Flags: --name=<feature> Feature slug (default: inferred from 00-idea.md).
|
|
42
|
+
--skip-retro Bypass mandatory retro gate (requires --retro-reason).
|
|
43
|
+
--retro-reason=<t> Reason for bypassing retro gate.
|
|
42
44
|
upgrade Refresh generated files in .cclaw without modifying user artifacts.
|
|
43
45
|
uninstall Remove .cclaw runtime and the generated harness shim files.
|
|
44
46
|
|
|
@@ -368,6 +370,14 @@ function parseArgs(argv) {
|
|
|
368
370
|
}
|
|
369
371
|
if (flag.startsWith("--name=")) {
|
|
370
372
|
parsed.archiveName = flag.replace("--name=", "").trim();
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
if (flag === "--skip-retro") {
|
|
376
|
+
parsed.archiveSkipRetro = true;
|
|
377
|
+
continue;
|
|
378
|
+
}
|
|
379
|
+
if (flag.startsWith("--retro-reason=")) {
|
|
380
|
+
parsed.archiveSkipRetroReason = flag.replace("--retro-reason=", "").trim();
|
|
371
381
|
}
|
|
372
382
|
}
|
|
373
383
|
return parsed;
|
|
@@ -466,7 +476,10 @@ async function runCommand(parsed, ctx) {
|
|
|
466
476
|
return 0;
|
|
467
477
|
}
|
|
468
478
|
if (command === "archive") {
|
|
469
|
-
const archived = await archiveRun(ctx.cwd, parsed.archiveName
|
|
479
|
+
const archived = await archiveRun(ctx.cwd, parsed.archiveName, {
|
|
480
|
+
skipRetro: parsed.archiveSkipRetro === true,
|
|
481
|
+
skipRetroReason: parsed.archiveSkipRetroReason
|
|
482
|
+
});
|
|
470
483
|
const snapshotSummary = archived.snapshottedStateFiles.length > 0
|
|
471
484
|
? ` Snapshotted ${archived.snapshottedStateFiles.length} state file(s) under ${archived.archivePath}/state and wrote archive-manifest.json.`
|
|
472
485
|
: "";
|
package/dist/config.js
CHANGED
|
@@ -17,6 +17,8 @@ const ALLOWED_CONFIG_KEYS = new Set([
|
|
|
17
17
|
"harnesses",
|
|
18
18
|
"autoAdvance",
|
|
19
19
|
"promptGuardMode",
|
|
20
|
+
"tddEnforcement",
|
|
21
|
+
"tddTestGlobs",
|
|
20
22
|
"gitHookGuards",
|
|
21
23
|
"defaultTrack",
|
|
22
24
|
"languageRulePacks",
|
|
@@ -60,6 +62,8 @@ export function createDefaultConfig(harnesses = DEFAULT_HARNESSES, defaultTrack
|
|
|
60
62
|
harnesses,
|
|
61
63
|
autoAdvance: false,
|
|
62
64
|
promptGuardMode: "advisory",
|
|
65
|
+
tddEnforcement: "advisory",
|
|
66
|
+
tddTestGlobs: ["**/*.test.*", "**/*.spec.*", "**/test/**"],
|
|
63
67
|
gitHookGuards: false,
|
|
64
68
|
defaultTrack,
|
|
65
69
|
languageRulePacks: []
|
|
@@ -79,6 +83,7 @@ export function createProfileConfig(profile, overrides = {}) {
|
|
|
79
83
|
harnesses: overrides.harnesses ?? ["claude"],
|
|
80
84
|
autoAdvance: false,
|
|
81
85
|
promptGuardMode: "advisory",
|
|
86
|
+
tddEnforcement: "advisory",
|
|
82
87
|
gitHookGuards: false,
|
|
83
88
|
defaultTrack: overrides.defaultTrack ?? "medium",
|
|
84
89
|
languageRulePacks: overrides.languageRulePacks ?? []
|
|
@@ -89,6 +94,7 @@ export function createProfileConfig(profile, overrides = {}) {
|
|
|
89
94
|
harnesses: overrides.harnesses ?? DEFAULT_HARNESSES,
|
|
90
95
|
autoAdvance: false,
|
|
91
96
|
promptGuardMode: "advisory",
|
|
97
|
+
tddEnforcement: "advisory",
|
|
92
98
|
gitHookGuards: false,
|
|
93
99
|
defaultTrack: overrides.defaultTrack ?? "standard",
|
|
94
100
|
languageRulePacks: overrides.languageRulePacks ?? []
|
|
@@ -99,6 +105,7 @@ export function createProfileConfig(profile, overrides = {}) {
|
|
|
99
105
|
harnesses: overrides.harnesses ?? DEFAULT_HARNESSES,
|
|
100
106
|
autoAdvance: false,
|
|
101
107
|
promptGuardMode: "strict",
|
|
108
|
+
tddEnforcement: "strict",
|
|
102
109
|
gitHookGuards: true,
|
|
103
110
|
defaultTrack: overrides.defaultTrack ?? "standard",
|
|
104
111
|
languageRulePacks: overrides.languageRulePacks ?? [...LANGUAGE_RULE_PACKS]
|
|
@@ -149,6 +156,16 @@ export async function readConfig(projectRoot) {
|
|
|
149
156
|
throw configValidationError(fullPath, `"promptGuardMode" must be "advisory" or "strict"`);
|
|
150
157
|
}
|
|
151
158
|
const promptGuardMode = promptGuardModeRaw === "strict" ? "strict" : "advisory";
|
|
159
|
+
const tddEnforcementRaw = parsed.tddEnforcement;
|
|
160
|
+
if (Object.prototype.hasOwnProperty.call(parsed, "tddEnforcement") &&
|
|
161
|
+
tddEnforcementRaw !== "advisory" &&
|
|
162
|
+
tddEnforcementRaw !== "strict") {
|
|
163
|
+
throw configValidationError(fullPath, `"tddEnforcement" must be "advisory" or "strict"`);
|
|
164
|
+
}
|
|
165
|
+
const tddEnforcement = tddEnforcementRaw === "strict" ? "strict" : "advisory";
|
|
166
|
+
const tddTestGlobsRaw = parsed.tddTestGlobs;
|
|
167
|
+
const tddTestGlobs = validateStringArray(tddTestGlobsRaw, "tddTestGlobs", fullPath)
|
|
168
|
+
?? ["**/*.test.*", "**/*.spec.*", "**/test/**"];
|
|
152
169
|
const gitHookGuardsRaw = parsed.gitHookGuards;
|
|
153
170
|
if (Object.prototype.hasOwnProperty.call(parsed, "gitHookGuards") &&
|
|
154
171
|
typeof gitHookGuardsRaw !== "boolean") {
|
|
@@ -246,6 +263,8 @@ export async function readConfig(projectRoot) {
|
|
|
246
263
|
harnesses,
|
|
247
264
|
autoAdvance,
|
|
248
265
|
promptGuardMode,
|
|
266
|
+
tddEnforcement,
|
|
267
|
+
tddTestGlobs,
|
|
249
268
|
gitHookGuards,
|
|
250
269
|
defaultTrack,
|
|
251
270
|
languageRulePacks,
|
package/dist/constants.d.ts
CHANGED
|
@@ -4,9 +4,9 @@ export declare const RUNTIME_ROOT = ".cclaw";
|
|
|
4
4
|
export declare const CCLAW_VERSION = "0.1.1";
|
|
5
5
|
export declare const FLOW_VERSION = "1.0.0";
|
|
6
6
|
export declare const DEFAULT_HARNESSES: HarnessId[];
|
|
7
|
-
export declare const REQUIRED_DIRS: readonly [".cclaw", ".cclaw/commands", ".cclaw/skills", ".cclaw/contexts", ".cclaw/templates", ".cclaw/artifacts", ".cclaw/state", ".cclaw/runs", ".cclaw/rules", ".cclaw/adapters", ".cclaw/agents", ".cclaw/hooks", ".cclaw/custom-skills"];
|
|
7
|
+
export declare const REQUIRED_DIRS: readonly [".cclaw", ".cclaw/commands", ".cclaw/skills", ".cclaw/contexts", ".cclaw/templates", ".cclaw/artifacts", ".cclaw/features", ".cclaw/state", ".cclaw/runs", ".cclaw/rules", ".cclaw/adapters", ".cclaw/agents", ".cclaw/hooks", ".cclaw/custom-skills"];
|
|
8
8
|
export declare const REQUIRED_GITIGNORE_PATTERNS: readonly ["# cclaw generated artifacts", ".cclaw/", ".claude/commands/cc-*.md", ".claude/commands/cc.md", ".cursor/commands/cc-*.md", ".cursor/commands/cc.md", ".opencode/commands/cc-*.md", ".opencode/commands/cc.md", ".codex/commands/cc-*.md", ".codex/commands/cc.md", ".claude/hooks/hooks.json", ".cursor/hooks.json", ".codex/hooks.json", ".opencode/plugins/cclaw-plugin.mjs", ".cursor/rules/cclaw-workflow.mdc"];
|
|
9
9
|
export declare const COMMAND_FILE_ORDER: FlowStage[];
|
|
10
|
-
export declare const UTILITY_COMMANDS: readonly ["learn", "next", "status"];
|
|
10
|
+
export declare const UTILITY_COMMANDS: readonly ["learn", "next", "status", "tree", "diff", "feature", "tdd-log", "retro", "rewind", "rewind-ack"];
|
|
11
11
|
export declare const SUBAGENT_SKILL_FOLDERS: readonly ["subagent-dev", "parallel-dispatch"];
|
|
12
12
|
export type UtilityCommand = (typeof UTILITY_COMMANDS)[number];
|
package/dist/constants.js
CHANGED
|
@@ -15,6 +15,7 @@ export const REQUIRED_DIRS = [
|
|
|
15
15
|
`${RUNTIME_ROOT}/contexts`,
|
|
16
16
|
`${RUNTIME_ROOT}/templates`,
|
|
17
17
|
`${RUNTIME_ROOT}/artifacts`,
|
|
18
|
+
`${RUNTIME_ROOT}/features`,
|
|
18
19
|
`${RUNTIME_ROOT}/state`,
|
|
19
20
|
`${RUNTIME_ROOT}/runs`,
|
|
20
21
|
`${RUNTIME_ROOT}/rules`,
|
|
@@ -50,7 +51,18 @@ export const COMMAND_FILE_ORDER = [
|
|
|
50
51
|
"review",
|
|
51
52
|
"ship"
|
|
52
53
|
];
|
|
53
|
-
export const UTILITY_COMMANDS = [
|
|
54
|
+
export const UTILITY_COMMANDS = [
|
|
55
|
+
"learn",
|
|
56
|
+
"next",
|
|
57
|
+
"status",
|
|
58
|
+
"tree",
|
|
59
|
+
"diff",
|
|
60
|
+
"feature",
|
|
61
|
+
"tdd-log",
|
|
62
|
+
"retro",
|
|
63
|
+
"rewind",
|
|
64
|
+
"rewind-ack"
|
|
65
|
+
];
|
|
54
66
|
export const SUBAGENT_SKILL_FOLDERS = [
|
|
55
67
|
"subagent-dev",
|
|
56
68
|
"parallel-dispatch"
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
|
+
const DIFF_SKILL_FOLDER = "flow-diff";
|
|
3
|
+
const DIFF_SKILL_NAME = "flow-diff";
|
|
4
|
+
function flowStatePath() {
|
|
5
|
+
return `${RUNTIME_ROOT}/state/flow-state.json`;
|
|
6
|
+
}
|
|
7
|
+
function snapshotPath() {
|
|
8
|
+
return `${RUNTIME_ROOT}/state/flow-state.snapshot.json`;
|
|
9
|
+
}
|
|
10
|
+
export function diffCommandContract() {
|
|
11
|
+
return `# /cc-diff
|
|
12
|
+
|
|
13
|
+
## Purpose
|
|
14
|
+
|
|
15
|
+
Show a visual before/after diff map for flow-state progression.
|
|
16
|
+
|
|
17
|
+
## HARD-GATE
|
|
18
|
+
|
|
19
|
+
- Compare against \`${snapshotPath()}\` first; do not overwrite baseline before rendering.
|
|
20
|
+
- If no snapshot exists, initialize baseline and report "baseline created" explicitly.
|
|
21
|
+
|
|
22
|
+
## Algorithm
|
|
23
|
+
|
|
24
|
+
1. Read current state from \`${flowStatePath()}\`.
|
|
25
|
+
2. Read baseline from \`${snapshotPath()}\` (if missing -> create baseline from current state and stop).
|
|
26
|
+
3. Compute deltas:
|
|
27
|
+
- stage transition (\`from -> to\`)
|
|
28
|
+
- completed stage additions/removals
|
|
29
|
+
- skipped stage additions/removals
|
|
30
|
+
- stale stage additions/removals
|
|
31
|
+
- current-stage gate \`passed\` and \`blocked\` changes
|
|
32
|
+
4. Render a compact diff map (added \`+\`, removed \`-\`, changed \`->\`).
|
|
33
|
+
5. Persist current state back to \`${snapshotPath()}\` as new baseline with \`capturedAt\`.
|
|
34
|
+
|
|
35
|
+
## Diff Map Format
|
|
36
|
+
|
|
37
|
+
\`\`\`
|
|
38
|
+
cclaw flow diff
|
|
39
|
+
stage: design -> spec
|
|
40
|
+
completed: +design
|
|
41
|
+
stale: -design
|
|
42
|
+
gates(spec): +spec_contract_complete -spec_open_questions_closed
|
|
43
|
+
blocked(spec): +spec_trace_matrix_missing
|
|
44
|
+
\`\`\`
|
|
45
|
+
|
|
46
|
+
## Primary skill
|
|
47
|
+
|
|
48
|
+
**${RUNTIME_ROOT}/skills/${DIFF_SKILL_FOLDER}/SKILL.md**
|
|
49
|
+
`;
|
|
50
|
+
}
|
|
51
|
+
export function diffCommandSkillMarkdown() {
|
|
52
|
+
return `---
|
|
53
|
+
name: ${DIFF_SKILL_NAME}
|
|
54
|
+
description: "Compare current flow-state against saved snapshot and render gate/stage deltas."
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
# /cc-diff
|
|
58
|
+
|
|
59
|
+
## HARD-GATE
|
|
60
|
+
|
|
61
|
+
Never lose baseline visibility: render deltas before writing a new snapshot.
|
|
62
|
+
|
|
63
|
+
## Protocol
|
|
64
|
+
|
|
65
|
+
1. Read \`${flowStatePath()}\`.
|
|
66
|
+
2. Read \`${snapshotPath()}\`.
|
|
67
|
+
3. If snapshot missing:
|
|
68
|
+
- write baseline snapshot from current state,
|
|
69
|
+
- print \`flow diff baseline created\`,
|
|
70
|
+
- stop.
|
|
71
|
+
4. Build deltas for stage, completed/skipped/stale sets, and current-stage gate arrays.
|
|
72
|
+
5. Print a compact diff map with explicit \`+\`, \`-\`, and \`->\` markers.
|
|
73
|
+
6. Write updated snapshot with:
|
|
74
|
+
- \`capturedAt\` (ISO)
|
|
75
|
+
- \`state\` (full current flow-state object)
|
|
76
|
+
|
|
77
|
+
## Validation
|
|
78
|
+
|
|
79
|
+
- Diff output must be deterministic for identical states ("no changes").
|
|
80
|
+
- Snapshot file stays valid JSON after every run.
|
|
81
|
+
- Do not suppress removed values; removals are first-class evidence.
|
|
82
|
+
`;
|
|
83
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
|
+
const FEATURE_SKILL_FOLDER = "feature-workspaces";
|
|
3
|
+
const FEATURE_SKILL_NAME = "feature-workspaces";
|
|
4
|
+
function activeFeaturePath() {
|
|
5
|
+
return `${RUNTIME_ROOT}/state/active-feature.json`;
|
|
6
|
+
}
|
|
7
|
+
function featuresRoot() {
|
|
8
|
+
return `${RUNTIME_ROOT}/features`;
|
|
9
|
+
}
|
|
10
|
+
function runtimeArtifactsPath() {
|
|
11
|
+
return `${RUNTIME_ROOT}/artifacts`;
|
|
12
|
+
}
|
|
13
|
+
function runtimeStatePath() {
|
|
14
|
+
return `${RUNTIME_ROOT}/state`;
|
|
15
|
+
}
|
|
16
|
+
export function featureCommandContract() {
|
|
17
|
+
return `# /cc-feature
|
|
18
|
+
|
|
19
|
+
## Purpose
|
|
20
|
+
|
|
21
|
+
Manage multi-feature workspaces without flow-state/artifact collisions.
|
|
22
|
+
|
|
23
|
+
The active runtime remains:
|
|
24
|
+
- \`${runtimeArtifactsPath()}\` (active artifacts)
|
|
25
|
+
- \`${runtimeStatePath()}\` (active state)
|
|
26
|
+
|
|
27
|
+
Feature snapshots live under \`${featuresRoot()}/<feature-id>/\`.
|
|
28
|
+
|
|
29
|
+
## HARD-GATE
|
|
30
|
+
|
|
31
|
+
- Never overwrite another feature snapshot silently.
|
|
32
|
+
- Before switching feature, snapshot the current active runtime first.
|
|
33
|
+
- Keep \`${activeFeaturePath()}\` as the single source of "current feature".
|
|
34
|
+
|
|
35
|
+
## Subcommands
|
|
36
|
+
|
|
37
|
+
### \`/cc-feature status\`
|
|
38
|
+
Show active feature id and snapshot location.
|
|
39
|
+
|
|
40
|
+
### \`/cc-feature list\`
|
|
41
|
+
List all feature ids in \`${featuresRoot()}/\` (directory names).
|
|
42
|
+
|
|
43
|
+
### \`/cc-feature new <feature-id>\`
|
|
44
|
+
Create \`${featuresRoot()}/<feature-id>/artifacts\` and \`${featuresRoot()}/<feature-id>/state\`.
|
|
45
|
+
|
|
46
|
+
Optional flag:
|
|
47
|
+
- \`--clone-active\`: clone current active runtime into the new feature snapshot.
|
|
48
|
+
|
|
49
|
+
### \`/cc-feature switch <feature-id>\`
|
|
50
|
+
1. Snapshot current active runtime into \`${featuresRoot()}/<active>/\`.
|
|
51
|
+
2. Restore target snapshot from \`${featuresRoot()}/<feature-id>/\` into active runtime:
|
|
52
|
+
- \`${runtimeArtifactsPath()}\`
|
|
53
|
+
- \`${runtimeStatePath()}\` (preserve \`active-feature.json\`)
|
|
54
|
+
3. Update \`${activeFeaturePath()}\` with \`activeFeature=<feature-id>\`.
|
|
55
|
+
|
|
56
|
+
If the target snapshot is empty, initialize runtime as a fresh flow.
|
|
57
|
+
|
|
58
|
+
## Output
|
|
59
|
+
|
|
60
|
+
Always print:
|
|
61
|
+
- active feature before
|
|
62
|
+
- active feature after
|
|
63
|
+
- whether snapshot/restore changed files
|
|
64
|
+
|
|
65
|
+
## Primary skill
|
|
66
|
+
|
|
67
|
+
**${RUNTIME_ROOT}/skills/${FEATURE_SKILL_FOLDER}/SKILL.md**
|
|
68
|
+
`;
|
|
69
|
+
}
|
|
70
|
+
export function featureCommandSkillMarkdown() {
|
|
71
|
+
return `---
|
|
72
|
+
name: ${FEATURE_SKILL_NAME}
|
|
73
|
+
description: "Manage cclaw multi-feature workspaces (status/list/new/switch) while preserving active flow runtime."
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
# /cc-feature — Feature Workspace Manager
|
|
77
|
+
|
|
78
|
+
## HARD-GATE
|
|
79
|
+
|
|
80
|
+
Do not switch feature by editing only \`active-feature.json\`. A valid switch must snapshot current runtime and restore target runtime.
|
|
81
|
+
|
|
82
|
+
## Paths
|
|
83
|
+
|
|
84
|
+
- Active pointer: \`${activeFeaturePath()}\`
|
|
85
|
+
- Feature snapshots: \`${featuresRoot()}/<feature-id>/\`
|
|
86
|
+
- Active runtime artifacts: \`${runtimeArtifactsPath()}\`
|
|
87
|
+
- Active runtime state: \`${runtimeStatePath()}\`
|
|
88
|
+
|
|
89
|
+
## Protocol
|
|
90
|
+
|
|
91
|
+
### status
|
|
92
|
+
1. Read \`${activeFeaturePath()}\`.
|
|
93
|
+
2. Print active feature id and its snapshot folder.
|
|
94
|
+
|
|
95
|
+
### list
|
|
96
|
+
1. Enumerate directories in \`${featuresRoot()}/\`.
|
|
97
|
+
2. Mark the active one.
|
|
98
|
+
|
|
99
|
+
### new <feature-id> [--clone-active]
|
|
100
|
+
1. Validate \`feature-id\` (lowercase slug, letters/numbers/dashes).
|
|
101
|
+
2. Create snapshot dirs:
|
|
102
|
+
- \`${featuresRoot()}/<feature-id>/artifacts\`
|
|
103
|
+
- \`${featuresRoot()}/<feature-id>/state\`
|
|
104
|
+
3. If \`--clone-active\`: copy active runtime artifacts/state into the new snapshot.
|
|
105
|
+
4. Do not change active feature unless the user explicitly requests switch.
|
|
106
|
+
|
|
107
|
+
### switch <feature-id>
|
|
108
|
+
1. Read current active feature id.
|
|
109
|
+
2. Snapshot current runtime into current feature snapshot.
|
|
110
|
+
3. Restore target snapshot into active runtime.
|
|
111
|
+
4. Update \`${activeFeaturePath()}\`.
|
|
112
|
+
5. Report stage/run after restore (\`flow-state.json\`).
|
|
113
|
+
|
|
114
|
+
## Safety checks
|
|
115
|
+
|
|
116
|
+
- If target feature does not exist: block and suggest \`/cc-feature new <id>\`.
|
|
117
|
+
- If snapshot copy fails: abort switch, keep current active feature unchanged.
|
|
118
|
+
- Preserve global pointer file \`active-feature.json\` when restoring state.
|
|
119
|
+
`;
|
|
120
|
+
}
|
|
@@ -66,6 +66,14 @@ All harnesses receive the same utility commands:
|
|
|
66
66
|
- \`/cc\` - flow entry and resume
|
|
67
67
|
- \`/cc-next\` - stage progression
|
|
68
68
|
- \`/cc-learn\` - knowledge capture/lookup
|
|
69
|
+
- \`/cc-status\` - read-only visual flow snapshot
|
|
70
|
+
- \`/cc-tree\` - deep flow tree (stages, artifacts, stale markers)
|
|
71
|
+
- \`/cc-diff\` - before/after flow-state diff map
|
|
72
|
+
- \`/cc-feature\` - multi-feature workspace management
|
|
73
|
+
- \`/cc-tdd-log\` - explicit RED/GREEN/REFACTOR evidence log
|
|
74
|
+
- \`/cc-retro\` - mandatory retrospective gate before archive
|
|
75
|
+
- \`/cc-rewind\` - rewind flow and invalidate downstream stages
|
|
76
|
+
- \`/cc-rewind-ack\` - clear stale stage markers after redo
|
|
69
77
|
|
|
70
78
|
Stage order remains canonical:
|
|
71
79
|
\`brainstorm -> scope -> design -> spec -> plan -> tdd -> review -> ship\`
|
package/dist/content/hooks.js
CHANGED
|
@@ -45,6 +45,7 @@ set -euo pipefail
|
|
|
45
45
|
${DETECT_ROOT}
|
|
46
46
|
|
|
47
47
|
STATE_FILE="$ROOT/${RUNTIME_ROOT}/state/flow-state.json"
|
|
48
|
+
ACTIVE_FEATURE_FILE="$ROOT/${RUNTIME_ROOT}/state/active-feature.json"
|
|
48
49
|
CHECKPOINT_FILE="$ROOT/${RUNTIME_ROOT}/state/checkpoint.json"
|
|
49
50
|
ACTIVITY_FILE="$ROOT/${RUNTIME_ROOT}/state/stage-activity.jsonl"
|
|
50
51
|
SUGGESTION_MEMORY_FILE="$ROOT/${RUNTIME_ROOT}/state/suggestion-memory.json"
|
|
@@ -59,13 +60,16 @@ META_SKILL="$ROOT/${RUNTIME_ROOT}/skills/${META_SKILL_NAME}/SKILL.md"
|
|
|
59
60
|
STAGE="none"
|
|
60
61
|
COMPLETED="0"
|
|
61
62
|
ACTIVE_RUN="none"
|
|
63
|
+
ACTIVE_FEATURE="default"
|
|
62
64
|
ACTIVE_CONTEXT_MODE="default"
|
|
65
|
+
STALE_STAGES=""
|
|
63
66
|
CONTEXT_MODE_NOTE=""
|
|
64
67
|
if [ -f "$STATE_FILE" ]; then
|
|
65
68
|
if command -v jq >/dev/null 2>&1; then
|
|
66
69
|
STAGE=$(jq -r '.currentStage // "none"' "$STATE_FILE" 2>/dev/null || echo "none")
|
|
67
70
|
COMPLETED=$(jq -r '(.completedStages | length) // 0' "$STATE_FILE" 2>/dev/null || echo "0")
|
|
68
71
|
ACTIVE_RUN=$(jq -r '.activeRunId // "none"' "$STATE_FILE" 2>/dev/null || echo "none")
|
|
72
|
+
STALE_STAGES=$(jq -r '(.staleStages // {} | keys | join(", "))' "$STATE_FILE" 2>/dev/null || echo "")
|
|
69
73
|
else
|
|
70
74
|
if command -v python3 >/dev/null 2>&1; then
|
|
71
75
|
STAGE=$(python3 - "$STATE_FILE" <<'PY'
|
|
@@ -115,6 +119,22 @@ except Exception:
|
|
|
115
119
|
pass
|
|
116
120
|
print(run)
|
|
117
121
|
PY
|
|
122
|
+
)
|
|
123
|
+
STALE_STAGES=$(python3 - "$STATE_FILE" <<'PY'
|
|
124
|
+
import json
|
|
125
|
+
import sys
|
|
126
|
+
value = ""
|
|
127
|
+
try:
|
|
128
|
+
with open(sys.argv[1], "r", encoding="utf-8") as fh:
|
|
129
|
+
data = json.load(fh)
|
|
130
|
+
stale = data.get("staleStages", {})
|
|
131
|
+
if isinstance(stale, dict):
|
|
132
|
+
keys = [k for k, v in stale.items() if isinstance(v, dict)]
|
|
133
|
+
value = ", ".join(keys)
|
|
134
|
+
except Exception:
|
|
135
|
+
pass
|
|
136
|
+
print(value)
|
|
137
|
+
PY
|
|
118
138
|
)
|
|
119
139
|
else
|
|
120
140
|
STAGE=$(grep -o '"currentStage"[[:space:]]*:[[:space:]]*"[^"]*"' "$STATE_FILE" 2>/dev/null | head -1 | sed 's/.*"\\([^"]*\\)"$/\\1/' || echo "none")
|
|
@@ -129,6 +149,28 @@ PY
|
|
|
129
149
|
fi
|
|
130
150
|
fi
|
|
131
151
|
|
|
152
|
+
if [ -f "$ACTIVE_FEATURE_FILE" ]; then
|
|
153
|
+
if command -v jq >/dev/null 2>&1; then
|
|
154
|
+
ACTIVE_FEATURE=$(jq -r '.activeFeature // "default"' "$ACTIVE_FEATURE_FILE" 2>/dev/null || echo "default")
|
|
155
|
+
elif command -v python3 >/dev/null 2>&1; then
|
|
156
|
+
ACTIVE_FEATURE=$(python3 - "$ACTIVE_FEATURE_FILE" <<'PY'
|
|
157
|
+
import json
|
|
158
|
+
import sys
|
|
159
|
+
feature = "default"
|
|
160
|
+
try:
|
|
161
|
+
with open(sys.argv[1], "r", encoding="utf-8") as fh:
|
|
162
|
+
data = json.load(fh)
|
|
163
|
+
value = data.get("activeFeature")
|
|
164
|
+
if isinstance(value, str) and value:
|
|
165
|
+
feature = value
|
|
166
|
+
except Exception:
|
|
167
|
+
pass
|
|
168
|
+
print(feature)
|
|
169
|
+
PY
|
|
170
|
+
)
|
|
171
|
+
fi
|
|
172
|
+
fi
|
|
173
|
+
|
|
132
174
|
if [ -f "$CONTEXT_MODE_FILE" ]; then
|
|
133
175
|
if command -v jq >/dev/null 2>&1; then
|
|
134
176
|
ACTIVE_CONTEXT_MODE=$(jq -r '.activeMode // "default"' "$CONTEXT_MODE_FILE" 2>/dev/null || echo "default")
|
|
@@ -415,7 +457,7 @@ if [ -n "$ROUTING_MISSING" ]; then
|
|
|
415
457
|
fi
|
|
416
458
|
|
|
417
459
|
# --- Build context message ---
|
|
418
|
-
CTX="cclaw loaded. Flow: stage=$STAGE ($COMPLETED/8 completed, run=$ACTIVE_RUN). Active artifacts: ${RUNTIME_ROOT}/artifacts/. Learnings: $LEARNINGS_COUNT entries."
|
|
460
|
+
CTX="cclaw loaded. Flow: stage=$STAGE ($COMPLETED/8 completed, run=$ACTIVE_RUN, feature=$ACTIVE_FEATURE). Active artifacts: ${RUNTIME_ROOT}/artifacts/. Feature snapshots: ${RUNTIME_ROOT}/features/$ACTIVE_FEATURE/. Learnings: $LEARNINGS_COUNT entries."
|
|
419
461
|
if [ -n "$VERSION_NOTE" ]; then
|
|
420
462
|
CTX="$CTX
|
|
421
463
|
$VERSION_NOTE"
|
|
@@ -452,6 +494,10 @@ if [ -n "$STAGE_SUGGESTION" ]; then
|
|
|
452
494
|
$STAGE_SUGGESTION
|
|
453
495
|
To disable suggestions persistently set ${RUNTIME_ROOT}/state/suggestion-memory.json -> enabled=false."
|
|
454
496
|
fi
|
|
497
|
+
if [ -n "$STALE_STAGES" ]; then
|
|
498
|
+
CTX="$CTX
|
|
499
|
+
Stale stages pending acknowledgement: $STALE_STAGES (use /cc-rewind-ack <stage> after redo)."
|
|
500
|
+
fi
|
|
455
501
|
if [ -n "$KNOWLEDGE_DIGEST" ]; then
|
|
456
502
|
CTX="$CTX
|
|
457
503
|
Knowledge digest (top relevant entries):
|
|
@@ -3,7 +3,7 @@ export const META_SKILL_NAME = "using-cclaw";
|
|
|
3
3
|
export function usingCclawSkillMarkdown() {
|
|
4
4
|
return `---
|
|
5
5
|
name: using-cclaw
|
|
6
|
-
description: "Routing brain for cclaw. Decide whether to start/resume a stage, answer directly, or use /cc-learn."
|
|
6
|
+
description: "Routing brain for cclaw. Decide whether to start/resume a stage, answer directly, or use utility commands like /cc-learn, /cc-status, /cc-tree, and /cc-diff."
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Using Cclaw
|
|
@@ -26,7 +26,8 @@ Task arrives
|
|
|
26
26
|
├─ Pure question / non-software ask? -> answer directly (no stage)
|
|
27
27
|
├─ New software work? -> /cc <idea>
|
|
28
28
|
├─ Resume existing flow? -> /cc or /cc-next
|
|
29
|
-
|
|
29
|
+
├─ Knowledge operation? -> /cc-learn
|
|
30
|
+
└─ Workspace operation? -> /cc-status, /cc-tree, /cc-diff, /cc-feature, /cc-tdd-log, /cc-retro, /cc-rewind
|
|
30
31
|
\`\`\`
|
|
31
32
|
|
|
32
33
|
## Task classification
|
|
@@ -39,11 +39,12 @@ This is the only progression command the user needs to drive the entire flow. St
|
|
|
39
39
|
|
|
40
40
|
1. Read **\`${flowPath}\`**. If missing → **BLOCKED** (state missing).
|
|
41
41
|
2. Parse JSON. Capture \`currentStage\` and \`stageGateCatalog[currentStage]\`.
|
|
42
|
-
3.
|
|
43
|
-
4. Let \`
|
|
44
|
-
5.
|
|
45
|
-
6.
|
|
46
|
-
7.
|
|
42
|
+
3. If \`staleStages[currentStage]\` exists, do not advance automatically. Re-run the stage artifact work, then clear the marker with \`/cc-rewind-ack <currentStage>\`.
|
|
43
|
+
4. Let \`G\` = \`requiredGates\` for **\`currentStage\`** from the stage schema.
|
|
44
|
+
5. Let \`catalog\` = \`stageGateCatalog[currentStage]\` from flow state.
|
|
45
|
+
6. **Satisfied** for gate id \`g\`: \`g\` in \`catalog.passed\` and \`g\` not in \`catalog.blocked\`.
|
|
46
|
+
7. Let \`M\` = \`mandatoryDelegations\` for \`currentStage\`.
|
|
47
|
+
8. If \`M\` is non-empty, inspect **\`${delegationPath}\`**. Treat as satisfied only if the agent is **completed** or **waived**.
|
|
47
48
|
|
|
48
49
|
### Path A: Current stage is NOT complete (any gate unmet or delegation missing)
|
|
49
50
|
|
|
@@ -120,7 +121,8 @@ Do **not** mark gates satisfied from memory alone. Cite **artifact evidence** (p
|
|
|
120
121
|
|
|
121
122
|
1. Open **\`${flowPath}\`**.
|
|
122
123
|
2. Record \`currentStage\` and \`stageGateCatalog[currentStage]\`.
|
|
123
|
-
3. If
|
|
124
|
+
3. If \`staleStages[currentStage]\` exists, re-run the stage and clear marker via \`/cc-rewind-ack <currentStage>\` before advancing.
|
|
125
|
+
4. If the file is missing or invalid JSON → **BLOCKED** (report and stop).
|
|
124
126
|
|
|
125
127
|
### Step 2: Evaluate gates
|
|
126
128
|
|
|
@@ -9,7 +9,11 @@ export interface PromptGuardOptions {
|
|
|
9
9
|
strictMode?: boolean;
|
|
10
10
|
}
|
|
11
11
|
export declare function promptGuardScript(options?: PromptGuardOptions): string;
|
|
12
|
-
export
|
|
12
|
+
export interface WorkflowGuardOptions {
|
|
13
|
+
tddEnforcementMode?: "advisory" | "strict";
|
|
14
|
+
tddTestGlobs?: string[];
|
|
15
|
+
}
|
|
16
|
+
export declare function workflowGuardScript(options?: WorkflowGuardOptions): string;
|
|
13
17
|
export declare function observeScript(): string;
|
|
14
18
|
export declare function contextMonitorScript(): string;
|
|
15
19
|
export declare function summarizeObservationsRuntimeModule(): string;
|