cclaw-cli 0.12.0 → 0.14.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 +25 -1
- package/dist/config.js +19 -8
- package/dist/constants.d.ts +2 -2
- package/dist/constants.js +16 -1
- package/dist/content/archive-command.d.ts +2 -0
- package/dist/content/archive-command.js +98 -0
- package/dist/content/contracts.js +1 -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 +11 -0
- package/dist/content/hooks.js +48 -2
- package/dist/content/learnings.d.ts +0 -2
- package/dist/content/learnings.js +4 -33
- package/dist/content/meta-skill.js +4 -2
- package/dist/content/next-command.js +18 -9
- package/dist/content/observe.d.ts +5 -1
- package/dist/content/observe.js +134 -2
- package/dist/content/ops-command.d.ts +2 -0
- package/dist/content/ops-command.js +60 -0
- package/dist/content/protocols.js +14 -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/skills.js +2 -0
- package/dist/content/stage-common-guidance.js +2 -1
- 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.d.ts +1 -1
- package/dist/content/templates.js +36 -6
- package/dist/content/tree-command.d.ts +2 -0
- package/dist/content/tree-command.js +91 -0
- package/dist/content/utility-skills.js +1 -1
- package/dist/content/view-command.d.ts +2 -0
- package/dist/content/view-command.js +57 -0
- package/dist/doctor-registry.js +3 -3
- 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 +95 -4
- package/dist/install.js +44 -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 -2
- package/package.json +1 -1
|
@@ -137,38 +137,9 @@ Do not edit source code from this command. Only operate on \`${KNOWLEDGE_PATH}\`
|
|
|
137
137
|
export function selfImprovementBlock(stageName) {
|
|
138
138
|
return `## Operational Self-Improvement
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
If yes, append one concise JSON line to the canonical knowledge store
|
|
145
|
-
(\`${KNOWLEDGE_PATH}\`) using the strict 8-field schema:
|
|
146
|
-
|
|
147
|
-
\`\`\`bash
|
|
148
|
-
TS="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
|
149
|
-
printf '%s\\n' '{"type":"pattern","trigger":"when <situation>","action":"<concrete move>","confidence":"medium","domain":null,"stage":"${stageName}","created":"'"$TS"'","project":null}' >> ${KNOWLEDGE_PATH}
|
|
150
|
-
\`\`\`
|
|
151
|
-
|
|
152
|
-
Type must be exactly one of: \`rule\`, \`pattern\`, \`lesson\`, \`compound\`.
|
|
153
|
-
Fields must appear in the order: \`type, trigger, action, confidence, domain, stage, created, project\`.
|
|
154
|
-
Missing optional values must be emitted as \`null\`, never omitted.
|
|
155
|
-
`;
|
|
156
|
-
}
|
|
157
|
-
export function learningsSearchPreamble(stage) {
|
|
158
|
-
return `## Prior Knowledge (load at stage start)
|
|
159
|
-
|
|
160
|
-
Before stage work, stream \`${KNOWLEDGE_PATH}\` and filter for entries relevant to
|
|
161
|
-
this stage (\`${stage}\`), affected domains, and key constraints. Apply matching
|
|
162
|
-
entries explicitly. If the file is empty, continue normally.
|
|
163
|
-
`;
|
|
164
|
-
}
|
|
165
|
-
export function learningsAgentsMdBlock() {
|
|
166
|
-
return `### Knowledge Store
|
|
167
|
-
|
|
168
|
-
\`${KNOWLEDGE_PATH}\` — append-only JSONL memory with entry types \`rule\`, \`pattern\`, \`lesson\`, \`compound\`.
|
|
169
|
-
Strict 8-field schema: \`type, trigger, action, confidence, domain, stage, created, project\`.
|
|
170
|
-
At session start and stage transitions, tail the file and apply relevant entries.
|
|
171
|
-
If a non-obvious reusable rule/pattern/lesson is discovered, append a new line
|
|
172
|
-
through \`/cc-learn add\` (never hand-edit).
|
|
140
|
+
Before closeout, capture 1-3 reusable insights in \`${KNOWLEDGE_PATH}\` whenever
|
|
141
|
+
the stage produced non-obvious decisions, patterns, or lessons.
|
|
142
|
+
Prefer \`type=rule|pattern|lesson\` (\`compound\` stays retro-only) and set
|
|
143
|
+
\`stage: "${stageName}"\` unless the insight is explicitly cross-stage.
|
|
173
144
|
`;
|
|
174
145
|
}
|
|
@@ -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-view, and /cc-ops."
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Using Cclaw
|
|
@@ -26,7 +26,9 @@ 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
|
+
├─ Read-only workspace view? -> /cc-view [status|tree|diff]
|
|
31
|
+
└─ Workspace operation? -> /cc-ops [feature|tdd-log|retro|archive|rewind|rewind-ack]
|
|
30
32
|
\`\`\`
|
|
31
33
|
|
|
32
34
|
## Task classification
|
|
@@ -33,17 +33,18 @@ This is the only progression command the user needs to drive the entire flow. St
|
|
|
33
33
|
|
|
34
34
|
- **Do not** invent gate completion: use only \`${flowPath}\` plus observable evidence in repo artifacts.
|
|
35
35
|
- **Do not** skip stages: advance only from \`currentStage\` to its configured successor.
|
|
36
|
-
- If the flow
|
|
36
|
+
- If the flow reaches terminal ship completion, route closeout in order: **/cc-retro -> /cc-archive**.
|
|
37
37
|
|
|
38
38
|
## Algorithm (mandatory)
|
|
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
|
|
|
@@ -53,7 +54,10 @@ This is the only progression command the user needs to drive the entire flow. St
|
|
|
53
54
|
|
|
54
55
|
### Path B: Current stage IS complete (all gates passed, all delegations satisfied)
|
|
55
56
|
|
|
56
|
-
→ If current stage's \`next\` is **\`done\`**:
|
|
57
|
+
→ If current stage's \`next\` is **\`done\`**:
|
|
58
|
+
- if \`currentStage === "ship"\` and \`retro.completedAt\` is missing -> route to \`/cc-retro\`,
|
|
59
|
+
- if \`currentStage === "ship"\` and \`retro.completedAt\` is present -> route to \`/cc-archive\`,
|
|
60
|
+
- otherwise report **"Flow complete. All stages finished."** and stop.
|
|
57
61
|
→ Otherwise: load **\`${RUNTIME_ROOT}/skills/<skillFolder>/SKILL.md\`** and **\`${RUNTIME_ROOT}/commands/<nextStage>.md\`** for the successor stage. Execute that stage's protocol.
|
|
58
62
|
|
|
59
63
|
### Track-aware successor resolution
|
|
@@ -120,7 +124,8 @@ Do **not** mark gates satisfied from memory alone. Cite **artifact evidence** (p
|
|
|
120
124
|
|
|
121
125
|
1. Open **\`${flowPath}\`**.
|
|
122
126
|
2. Record \`currentStage\` and \`stageGateCatalog[currentStage]\`.
|
|
123
|
-
3. If
|
|
127
|
+
3. If \`staleStages[currentStage]\` exists, re-run the stage and clear marker via \`/cc-rewind-ack <currentStage>\` before advancing.
|
|
128
|
+
4. If the file is missing or invalid JSON → **BLOCKED** (report and stop).
|
|
124
129
|
|
|
125
130
|
### Step 2: Evaluate gates
|
|
126
131
|
|
|
@@ -142,7 +147,11 @@ Execute the stage protocol. The stage skill handles interaction, STOP points, ga
|
|
|
142
147
|
|
|
143
148
|
**Path B — stage IS complete (all gates met, all delegations done):**
|
|
144
149
|
|
|
145
|
-
If \`next\` is \`done
|
|
150
|
+
If \`next\` is \`done\`:
|
|
151
|
+
|
|
152
|
+
- If \`currentStage\` is \`ship\` and \`retro.completedAt\` is missing -> route to \`/cc-retro\`.
|
|
153
|
+
- If \`currentStage\` is \`ship\` and \`retro.completedAt\` exists -> route to \`/cc-archive\`.
|
|
154
|
+
- Otherwise report **"Flow complete. All stages finished."** and stop.
|
|
146
155
|
|
|
147
156
|
Otherwise load the next stage's skill and command contract, begin execution.
|
|
148
157
|
|
|
@@ -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;
|
package/dist/content/observe.js
CHANGED
|
@@ -153,18 +153,25 @@ fi
|
|
|
153
153
|
exit 0
|
|
154
154
|
`;
|
|
155
155
|
}
|
|
156
|
-
export function workflowGuardScript() {
|
|
156
|
+
export function workflowGuardScript(options = {}) {
|
|
157
|
+
const tddEnforcementMode = options.tddEnforcementMode === "strict" ? "strict" : "advisory";
|
|
158
|
+
const tddTestGlobs = options.tddTestGlobs && options.tddTestGlobs.length > 0
|
|
159
|
+
? options.tddTestGlobs.join(",")
|
|
160
|
+
: "**/*.test.*,**/*.spec.*,**/test/**";
|
|
157
161
|
return `#!/usr/bin/env bash
|
|
158
162
|
# cclaw workflow guard hook — generated by cclaw sync
|
|
159
163
|
# Enforces stage-aware command discipline and recent flow-state read hygiene.
|
|
160
164
|
set -uo pipefail
|
|
161
165
|
WORKFLOW_GUARD_MODE="\${CCLAW_WORKFLOW_GUARD_MODE:-advisory}"
|
|
162
166
|
MAX_FLOW_READ_AGE_SEC="\${CCLAW_WORKFLOW_GUARD_MAX_AGE_SEC:-1800}"
|
|
167
|
+
TDD_ENFORCEMENT_MODE="${tddEnforcementMode}"
|
|
168
|
+
TDD_TEST_GLOBS="${tddTestGlobs}"
|
|
163
169
|
|
|
164
170
|
${RUNTIME_SHELL_DETECT_ROOT}
|
|
165
171
|
|
|
166
172
|
STATE_DIR="$ROOT/${RUNTIME_ROOT}/state"
|
|
167
173
|
FLOW_STATE_FILE="$STATE_DIR/flow-state.json"
|
|
174
|
+
TDD_LOG_FILE="$STATE_DIR/tdd-cycle-log.jsonl"
|
|
168
175
|
GUARD_STATE_FILE="$STATE_DIR/workflow-guard.json"
|
|
169
176
|
GUARD_LOG="$STATE_DIR/workflow-guard.jsonl"
|
|
170
177
|
mkdir -p "$STATE_DIR" 2>/dev/null || true
|
|
@@ -234,9 +241,11 @@ NOW_EPOCH=$(date +%s 2>/dev/null || echo "0")
|
|
|
234
241
|
REASONS=""
|
|
235
242
|
|
|
236
243
|
CURRENT_STAGE="none"
|
|
244
|
+
CURRENT_RUN="active"
|
|
237
245
|
if [ -f "$FLOW_STATE_FILE" ]; then
|
|
238
246
|
if command -v jq >/dev/null 2>&1; then
|
|
239
247
|
CURRENT_STAGE=$(jq -r '.currentStage // "none"' "$FLOW_STATE_FILE" 2>/dev/null || echo "none")
|
|
248
|
+
CURRENT_RUN=$(jq -r '.activeRunId // "active"' "$FLOW_STATE_FILE" 2>/dev/null || echo "active")
|
|
240
249
|
elif command -v python3 >/dev/null 2>&1; then
|
|
241
250
|
CURRENT_STAGE=$(python3 - "$FLOW_STATE_FILE" <<'PY'
|
|
242
251
|
import json
|
|
@@ -252,6 +261,21 @@ except Exception:
|
|
|
252
261
|
pass
|
|
253
262
|
print(stage)
|
|
254
263
|
PY
|
|
264
|
+
)
|
|
265
|
+
CURRENT_RUN=$(python3 - "$FLOW_STATE_FILE" <<'PY'
|
|
266
|
+
import json
|
|
267
|
+
import sys
|
|
268
|
+
run_id = "active"
|
|
269
|
+
try:
|
|
270
|
+
with open(sys.argv[1], "r", encoding="utf-8") as fh:
|
|
271
|
+
parsed = json.load(fh)
|
|
272
|
+
value = parsed.get("activeRunId")
|
|
273
|
+
if isinstance(value, str) and value:
|
|
274
|
+
run_id = value
|
|
275
|
+
except Exception:
|
|
276
|
+
pass
|
|
277
|
+
print(run_id)
|
|
278
|
+
PY
|
|
255
279
|
)
|
|
256
280
|
fi
|
|
257
281
|
fi
|
|
@@ -325,6 +349,99 @@ is_preimplementation_stage() {
|
|
|
325
349
|
esac
|
|
326
350
|
}
|
|
327
351
|
|
|
352
|
+
is_tdd_test_payload() {
|
|
353
|
+
local text="$1"
|
|
354
|
+
if printf '%s' "$text" | grep -Eq '/tests?/|\\.test\\.|\\.spec\\.'; then
|
|
355
|
+
return 0
|
|
356
|
+
fi
|
|
357
|
+
if printf '%s' "$TDD_TEST_GLOBS" | grep -Eq '.' && printf '%s' "$text" | grep -Eq '(test|spec)'; then
|
|
358
|
+
return 0
|
|
359
|
+
fi
|
|
360
|
+
return 1
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
is_tdd_runtime_write_payload() {
|
|
364
|
+
local text="$1"
|
|
365
|
+
if printf '%s' "$text" | grep -Eq '\\.cclaw/'; then
|
|
366
|
+
return 1
|
|
367
|
+
fi
|
|
368
|
+
if ! printf '%s' "$text" | grep -Eq '\\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|kt|rb|php|cs|swift)'; then
|
|
369
|
+
return 1
|
|
370
|
+
fi
|
|
371
|
+
if is_tdd_test_payload "$text"; then
|
|
372
|
+
return 1
|
|
373
|
+
fi
|
|
374
|
+
return 0
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
has_open_red_cycle() {
|
|
378
|
+
if [ ! -f "$TDD_LOG_FILE" ] || [ ! -s "$TDD_LOG_FILE" ]; then
|
|
379
|
+
return 1
|
|
380
|
+
fi
|
|
381
|
+
local red_count="0"
|
|
382
|
+
local green_count="0"
|
|
383
|
+
if command -v jq >/dev/null 2>&1; then
|
|
384
|
+
red_count=$(jq -r --arg run "$CURRENT_RUN" 'select((.runId // $run) == $run and .phase == "red") | .phase' "$TDD_LOG_FILE" 2>/dev/null | wc -l | tr -d ' ' || echo "0")
|
|
385
|
+
green_count=$(jq -r --arg run "$CURRENT_RUN" 'select((.runId // $run) == $run and .phase == "green") | .phase' "$TDD_LOG_FILE" 2>/dev/null | wc -l | tr -d ' ' || echo "0")
|
|
386
|
+
elif command -v python3 >/dev/null 2>&1; then
|
|
387
|
+
red_count=$(python3 - "$TDD_LOG_FILE" "$CURRENT_RUN" <<'PY'
|
|
388
|
+
import json
|
|
389
|
+
import sys
|
|
390
|
+
count = 0
|
|
391
|
+
run_id = sys.argv[2]
|
|
392
|
+
with open(sys.argv[1], "r", encoding="utf-8") as fh:
|
|
393
|
+
for raw in fh:
|
|
394
|
+
raw = raw.strip()
|
|
395
|
+
if not raw:
|
|
396
|
+
continue
|
|
397
|
+
try:
|
|
398
|
+
parsed = json.loads(raw)
|
|
399
|
+
except Exception:
|
|
400
|
+
continue
|
|
401
|
+
if not isinstance(parsed, dict):
|
|
402
|
+
continue
|
|
403
|
+
if str(parsed.get("runId", run_id)) != run_id:
|
|
404
|
+
continue
|
|
405
|
+
if parsed.get("phase") == "red":
|
|
406
|
+
count += 1
|
|
407
|
+
print(count)
|
|
408
|
+
PY
|
|
409
|
+
)
|
|
410
|
+
green_count=$(python3 - "$TDD_LOG_FILE" "$CURRENT_RUN" <<'PY'
|
|
411
|
+
import json
|
|
412
|
+
import sys
|
|
413
|
+
count = 0
|
|
414
|
+
run_id = sys.argv[2]
|
|
415
|
+
with open(sys.argv[1], "r", encoding="utf-8") as fh:
|
|
416
|
+
for raw in fh:
|
|
417
|
+
raw = raw.strip()
|
|
418
|
+
if not raw:
|
|
419
|
+
continue
|
|
420
|
+
try:
|
|
421
|
+
parsed = json.loads(raw)
|
|
422
|
+
except Exception:
|
|
423
|
+
continue
|
|
424
|
+
if not isinstance(parsed, dict):
|
|
425
|
+
continue
|
|
426
|
+
if str(parsed.get("runId", run_id)) != run_id:
|
|
427
|
+
continue
|
|
428
|
+
if parsed.get("phase") == "green":
|
|
429
|
+
count += 1
|
|
430
|
+
print(count)
|
|
431
|
+
PY
|
|
432
|
+
)
|
|
433
|
+
else
|
|
434
|
+
red_count=$(grep -ci '"phase"[[:space:]]*:[[:space:]]*"red"' "$TDD_LOG_FILE" 2>/dev/null || echo "0")
|
|
435
|
+
green_count=$(grep -ci '"phase"[[:space:]]*:[[:space:]]*"green"' "$TDD_LOG_FILE" 2>/dev/null || echo "0")
|
|
436
|
+
fi
|
|
437
|
+
[ -n "$red_count" ] || red_count="0"
|
|
438
|
+
[ -n "$green_count" ] || green_count="0"
|
|
439
|
+
if [ "$red_count" -gt "$green_count" ]; then
|
|
440
|
+
return 0
|
|
441
|
+
fi
|
|
442
|
+
return 1
|
|
443
|
+
}
|
|
444
|
+
|
|
328
445
|
detect_target_stage() {
|
|
329
446
|
local text="$1"
|
|
330
447
|
for stage in brainstorm scope design spec plan tdd review ship; do
|
|
@@ -373,6 +490,18 @@ if is_preimplementation_stage "$CURRENT_STAGE" && is_mutating_tool "$TOOL_LOWER"
|
|
|
373
490
|
fi
|
|
374
491
|
fi
|
|
375
492
|
|
|
493
|
+
if [ "$CURRENT_STAGE" = "tdd" ] && is_mutating_tool "$TOOL_LOWER"; then
|
|
494
|
+
if is_tdd_runtime_write_payload "$PAYLOAD_LOWER"; then
|
|
495
|
+
if ! has_open_red_cycle; then
|
|
496
|
+
if [ -n "$REASONS" ]; then
|
|
497
|
+
REASONS="$REASONS,tdd_write_without_open_red"
|
|
498
|
+
else
|
|
499
|
+
REASONS="tdd_write_without_open_red"
|
|
500
|
+
fi
|
|
501
|
+
fi
|
|
502
|
+
fi
|
|
503
|
+
fi
|
|
504
|
+
|
|
376
505
|
if is_preimplementation_stage "$CURRENT_STAGE" && ! is_plan_mode_safe_tool "$TOOL_LOWER"; then
|
|
377
506
|
if ! is_mutating_tool "$TOOL_LOWER"; then
|
|
378
507
|
if ! printf '%s' "$PAYLOAD_LOWER" | grep -Eq '\.cclaw/' && ! is_cclaw_cli_payload "$PAYLOAD_LOWER"; then
|
|
@@ -438,7 +567,7 @@ PY
|
|
|
438
567
|
fi
|
|
439
568
|
|
|
440
569
|
if [ -n "$REASONS" ]; then
|
|
441
|
-
NOTE="Cclaw workflow guard: detected potential flow violation (\${REASONS}). Re-read ${RUNTIME_ROOT}/state/flow-state.json, avoid source edits before tdd stage, and
|
|
570
|
+
NOTE="Cclaw workflow guard: detected potential flow violation (\${REASONS}). Re-read ${RUNTIME_ROOT}/state/flow-state.json, avoid source edits before tdd stage, and enforce RED -> GREEN -> REFACTOR discipline inside tdd."
|
|
442
571
|
if command -v jq >/dev/null 2>&1; then
|
|
443
572
|
ENTRY=$(jq -n -c \
|
|
444
573
|
--arg ts "$TS" \
|
|
@@ -458,6 +587,9 @@ if [ -n "$REASONS" ]; then
|
|
|
458
587
|
if printf '%s' "$REASONS" | grep -Eq 'implementation_write_before_'; then
|
|
459
588
|
SHOULD_BLOCK="true"
|
|
460
589
|
fi
|
|
590
|
+
if printf '%s' "$REASONS" | grep -Eq 'tdd_write_without_open_red' && [ "$TDD_ENFORCEMENT_MODE" = "strict" ]; then
|
|
591
|
+
SHOULD_BLOCK="true"
|
|
592
|
+
fi
|
|
461
593
|
if [ "$WORKFLOW_GUARD_MODE" = "strict" ] || [ "$SHOULD_BLOCK" = "true" ]; then
|
|
462
594
|
printf '[cclaw] %s (blocked by workflow guard)\n' "$NOTE" >&2
|
|
463
595
|
exit 1
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
|
+
const OPS_SKILL_FOLDER = "flow-ops";
|
|
3
|
+
const OPS_SKILL_NAME = "flow-ops";
|
|
4
|
+
export function opsCommandContract() {
|
|
5
|
+
return `# /cc-ops
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Unified operational command surface for non-stage flow actions.
|
|
10
|
+
|
|
11
|
+
Subcommands:
|
|
12
|
+
- \`feature\` -> \`/cc-feature\`
|
|
13
|
+
- \`tdd-log\` -> \`/cc-tdd-log\`
|
|
14
|
+
- \`retro\` -> \`/cc-retro\`
|
|
15
|
+
- \`archive\` -> \`/cc-archive\`
|
|
16
|
+
- \`rewind\` -> \`/cc-rewind\`
|
|
17
|
+
- \`rewind-ack\` -> \`/cc-rewind-ack\`
|
|
18
|
+
|
|
19
|
+
## HARD-GATE
|
|
20
|
+
|
|
21
|
+
- \`/cc-ops\` is a routing wrapper; execute only one target subcommand per call.
|
|
22
|
+
- Preserve target command safety contracts (retro gate, archive gate, rewind atomicity, etc.).
|
|
23
|
+
|
|
24
|
+
## Routing
|
|
25
|
+
|
|
26
|
+
1. Parse required subcommand token.
|
|
27
|
+
2. Dispatch:
|
|
28
|
+
- \`feature\` -> \`${RUNTIME_ROOT}/commands/feature.md\`
|
|
29
|
+
- \`tdd-log\` -> \`${RUNTIME_ROOT}/commands/tdd-log.md\`
|
|
30
|
+
- \`retro\` -> \`${RUNTIME_ROOT}/commands/retro.md\`
|
|
31
|
+
- \`archive\` -> \`${RUNTIME_ROOT}/commands/archive.md\`
|
|
32
|
+
- \`rewind\` -> \`${RUNTIME_ROOT}/commands/rewind.md\`
|
|
33
|
+
- \`rewind-ack\` -> \`${RUNTIME_ROOT}/commands/rewind-ack.md\`
|
|
34
|
+
3. Unknown subcommand -> print supported values and stop.
|
|
35
|
+
|
|
36
|
+
## Primary skill
|
|
37
|
+
|
|
38
|
+
**${RUNTIME_ROOT}/skills/${OPS_SKILL_FOLDER}/SKILL.md**
|
|
39
|
+
`;
|
|
40
|
+
}
|
|
41
|
+
export function opsCommandSkillMarkdown() {
|
|
42
|
+
return `---
|
|
43
|
+
name: ${OPS_SKILL_NAME}
|
|
44
|
+
description: "Unified operational router for feature/tdd-log/retro/archive/rewind commands."
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
# /cc-ops
|
|
48
|
+
|
|
49
|
+
## HARD-GATE
|
|
50
|
+
|
|
51
|
+
This wrapper only dispatches. It must not apply state mutations itself.
|
|
52
|
+
|
|
53
|
+
## Protocol
|
|
54
|
+
|
|
55
|
+
1. Require a subcommand (\`feature|tdd-log|retro|archive|rewind|rewind-ack\`).
|
|
56
|
+
2. Route to the matching command contract + skill pair.
|
|
57
|
+
3. Preserve pass-through args after the subcommand (e.g. \`/cc-ops rewind design\`).
|
|
58
|
+
4. Echo which subcommand was dispatched for auditability.
|
|
59
|
+
`;
|
|
60
|
+
}
|
|
@@ -46,8 +46,20 @@ Shared closeout sequence applied by every stage skill.
|
|
|
46
46
|
- update \`guardEvidence\`.
|
|
47
47
|
3. Persist stage artifact under \`.cclaw/artifacts/\`.
|
|
48
48
|
4. Run \`npx cclaw doctor\` and resolve failures.
|
|
49
|
-
5.
|
|
50
|
-
|
|
49
|
+
5. Capture reusable learnings from this stage artifact:
|
|
50
|
+
- append 1-3 strict-schema JSONL entries when the stage produced non-obvious
|
|
51
|
+
decisions, patterns, or lessons,
|
|
52
|
+
- use \`type=rule|pattern|lesson\` (\`compound\` stays retro-focused).
|
|
53
|
+
6. Notify user with stage completion and next action (\`/cc-next\`).
|
|
54
|
+
7. Stop; do not auto-run the next stage unless user asks.
|
|
55
|
+
|
|
56
|
+
## Automatic learning capture policy
|
|
57
|
+
|
|
58
|
+
- \`standard\` / \`medium\` tracks: required for \`design\`, \`tdd\`, and \`review\`;
|
|
59
|
+
recommended for other stages.
|
|
60
|
+
- \`quick\` track: recommended only (avoid overhead for tiny fixes).
|
|
61
|
+
- "No learning captured" is acceptable only when explicitly justified (e.g. pure
|
|
62
|
+
mechanical change, no new trade-offs).
|
|
51
63
|
|
|
52
64
|
## Resume protocol
|
|
53
65
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
|
+
const RETRO_SKILL_FOLDER = "flow-retro";
|
|
3
|
+
const RETRO_SKILL_NAME = "flow-retro";
|
|
4
|
+
function flowStatePath() {
|
|
5
|
+
return `${RUNTIME_ROOT}/state/flow-state.json`;
|
|
6
|
+
}
|
|
7
|
+
function retroArtifactPath() {
|
|
8
|
+
return `${RUNTIME_ROOT}/artifacts/09-retro.md`;
|
|
9
|
+
}
|
|
10
|
+
function knowledgePath() {
|
|
11
|
+
return `${RUNTIME_ROOT}/knowledge.jsonl`;
|
|
12
|
+
}
|
|
13
|
+
export function retroCommandContract() {
|
|
14
|
+
return `# /cc-retro
|
|
15
|
+
|
|
16
|
+
## Purpose
|
|
17
|
+
|
|
18
|
+
Mandatory retrospective gate before archive once ship is complete.
|
|
19
|
+
|
|
20
|
+
## HARD-GATE
|
|
21
|
+
|
|
22
|
+
- Do not mark retro complete without writing \`${retroArtifactPath()}\`.
|
|
23
|
+
- Do not finish retro without appending at least one \`type=compound\` entry into \`${knowledgePath()}\`.
|
|
24
|
+
|
|
25
|
+
## Algorithm
|
|
26
|
+
|
|
27
|
+
1. Read \`${flowStatePath()}\`; confirm ship stage is complete for current run.
|
|
28
|
+
2. Synthesize retrospective artifact \`${retroArtifactPath()}\` with:
|
|
29
|
+
- what slowed this run
|
|
30
|
+
- what accelerated this run
|
|
31
|
+
- concrete repeatable rule for next run
|
|
32
|
+
3. Append >=1 strict-schema JSONL entry to \`${knowledgePath()}\` with:
|
|
33
|
+
- \`type: "compound"\`
|
|
34
|
+
- \`stage: "ship"\` or \`"retro"\`
|
|
35
|
+
4. Update flow-state \`retro\` block:
|
|
36
|
+
- \`required: true\`
|
|
37
|
+
- \`completedAt: <ISO>\`
|
|
38
|
+
- \`compoundEntries: <count>\`
|
|
39
|
+
5. Report completion summary and remind user that \`/cc-archive\` is now unblocked.
|
|
40
|
+
|
|
41
|
+
## Primary skill
|
|
42
|
+
|
|
43
|
+
**${RUNTIME_ROOT}/skills/${RETRO_SKILL_FOLDER}/SKILL.md**
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
export function retroCommandSkillMarkdown() {
|
|
47
|
+
return `---
|
|
48
|
+
name: ${RETRO_SKILL_NAME}
|
|
49
|
+
description: "Run mandatory retrospective and record compound knowledge before archive."
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
# /cc-retro
|
|
53
|
+
|
|
54
|
+
## HARD-GATE
|
|
55
|
+
|
|
56
|
+
Archive must remain blocked until retro artifact exists and compound knowledge was appended.
|
|
57
|
+
|
|
58
|
+
## Protocol
|
|
59
|
+
|
|
60
|
+
1. Confirm ship completion from \`${flowStatePath()}\`.
|
|
61
|
+
2. Create/update \`${retroArtifactPath()}\` with concise retrospective sections:
|
|
62
|
+
- outcomes
|
|
63
|
+
- bottlenecks
|
|
64
|
+
- reusable acceleration patterns
|
|
65
|
+
3. Append at least one \`compound\` knowledge entry into \`${knowledgePath()}\`.
|
|
66
|
+
4. Update \`flow-state.json.retro\` with completion timestamp + compound count.
|
|
67
|
+
5. Print explicit completion line:
|
|
68
|
+
- \`retro gate: complete\`
|
|
69
|
+
- \`compound entries added: <N>\`
|
|
70
|
+
|
|
71
|
+
## Validation
|
|
72
|
+
|
|
73
|
+
- \`${retroArtifactPath()}\` exists and is non-empty.
|
|
74
|
+
- \`${knowledgePath()}\` contains >=1 valid \`compound\` line.
|
|
75
|
+
- \`retro.completedAt\` is set in flow-state.
|
|
76
|
+
`;
|
|
77
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
|
+
const REWIND_SKILL_FOLDER = "flow-rewind";
|
|
3
|
+
const REWIND_SKILL_NAME = "flow-rewind";
|
|
4
|
+
function flowStatePath() {
|
|
5
|
+
return `${RUNTIME_ROOT}/state/flow-state.json`;
|
|
6
|
+
}
|
|
7
|
+
function artifactsPath() {
|
|
8
|
+
return `${RUNTIME_ROOT}/artifacts`;
|
|
9
|
+
}
|
|
10
|
+
function rewindLogPath() {
|
|
11
|
+
return `${RUNTIME_ROOT}/state/rewind-log.jsonl`;
|
|
12
|
+
}
|
|
13
|
+
export function rewindCommandContract() {
|
|
14
|
+
return `# /cc-rewind
|
|
15
|
+
|
|
16
|
+
## Purpose
|
|
17
|
+
|
|
18
|
+
Rewind active flow to an earlier stage and atomically invalidate downstream work.
|
|
19
|
+
|
|
20
|
+
## HARD-GATE
|
|
21
|
+
|
|
22
|
+
- Never rewind without preserving downstream artifact history.
|
|
23
|
+
- Mark downstream stages as stale; do not leave completedStages pointing to invalidated work.
|
|
24
|
+
- Record a rewind reason in \`${rewindLogPath()}\`.
|
|
25
|
+
|
|
26
|
+
## Inputs
|
|
27
|
+
|
|
28
|
+
\`/cc-rewind <target-stage> [reason]\`
|
|
29
|
+
|
|
30
|
+
## Algorithm
|
|
31
|
+
|
|
32
|
+
1. Read \`${flowStatePath()}\` and current track.
|
|
33
|
+
2. Validate \`target-stage\` belongs to the active track and is not ahead of current stage.
|
|
34
|
+
3. Compute downstream stages to invalidate (all stages after target that were completed or current).
|
|
35
|
+
4. Archive downstream artifacts into \`${artifactsPath()}/_rewind-archive/<rewind-id>/\`.
|
|
36
|
+
5. Rename active downstream artifacts to \`*.stale.md\`.
|
|
37
|
+
6. Update flow-state:
|
|
38
|
+
- \`currentStage = target-stage\`
|
|
39
|
+
- trim \`completedStages\` to stages before target-stage
|
|
40
|
+
- clear gate evidence/catalog for target-stage and downstream
|
|
41
|
+
- mark downstream entries in \`staleStages\`
|
|
42
|
+
- append \`rewinds[]\` record
|
|
43
|
+
7. Append JSON line to \`${rewindLogPath()}\`.
|
|
44
|
+
|
|
45
|
+
## Output
|
|
46
|
+
|
|
47
|
+
- Rewind id
|
|
48
|
+
- from -> to stage
|
|
49
|
+
- Invalidated stages list
|
|
50
|
+
- Number of stale artifacts
|
|
51
|
+
|
|
52
|
+
## Primary skill
|
|
53
|
+
|
|
54
|
+
**${RUNTIME_ROOT}/skills/${REWIND_SKILL_FOLDER}/SKILL.md**
|
|
55
|
+
`;
|
|
56
|
+
}
|
|
57
|
+
export function rewindAcknowledgeCommandContract() {
|
|
58
|
+
return `# /cc-rewind-ack
|
|
59
|
+
|
|
60
|
+
## Purpose
|
|
61
|
+
|
|
62
|
+
Acknowledge and clear stale-stage markers after downstream work is intentionally redone.
|
|
63
|
+
|
|
64
|
+
## Input
|
|
65
|
+
|
|
66
|
+
\`/cc-rewind-ack <stage>\`
|
|
67
|
+
|
|
68
|
+
## HARD-GATE
|
|
69
|
+
|
|
70
|
+
- Only clear stale marker for the requested stage.
|
|
71
|
+
- Never modify completedStages from this command.
|
|
72
|
+
|
|
73
|
+
## Algorithm
|
|
74
|
+
|
|
75
|
+
1. Read \`${flowStatePath()}\`.
|
|
76
|
+
2. If \`staleStages.<stage>\` is missing, report no-op.
|
|
77
|
+
3. Remove \`staleStages.<stage>\`.
|
|
78
|
+
4. Write updated flow-state.
|
|
79
|
+
5. Print remaining stale stages (if any).
|
|
80
|
+
|
|
81
|
+
## Primary skill
|
|
82
|
+
|
|
83
|
+
**${RUNTIME_ROOT}/skills/${REWIND_SKILL_FOLDER}/SKILL.md**
|
|
84
|
+
`;
|
|
85
|
+
}
|
|
86
|
+
export function rewindCommandSkillMarkdown() {
|
|
87
|
+
return `---
|
|
88
|
+
name: ${REWIND_SKILL_NAME}
|
|
89
|
+
description: "Rewind active flow stage safely and acknowledge stale invalidations."
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
# /cc-rewind + /cc-rewind-ack
|
|
93
|
+
|
|
94
|
+
## HARD-GATE
|
|
95
|
+
|
|
96
|
+
Rewind is an atomic state transition. Never leave flow-state half-updated (for example currentStage changed but stale markers/artifact archive missing).
|
|
97
|
+
|
|
98
|
+
## Protocol
|
|
99
|
+
|
|
100
|
+
### rewind
|
|
101
|
+
1. Validate target stage belongs to current track and is upstream.
|
|
102
|
+
2. Archive downstream artifacts under \`${artifactsPath()}/_rewind-archive/<rewind-id>/\`.
|
|
103
|
+
3. Mark downstream artifacts as stale (\`*.stale.md\`).
|
|
104
|
+
4. Reset downstream gate catalog and guard evidence.
|
|
105
|
+
5. Record \`rewinds[]\` and \`staleStages\` in flow-state.
|
|
106
|
+
6. Append rewind entry into \`${rewindLogPath()}\`.
|
|
107
|
+
|
|
108
|
+
### rewind-ack
|
|
109
|
+
1. Load flow-state stale map.
|
|
110
|
+
2. Remove exactly one stale stage marker.
|
|
111
|
+
3. Report remaining stale stages.
|
|
112
|
+
|
|
113
|
+
## Validation checklist
|
|
114
|
+
|
|
115
|
+
- \`${flowStatePath()}\` remains valid JSON.
|
|
116
|
+
- \`currentStage\` equals requested rewind target.
|
|
117
|
+
- invalidated stages are absent from \`completedStages\`.
|
|
118
|
+
- archived copies exist for each moved artifact.
|
|
119
|
+
`;
|
|
120
|
+
}
|
package/dist/content/skills.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
2
|
import { STAGE_EXAMPLES_REFERENCE_DIR, stageDomainExamples, stageExamples, stageGoodBadExamples } from "./examples.js";
|
|
3
|
+
import { selfImprovementBlock } from "./learnings.js";
|
|
3
4
|
import { STAGE_COMMON_GUIDANCE_REL_PATH } from "./stage-common-guidance.js";
|
|
4
5
|
import { stageAutoSubagentDispatch, stageSchema } from "./stage-schema.js";
|
|
5
6
|
const VERIFICATION_STAGES = ["tdd", "review", "ship"];
|
|
@@ -377,6 +378,7 @@ ${mergedAntiPatterns(schema)}
|
|
|
377
378
|
## Verification
|
|
378
379
|
${schema.exitCriteria.map((item) => `- [ ] ${item}`).join("\n")}
|
|
379
380
|
|
|
381
|
+
${selfImprovementBlock(schema.stage)}
|
|
380
382
|
${completionParametersBlock(schema)}
|
|
381
383
|
## Shared Stage Guidance
|
|
382
384
|
See:
|
|
@@ -59,7 +59,8 @@ Rollback / fallback: <if decision proves wrong>
|
|
|
59
59
|
## Self-improvement reminder
|
|
60
60
|
|
|
61
61
|
If a reusable lesson appears during the stage, append one strict-schema JSONL
|
|
62
|
-
entry via \`/cc-learn add
|
|
62
|
+
entry (manually via \`/cc-learn add\` or directly in stage closeout protocol).
|
|
63
|
+
Do not keep operational lessons only in chat.
|
|
63
64
|
|
|
64
65
|
## Progressive disclosure baseline
|
|
65
66
|
|