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.
Files changed (39) hide show
  1. package/dist/cli.d.ts +2 -0
  2. package/dist/cli.js +14 -1
  3. package/dist/config.js +19 -0
  4. package/dist/constants.d.ts +2 -2
  5. package/dist/constants.js +13 -1
  6. package/dist/content/diff-command.d.ts +2 -0
  7. package/dist/content/diff-command.js +83 -0
  8. package/dist/content/feature-command.d.ts +2 -0
  9. package/dist/content/feature-command.js +120 -0
  10. package/dist/content/harnesses-doc.js +8 -0
  11. package/dist/content/hooks.js +47 -1
  12. package/dist/content/meta-skill.js +3 -2
  13. package/dist/content/next-command.js +8 -6
  14. package/dist/content/observe.d.ts +5 -1
  15. package/dist/content/observe.js +134 -2
  16. package/dist/content/retro-command.d.ts +2 -0
  17. package/dist/content/retro-command.js +77 -0
  18. package/dist/content/rewind-command.d.ts +3 -0
  19. package/dist/content/rewind-command.js +120 -0
  20. package/dist/content/status-command.js +43 -35
  21. package/dist/content/tdd-log-command.d.ts +2 -0
  22. package/dist/content/tdd-log-command.js +75 -0
  23. package/dist/content/templates.js +35 -5
  24. package/dist/content/tree-command.d.ts +2 -0
  25. package/dist/content/tree-command.js +91 -0
  26. package/dist/doctor.js +149 -3
  27. package/dist/feature-system.d.ts +18 -0
  28. package/dist/feature-system.js +247 -0
  29. package/dist/flow-state.d.ts +25 -0
  30. package/dist/flow-state.js +8 -1
  31. package/dist/harness-adapters.js +74 -4
  32. package/dist/install.js +35 -2
  33. package/dist/policy.js +22 -0
  34. package/dist/runs.d.ts +33 -1
  35. package/dist/runs.js +365 -6
  36. package/dist/tdd-cycle.d.ts +22 -0
  37. package/dist/tdd-cycle.js +82 -0
  38. package/dist/types.d.ts +4 -0
  39. package/package.json +1 -1
@@ -11,6 +11,68 @@ function escapeRegExp(value) {
11
11
  const RUNTIME_AGENTS_BLOCK_SOURCE = `${escapeRegExp(CCLAW_MARKER_START)}[\\s\\S]*?${escapeRegExp(CCLAW_MARKER_END)}`;
12
12
  const RUNTIME_AGENTS_BLOCK_PATTERN = new RegExp(RUNTIME_AGENTS_BLOCK_SOURCE, "u");
13
13
  const RUNTIME_AGENTS_BLOCK_GLOBAL_PATTERN = new RegExp(RUNTIME_AGENTS_BLOCK_SOURCE, "gu");
14
+ const UTILITY_SHIMS = [
15
+ {
16
+ fileName: "cc-next.md",
17
+ command: "next",
18
+ skillFolder: "flow-next-step",
19
+ commandFile: "next.md"
20
+ },
21
+ {
22
+ fileName: "cc-learn.md",
23
+ command: "learn",
24
+ skillFolder: "learnings",
25
+ commandFile: "learn.md"
26
+ },
27
+ {
28
+ fileName: "cc-status.md",
29
+ command: "status",
30
+ skillFolder: "flow-status",
31
+ commandFile: "status.md"
32
+ },
33
+ {
34
+ fileName: "cc-tree.md",
35
+ command: "tree",
36
+ skillFolder: "flow-tree",
37
+ commandFile: "tree.md"
38
+ },
39
+ {
40
+ fileName: "cc-diff.md",
41
+ command: "diff",
42
+ skillFolder: "flow-diff",
43
+ commandFile: "diff.md"
44
+ },
45
+ {
46
+ fileName: "cc-feature.md",
47
+ command: "feature",
48
+ skillFolder: "feature-workspaces",
49
+ commandFile: "feature.md"
50
+ },
51
+ {
52
+ fileName: "cc-tdd-log.md",
53
+ command: "tdd-log",
54
+ skillFolder: "tdd-cycle-log",
55
+ commandFile: "tdd-log.md"
56
+ },
57
+ {
58
+ fileName: "cc-retro.md",
59
+ command: "retro",
60
+ skillFolder: "flow-retro",
61
+ commandFile: "retro.md"
62
+ },
63
+ {
64
+ fileName: "cc-rewind.md",
65
+ command: "rewind",
66
+ skillFolder: "flow-rewind",
67
+ commandFile: "rewind.md"
68
+ },
69
+ {
70
+ fileName: "cc-rewind-ack.md",
71
+ command: "rewind-ack",
72
+ skillFolder: "flow-rewind",
73
+ commandFile: "rewind-ack.md"
74
+ }
75
+ ];
14
76
  export const HARNESS_ADAPTERS = {
15
77
  claude: {
16
78
  id: "claude",
@@ -98,13 +160,21 @@ When in doubt, prefer **non-trivial** — the quick track is opt-in and only saf
98
160
  5. Contextual utility skills.
99
161
  6. Training priors.
100
162
 
101
- ### Commands (3 total)
163
+ ### Commands
102
164
 
103
165
  | Command | Purpose |
104
166
  |---|---|
105
167
  | \`/cc\` | **Entry point.** No args = resume current stage. With prompt = classify task and start the right flow. |
106
168
  | \`/cc-next\` | **Progression.** Advances to the next stage when current is complete. |
107
169
  | \`/cc-learn\` | **Cross-cutting.** Capture or review project knowledge (append-only JSONL). |
170
+ | \`/cc-status\` | **Read-only.** Visual snapshot with progress bar, gate delta, and delegations. |
171
+ | \`/cc-tree\` | **Read-only.** Deep flow tree for stages, artifacts, and stale markers. |
172
+ | \`/cc-diff\` | **Delta map.** Compare current flow-state with saved baseline snapshot. |
173
+ | \`/cc-feature\` | **Workspace.** Manage active feature snapshots for parallel tracks. |
174
+ | \`/cc-tdd-log\` | **Evidence.** Record RED/GREEN/REFACTOR cycle events for enforcement. |
175
+ | \`/cc-retro\` | **Learning gate.** Mandatory retrospective before archive after ship. |
176
+ | \`/cc-rewind\` | **Recovery.** Rewind flow to an earlier stage and invalidate downstream work. |
177
+ | \`/cc-rewind-ack\` | **Recovery.** Clear stale-stage markers after redo. |
108
178
 
109
179
  **Stage order:** brainstorm > scope > design > spec > plan > tdd > review > ship.
110
180
  \`/cc-next\` loads the right stage skill automatically. Gates must pass before handoff.
@@ -213,9 +283,9 @@ export async function syncHarnessShims(projectRoot, harnesses) {
213
283
  const commandDir = path.join(projectRoot, adapter.commandDir);
214
284
  await ensureDir(commandDir);
215
285
  await writeFileSafe(path.join(commandDir, "cc.md"), utilityShimContent(harness, "cc", "flow-start", "start.md"));
216
- await writeFileSafe(path.join(commandDir, "cc-next.md"), utilityShimContent(harness, "next", "flow-next-step", "next.md"));
217
- await writeFileSafe(path.join(commandDir, "cc-learn.md"), utilityShimContent(harness, "learn", "learnings", "learn.md"));
218
- await writeFileSafe(path.join(commandDir, "cc-status.md"), utilityShimContent(harness, "status", "flow-status", "status.md"));
286
+ for (const shim of UTILITY_SHIMS) {
287
+ await writeFileSafe(path.join(commandDir, shim.fileName), utilityShimContent(harness, shim.command, shim.skillFolder, shim.commandFile));
288
+ }
219
289
  }
220
290
  await syncAgentFiles(projectRoot);
221
291
  await syncAgentsMd(projectRoot, harnesses);
package/dist/install.js CHANGED
@@ -10,6 +10,12 @@ import { learnSkillMarkdown, learnCommandContract } from "./content/learnings.js
10
10
  import { nextCommandContract, nextCommandSkillMarkdown } from "./content/next-command.js";
11
11
  import { startCommandContract, startCommandSkillMarkdown } from "./content/start-command.js";
12
12
  import { statusCommandContract, statusCommandSkillMarkdown } from "./content/status-command.js";
13
+ import { treeCommandContract, treeCommandSkillMarkdown } from "./content/tree-command.js";
14
+ import { diffCommandContract, diffCommandSkillMarkdown } from "./content/diff-command.js";
15
+ import { featureCommandContract, featureCommandSkillMarkdown } from "./content/feature-command.js";
16
+ import { tddLogCommandContract, tddLogCommandSkillMarkdown } from "./content/tdd-log-command.js";
17
+ import { retroCommandContract, retroCommandSkillMarkdown } from "./content/retro-command.js";
18
+ import { rewindAcknowledgeCommandContract, rewindCommandContract, rewindCommandSkillMarkdown } from "./content/rewind-command.js";
13
19
  import { subagentDrivenDevSkill, parallelAgentsSkill } from "./content/subagents.js";
14
20
  import { sessionHooksSkillMarkdown } from "./content/session-hooks.js";
15
21
  import { sessionStartScript, stopCheckpointScript, preCompactScript, opencodePluginJs, claudeHooksJson, cursorHooksJson, codexHooksJson } from "./content/hooks.js";
@@ -197,6 +203,12 @@ async function writeSkills(projectRoot, config) {
197
203
  await writeFileSafe(runtimePath(projectRoot, "skills", "flow-next-step", "SKILL.md"), nextCommandSkillMarkdown());
198
204
  await writeFileSafe(runtimePath(projectRoot, "skills", "flow-start", "SKILL.md"), startCommandSkillMarkdown());
199
205
  await writeFileSafe(runtimePath(projectRoot, "skills", "flow-status", "SKILL.md"), statusCommandSkillMarkdown());
206
+ await writeFileSafe(runtimePath(projectRoot, "skills", "flow-tree", "SKILL.md"), treeCommandSkillMarkdown());
207
+ await writeFileSafe(runtimePath(projectRoot, "skills", "flow-diff", "SKILL.md"), diffCommandSkillMarkdown());
208
+ await writeFileSafe(runtimePath(projectRoot, "skills", "feature-workspaces", "SKILL.md"), featureCommandSkillMarkdown());
209
+ await writeFileSafe(runtimePath(projectRoot, "skills", "tdd-cycle-log", "SKILL.md"), tddLogCommandSkillMarkdown());
210
+ await writeFileSafe(runtimePath(projectRoot, "skills", "flow-retro", "SKILL.md"), retroCommandSkillMarkdown());
211
+ await writeFileSafe(runtimePath(projectRoot, "skills", "flow-rewind", "SKILL.md"), rewindCommandSkillMarkdown());
200
212
  await writeFileSafe(runtimePath(projectRoot, "skills", "subagent-dev", "SKILL.md"), subagentDrivenDevSkill());
201
213
  await writeFileSafe(runtimePath(projectRoot, "skills", "parallel-dispatch", "SKILL.md"), parallelAgentsSkill());
202
214
  await writeFileSafe(runtimePath(projectRoot, "skills", "session", "SKILL.md"), sessionHooksSkillMarkdown());
@@ -250,6 +262,13 @@ async function writeUtilityCommands(projectRoot) {
250
262
  await writeFileSafe(runtimePath(projectRoot, "commands", "next.md"), nextCommandContract());
251
263
  await writeFileSafe(runtimePath(projectRoot, "commands", "start.md"), startCommandContract());
252
264
  await writeFileSafe(runtimePath(projectRoot, "commands", "status.md"), statusCommandContract());
265
+ await writeFileSafe(runtimePath(projectRoot, "commands", "tree.md"), treeCommandContract());
266
+ await writeFileSafe(runtimePath(projectRoot, "commands", "diff.md"), diffCommandContract());
267
+ await writeFileSafe(runtimePath(projectRoot, "commands", "feature.md"), featureCommandContract());
268
+ await writeFileSafe(runtimePath(projectRoot, "commands", "tdd-log.md"), tddLogCommandContract());
269
+ await writeFileSafe(runtimePath(projectRoot, "commands", "retro.md"), retroCommandContract());
270
+ await writeFileSafe(runtimePath(projectRoot, "commands", "rewind.md"), rewindCommandContract());
271
+ await writeFileSafe(runtimePath(projectRoot, "commands", "rewind-ack.md"), rewindAcknowledgeCommandContract());
253
272
  }
254
273
  function toObject(value) {
255
274
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -550,7 +569,10 @@ async function writeHooks(projectRoot, config) {
550
569
  await writeFileSafe(path.join(hooksDir, "prompt-guard.sh"), promptGuardScript({
551
570
  strictMode: config.promptGuardMode === "strict"
552
571
  }));
553
- await writeFileSafe(path.join(hooksDir, "workflow-guard.sh"), workflowGuardScript());
572
+ await writeFileSafe(path.join(hooksDir, "workflow-guard.sh"), workflowGuardScript({
573
+ tddEnforcementMode: config.tddEnforcement ?? "advisory",
574
+ tddTestGlobs: config.tddTestGlobs
575
+ }));
554
576
  await writeFileSafe(path.join(hooksDir, "context-monitor.sh"), contextMonitorScript());
555
577
  const opencodePluginSource = opencodePluginJs();
556
578
  await writeFileSafe(path.join(hooksDir, "opencode-plugin.mjs"), opencodePluginSource);
@@ -760,13 +782,13 @@ Drop this section if no hard rule applies. Keep it crisp:
760
782
  async function ensureSessionStateFiles(projectRoot) {
761
783
  const stateDir = runtimePath(projectRoot, "state");
762
784
  await ensureDir(stateDir);
785
+ const flow = await readFlowState(projectRoot);
763
786
  const activityPath = path.join(stateDir, "stage-activity.jsonl");
764
787
  if (!(await exists(activityPath))) {
765
788
  await writeFileSafe(activityPath, "");
766
789
  }
767
790
  const checkpointPath = path.join(stateDir, "checkpoint.json");
768
791
  if (!(await exists(checkpointPath))) {
769
- const flow = await readFlowState(projectRoot);
770
792
  const initialCheckpoint = {
771
793
  stage: flow.currentStage,
772
794
  runId: flow.activeRunId,
@@ -800,6 +822,17 @@ async function ensureSessionStateFiles(projectRoot) {
800
822
  if (!(await exists(preambleLogPath))) {
801
823
  await writeFileSafe(preambleLogPath, "");
802
824
  }
825
+ const tddCycleLogPath = path.join(stateDir, "tdd-cycle-log.jsonl");
826
+ if (!(await exists(tddCycleLogPath))) {
827
+ await writeFileSafe(tddCycleLogPath, "");
828
+ }
829
+ const flowSnapshotPath = path.join(stateDir, "flow-state.snapshot.json");
830
+ if (!(await exists(flowSnapshotPath))) {
831
+ await writeFileSafe(flowSnapshotPath, `${JSON.stringify({
832
+ capturedAt: new Date().toISOString(),
833
+ state: flow
834
+ }, null, 2)}\n`);
835
+ }
803
836
  }
804
837
  async function writeRulebook(projectRoot) {
805
838
  await writeFileSafe(runtimePath(projectRoot, "rules", "RULES.md"), RULEBOOK_MARKDOWN);
package/dist/policy.js CHANGED
@@ -90,6 +90,27 @@ export async function policyChecks(projectRoot, options = {}) {
90
90
  { file: runtimeFile("skills/learnings/SKILL.md"), needle: "## Subcommands", name: "utility_skill:learnings:subcommands" },
91
91
  { file: runtimeFile("skills/learnings/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:learnings:hard_gate" },
92
92
  { file: runtimeFile("commands/learn.md"), needle: "## Subcommands", name: "utility_command:learn:subcommands" },
93
+ { file: runtimeFile("commands/status.md"), needle: "bar:", name: "utility_command:status:visual_bar" },
94
+ { file: runtimeFile("commands/status.md"), needle: "/cc-tree · /cc-diff", name: "utility_command:status:tree_diff_link" },
95
+ { file: runtimeFile("commands/tree.md"), needle: "## Algorithm", name: "utility_command:tree:algorithm" },
96
+ { file: runtimeFile("skills/flow-tree/SKILL.md"), needle: "## Protocol", name: "utility_skill:tree:protocol" },
97
+ { file: runtimeFile("skills/flow-tree/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:tree:hard_gate" },
98
+ { file: runtimeFile("commands/diff.md"), needle: "## Algorithm", name: "utility_command:diff:algorithm" },
99
+ { file: runtimeFile("skills/flow-diff/SKILL.md"), needle: "## Protocol", name: "utility_skill:diff:protocol" },
100
+ { file: runtimeFile("skills/flow-diff/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:diff:hard_gate" },
101
+ { file: runtimeFile("commands/feature.md"), needle: "## Subcommands", name: "utility_command:feature:subcommands" },
102
+ { file: runtimeFile("skills/feature-workspaces/SKILL.md"), needle: "## Protocol", name: "utility_skill:feature:protocol" },
103
+ { file: runtimeFile("skills/feature-workspaces/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:feature:hard_gate" },
104
+ { file: runtimeFile("commands/tdd-log.md"), needle: "## Subcommands", name: "utility_command:tdd_log:subcommands" },
105
+ { file: runtimeFile("skills/tdd-cycle-log/SKILL.md"), needle: "## Protocol", name: "utility_skill:tdd_log:protocol" },
106
+ { file: runtimeFile("skills/tdd-cycle-log/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:tdd_log:hard_gate" },
107
+ { file: runtimeFile("commands/retro.md"), needle: "## Algorithm", name: "utility_command:retro:algorithm" },
108
+ { file: runtimeFile("skills/flow-retro/SKILL.md"), needle: "## Protocol", name: "utility_skill:retro:protocol" },
109
+ { file: runtimeFile("skills/flow-retro/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:retro:hard_gate" },
110
+ { file: runtimeFile("commands/rewind.md"), needle: "## Algorithm", name: "utility_command:rewind:algorithm" },
111
+ { file: runtimeFile("commands/rewind-ack.md"), needle: "## Algorithm", name: "utility_command:rewind_ack:algorithm" },
112
+ { file: runtimeFile("skills/flow-rewind/SKILL.md"), needle: "## Protocol", name: "utility_skill:rewind:protocol" },
113
+ { file: runtimeFile("skills/flow-rewind/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:rewind:hard_gate" },
93
114
  { file: runtimeFile("skills/subagent-dev/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:sdd:hard_gate" },
94
115
  { file: runtimeFile("skills/subagent-dev/SKILL.md"), needle: "## Status Contract", name: "utility_skill:sdd:status_contract" },
95
116
  { file: runtimeFile("skills/subagent-dev/SKILL.md"), needle: "Implementer", name: "utility_skill:sdd:implementer_template" },
@@ -158,6 +179,7 @@ export async function policyChecks(projectRoot, options = {}) {
158
179
  { file: runtimeFile("hooks/prompt-guard.sh"), needle: "write_to_cclaw_runtime", name: "hooks:guard:risky_write_advisory" },
159
180
  { file: runtimeFile("hooks/workflow-guard.sh"), needle: "stage_invocation_without_recent_flow_read", name: "hooks:workflow_guard:flow_read_reason" },
160
181
  { file: runtimeFile("hooks/workflow-guard.sh"), needle: "stage_jump_", name: "hooks:workflow_guard:stage_jump_reason" },
182
+ { file: runtimeFile("hooks/workflow-guard.sh"), needle: "tdd_write_without_open_red", name: "hooks:workflow_guard:tdd_red_first" },
161
183
  { file: runtimeFile("hooks/context-monitor.sh"), needle: "remaining is", name: "hooks:context:threshold_warning" },
162
184
  { file: runtimeFile("hooks/opencode-plugin.mjs"), needle: "activeRunId", name: "hooks:opencode:active_run" },
163
185
  { file: runtimeFile("hooks/session-start.sh"), needle: "Knowledge digest", name: "hooks:session_start:knowledge_digest" },
package/dist/runs.d.ts CHANGED
@@ -22,6 +22,7 @@ export interface ArchiveRunResult {
22
22
  archivePath: string;
23
23
  archivedAt: string;
24
24
  featureName: string;
25
+ activeFeature: string;
25
26
  resetState: FlowState;
26
27
  snapshottedStateFiles: string[];
27
28
  /** Knowledge curation hint: total active entries + soft threshold (50). */
@@ -31,20 +32,46 @@ export interface ArchiveRunResult {
31
32
  overThreshold: boolean;
32
33
  knowledgePath: string;
33
34
  };
35
+ retro: {
36
+ required: boolean;
37
+ completed: boolean;
38
+ skipped: boolean;
39
+ skipReason?: string;
40
+ compoundEntries: number;
41
+ };
34
42
  }
35
43
  export interface ArchiveManifest {
36
44
  version: 1;
37
45
  archiveId: string;
38
46
  archivedAt: string;
39
47
  featureName: string;
48
+ activeFeature: string;
40
49
  sourceRunId: string;
41
50
  sourceCurrentStage: FlowStage;
42
51
  sourceCompletedStages: FlowStage[];
43
52
  snapshottedStateFiles: string[];
53
+ retro: ArchiveRunResult["retro"];
54
+ }
55
+ export interface RewindRunOptions {
56
+ to: FlowStage;
57
+ reason?: string;
58
+ }
59
+ export interface RewindRunResult {
60
+ rewindId: string;
61
+ from: FlowStage;
62
+ to: FlowStage;
63
+ invalidatedStages: FlowStage[];
64
+ staleArtifacts: string[];
65
+ archivePath: string;
66
+ nextState: FlowState;
44
67
  }
45
68
  interface EnsureRunSystemOptions {
46
69
  createIfMissing?: boolean;
47
70
  }
71
+ export interface ArchiveRunOptions {
72
+ skipRetro?: boolean;
73
+ skipRetroReason?: string;
74
+ }
48
75
  export declare class CorruptFlowStateError extends Error {
49
76
  readonly statePath: string;
50
77
  readonly quarantinedPath: string;
@@ -54,7 +81,12 @@ export declare function readFlowState(projectRoot: string): Promise<FlowState>;
54
81
  export declare function writeFlowState(projectRoot: string, state: FlowState, options?: WriteFlowStateOptions): Promise<void>;
55
82
  export declare function ensureRunSystem(projectRoot: string, _options?: EnsureRunSystemOptions): Promise<FlowState>;
56
83
  export declare function listRuns(projectRoot: string): Promise<CclawRunMeta[]>;
57
- export declare function archiveRun(projectRoot: string, featureName?: string): Promise<ArchiveRunResult>;
84
+ export declare function archiveRun(projectRoot: string, featureName?: string, options?: ArchiveRunOptions): Promise<ArchiveRunResult>;
85
+ export declare function rewindRun(projectRoot: string, options: RewindRunOptions): Promise<RewindRunResult>;
86
+ export declare function acknowledgeStaleStage(projectRoot: string, stage: FlowStage): Promise<{
87
+ acknowledged: boolean;
88
+ remaining: FlowStage[];
89
+ }>;
58
90
  /**
59
91
  * Counts entries in the canonical JSONL knowledge store. An "active" entry is one
60
92
  * non-empty line that parses as JSON with the required `type` field belonging to the