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/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,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 \`cclaw 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
|
+
}
|
|
@@ -19,6 +19,9 @@ function checkpointPath() {
|
|
|
19
19
|
function stageActivityPath() {
|
|
20
20
|
return `${RUNTIME_ROOT}/state/stage-activity.jsonl`;
|
|
21
21
|
}
|
|
22
|
+
function snapshotPath() {
|
|
23
|
+
return `${RUNTIME_ROOT}/state/flow-state.snapshot.json`;
|
|
24
|
+
}
|
|
22
25
|
/**
|
|
23
26
|
* Command contract for /cc-status — a read-only snapshot command.
|
|
24
27
|
* Does not mutate state. Always safe to run.
|
|
@@ -30,8 +33,8 @@ export function statusCommandContract() {
|
|
|
30
33
|
|
|
31
34
|
## Purpose
|
|
32
35
|
|
|
33
|
-
**Read-only snapshot of the cclaw run.** Shows
|
|
34
|
-
gate coverage,
|
|
36
|
+
**Read-only visual snapshot of the cclaw run.** Shows progress bar, current stage,
|
|
37
|
+
gate coverage, delegation status, stale markers, and top knowledge highlights.
|
|
35
38
|
|
|
36
39
|
This command **never mutates state**. Use it at session start to orient, or at any
|
|
37
40
|
time to answer "where are we?" without advancing the flow.
|
|
@@ -41,6 +44,7 @@ time to answer "where are we?" without advancing the flow.
|
|
|
41
44
|
- **Do not** use \`/cc-status\` output to infer gate completion for decisions — cite
|
|
42
45
|
artifact evidence via \`/cc-next\` when advancing.
|
|
43
46
|
- **Do not** mutate \`${flowPath}\` or delegation log from this command.
|
|
47
|
+
- **Do not** rewrite \`${snapshotPath()}\` from this command (use \`/cc-diff\`).
|
|
44
48
|
|
|
45
49
|
## Algorithm
|
|
46
50
|
|
|
@@ -54,38 +58,32 @@ time to answer "where are we?" without advancing the flow.
|
|
|
54
58
|
- Otherwise scan \`${stageActivityPath()}\` from the end for the first entry whose \`stage\` matches \`currentStage\` and use its \`ts\`.
|
|
55
59
|
- Compute the duration as \`now - signalTimestamp\` and render compactly: \`<X>m\`, \`<X>h<Y>m\`, or \`<X>d<Y>h\`.
|
|
56
60
|
- If no signal exists, render \`(unknown)\`.
|
|
57
|
-
5.
|
|
61
|
+
5. Optionally read **\`${snapshotPath()}\`** to compute gate delta versus prior baseline:
|
|
62
|
+
- If missing or invalid, render \`delta: (baseline unavailable; run /cc-diff)\`.
|
|
63
|
+
6. Read the top of **\`${knowledgePath}\`** — surface up to 3 most recent entries
|
|
58
64
|
(by trailing timestamp or source marker).
|
|
59
|
-
|
|
65
|
+
7. Emit the visual status block described below. Do **not** load any stage skill.
|
|
66
|
+
|
|
67
|
+
## Visual markers
|
|
68
|
+
|
|
69
|
+
Default UTF markers: \`✓\` passed, \`▶\` current, \`○\` pending, \`⊘\` skipped, \`⏸\` stale, \`✗\` blocked.
|
|
70
|
+
ASCII fallback (no UTF locale): \`[x]\`, \`[>]\`, \`[ ]\`, \`[-]\`, \`[=]\`, \`[!]\`.
|
|
60
71
|
|
|
61
72
|
## Status Block Format
|
|
62
73
|
|
|
63
74
|
\`\`\`
|
|
64
75
|
cclaw status
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
delegations (current stage):
|
|
78
|
-
required: <list>
|
|
79
|
-
completed: <list>
|
|
80
|
-
pending: <list>
|
|
81
|
-
|
|
82
|
-
knowledge highlights:
|
|
83
|
-
- <latest entry summary line>
|
|
84
|
-
- <second entry summary line>
|
|
85
|
-
- <third entry summary line>
|
|
86
|
-
|
|
87
|
-
next action:
|
|
88
|
-
/cc-next (advance or resume current stage)
|
|
76
|
+
flow: <track> · run=<runId> · feature=<feature-id>
|
|
77
|
+
stage: <stage> (<N>/<total>) · time <Xd|XhYm|Xm|unknown> · mode <activeMode>
|
|
78
|
+
bar: [✓ brainstorm] [✓ scope] [▶ design] [○ spec] [○ plan] [○ tdd] [○ review] [○ ship]
|
|
79
|
+
gates: now <passed>/<required> · blocked <count> · delta <summary or baseline-unavailable>
|
|
80
|
+
delegations: [✓ <role>] [○ <role>] ...
|
|
81
|
+
stale: <list or none>
|
|
82
|
+
knowledge:
|
|
83
|
+
- <latest entry summary>
|
|
84
|
+
- <second entry summary>
|
|
85
|
+
- <third entry summary>
|
|
86
|
+
next: /cc-next · /cc-tree · /cc-diff
|
|
89
87
|
\`\`\`
|
|
90
88
|
|
|
91
89
|
## Anti-patterns
|
|
@@ -93,6 +91,7 @@ cclaw status
|
|
|
93
91
|
- Inventing gate status without reading \`${flowPath}\`.
|
|
94
92
|
- Reporting delegations as satisfied when the log says \`pending\`.
|
|
95
93
|
- Advancing the stage from \`/cc-status\` — progression belongs to \`/cc-next\`.
|
|
94
|
+
- Hiding stale stages; stale markers must be surfaced directly in the status line.
|
|
96
95
|
|
|
97
96
|
## Primary skill
|
|
98
97
|
|
|
@@ -107,7 +106,7 @@ export function statusCommandSkillMarkdown() {
|
|
|
107
106
|
const delegationPath = delegationLogPath();
|
|
108
107
|
return `---
|
|
109
108
|
name: ${STATUS_SKILL_NAME}
|
|
110
|
-
description: "Read-only snapshot of the cclaw flow
|
|
109
|
+
description: "Read-only visual snapshot of the cclaw flow with progress bar, gate delta, delegations, and stale markers."
|
|
111
110
|
---
|
|
112
111
|
|
|
113
112
|
# /cc-status — Flow Status Snapshot
|
|
@@ -120,7 +119,7 @@ advancing or mutating anything. Safe to run at any point.
|
|
|
120
119
|
## HARD-GATE
|
|
121
120
|
|
|
122
121
|
Do **not** mutate \`${flowPath}\` or \`${delegationPath}\` from this skill. This is
|
|
123
|
-
a read-only command.
|
|
122
|
+
a read-only command. Do **not** update \`${snapshotPath()}\` here.
|
|
124
123
|
|
|
125
124
|
## Algorithm
|
|
126
125
|
|
|
@@ -131,19 +130,28 @@ a read-only command.
|
|
|
131
130
|
- Prefer \`${checkpointPath()}\` when \`stage === currentStage\` and \`timestamp\` parses as ISO 8601.
|
|
132
131
|
- Else scan \`${stageActivityPath()}\` from tail for the most recent entry whose \`stage === currentStage\`; use its \`ts\`.
|
|
133
132
|
- Render \`<X>d<Y>h\`, \`<X>h<Y>m\`, \`<X>m\`, or \`(unknown)\`.
|
|
134
|
-
5.
|
|
135
|
-
|
|
133
|
+
5. Try reading \`${snapshotPath()}\` for gate delta:
|
|
134
|
+
- If available, compare current stage \`passed\` / \`blocked\` sets against baseline.
|
|
135
|
+
- If unavailable, render \`delta: (baseline unavailable; run /cc-diff)\`.
|
|
136
|
+
6. Read \`${RUNTIME_ROOT}/knowledge.jsonl\`. If missing or empty → knowledge highlights are \`(none recorded)\`. Parse each line as JSON and surface its \`trigger\`/\`action\`.
|
|
137
|
+
7. For each gate in \`stageGateCatalog[currentStage].required\`:
|
|
136
138
|
- Satisfied if present in \`passed\` and absent from \`blocked\`.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
+
8. Build and print the visual status block:
|
|
140
|
+
- stage header
|
|
141
|
+
- one-line progress bar with per-stage markers
|
|
142
|
+
- gate summary + delta
|
|
143
|
+
- delegation row
|
|
144
|
+
- stale stage row
|
|
145
|
+
9. Suggest the next action:
|
|
139
146
|
- If current stage has unmet gates → \`/cc-next\` to resume.
|
|
140
147
|
- If current stage is complete → \`/cc-next\` to advance (or report "Flow complete" if terminal).
|
|
141
148
|
|
|
142
149
|
## Output Guidelines
|
|
143
150
|
|
|
144
|
-
- Keep output compact (≤
|
|
151
|
+
- Keep output compact (≤ 30 lines) — status, not narrative.
|
|
145
152
|
- Report counts, not full artifact contents.
|
|
146
153
|
- If any data source is missing or corrupt, say so explicitly rather than guessing.
|
|
154
|
+
- Include \`/cc-tree\` for deep structure and \`/cc-diff\` for before/after map in the final line.
|
|
147
155
|
|
|
148
156
|
## Anti-patterns
|
|
149
157
|
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
|
+
const TDD_LOG_SKILL_FOLDER = "tdd-cycle-log";
|
|
3
|
+
const TDD_LOG_SKILL_NAME = "tdd-cycle-log";
|
|
4
|
+
function logPath() {
|
|
5
|
+
return `${RUNTIME_ROOT}/state/tdd-cycle-log.jsonl`;
|
|
6
|
+
}
|
|
7
|
+
function flowStatePath() {
|
|
8
|
+
return `${RUNTIME_ROOT}/state/flow-state.json`;
|
|
9
|
+
}
|
|
10
|
+
export function tddLogCommandContract() {
|
|
11
|
+
return `# /cc-tdd-log
|
|
12
|
+
|
|
13
|
+
## Purpose
|
|
14
|
+
|
|
15
|
+
Record explicit RED/GREEN/REFACTOR evidence used by workflow guard and doctor checks.
|
|
16
|
+
|
|
17
|
+
## HARD-GATE
|
|
18
|
+
|
|
19
|
+
- Every implementation write in tdd must be preceded by a logged RED event.
|
|
20
|
+
- Use append-only JSONL at \`${logPath()}\`; never rewrite prior lines.
|
|
21
|
+
|
|
22
|
+
## Subcommands
|
|
23
|
+
|
|
24
|
+
- \`/cc-tdd-log red <slice> <command> [note]\`
|
|
25
|
+
- \`/cc-tdd-log green <slice> <command> [note]\`
|
|
26
|
+
- \`/cc-tdd-log refactor <slice> <command> [note]\`
|
|
27
|
+
- \`/cc-tdd-log show\`
|
|
28
|
+
|
|
29
|
+
## Log Schema
|
|
30
|
+
|
|
31
|
+
Each JSON line must include:
|
|
32
|
+
- \`ts\` (ISO timestamp)
|
|
33
|
+
- \`runId\` (from flow-state)
|
|
34
|
+
- \`stage\` (usually \`tdd\`)
|
|
35
|
+
- \`slice\` (e.g. \`S-1\`)
|
|
36
|
+
- \`phase\` (\`red\` | \`green\` | \`refactor\`)
|
|
37
|
+
- \`command\`
|
|
38
|
+
- optional: \`files\`, \`exitCode\`, \`note\`
|
|
39
|
+
|
|
40
|
+
## Primary skill
|
|
41
|
+
|
|
42
|
+
**${RUNTIME_ROOT}/skills/${TDD_LOG_SKILL_FOLDER}/SKILL.md**
|
|
43
|
+
`;
|
|
44
|
+
}
|
|
45
|
+
export function tddLogCommandSkillMarkdown() {
|
|
46
|
+
return `---
|
|
47
|
+
name: ${TDD_LOG_SKILL_NAME}
|
|
48
|
+
description: "Append RED/GREEN/REFACTOR entries into tdd-cycle-log.jsonl for guard/doctor enforcement."
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
# /cc-tdd-log
|
|
52
|
+
|
|
53
|
+
## HARD-GATE
|
|
54
|
+
|
|
55
|
+
Do not fake RED evidence. A \`red\` entry must correspond to a failing test command.
|
|
56
|
+
|
|
57
|
+
## Protocol
|
|
58
|
+
|
|
59
|
+
1. Read \`${flowStatePath()}\` and capture \`activeRunId\` + \`currentStage\`.
|
|
60
|
+
2. Build JSON entry:
|
|
61
|
+
- \`ts\`: now ISO
|
|
62
|
+
- \`runId\`: activeRunId
|
|
63
|
+
- \`stage\`: currentStage
|
|
64
|
+
- \`slice\`: user-provided slice id
|
|
65
|
+
- \`phase\`: red|green|refactor
|
|
66
|
+
- \`command\`: test command or refactor verification command
|
|
67
|
+
3. Append one line to \`${logPath()}\`.
|
|
68
|
+
4. \`show\`: print the last 20 lines grouped by slice.
|
|
69
|
+
|
|
70
|
+
## Validation
|
|
71
|
+
|
|
72
|
+
- File remains valid JSONL (one JSON object per line).
|
|
73
|
+
- For each slice, first phase must be \`red\`.
|
|
74
|
+
`;
|
|
75
|
+
}
|
|
@@ -480,11 +480,41 @@ Execution rule: complete and verify each wave before starting the next wave.
|
|
|
480
480
|
- SHIPPED | SHIPPED_WITH_EXCEPTIONS | BLOCKED
|
|
481
481
|
- Exceptions (if any):
|
|
482
482
|
|
|
483
|
-
##
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
-
|
|
487
|
-
|
|
483
|
+
## Retro Gate Handoff
|
|
484
|
+
- Run \`/cc-retro\` before archive.
|
|
485
|
+
- Retro artifact path: \`.cclaw/artifacts/09-retro.md\`
|
|
486
|
+
- Archive remains blocked until retro gate is complete.
|
|
487
|
+
`,
|
|
488
|
+
"09-retro.md": `# Retro Artifact
|
|
489
|
+
|
|
490
|
+
## Run Summary
|
|
491
|
+
- Flow track:
|
|
492
|
+
- Scope delivered:
|
|
493
|
+
- Main outcome:
|
|
494
|
+
|
|
495
|
+
## Friction Log
|
|
496
|
+
| Category | What slowed us down | Evidence | Prevention rule |
|
|
497
|
+
|---|---|---|---|
|
|
498
|
+
| | | | |
|
|
499
|
+
|
|
500
|
+
## Acceleration Log
|
|
501
|
+
| Category | What helped | Evidence | Reuse trigger |
|
|
502
|
+
|---|---|---|---|
|
|
503
|
+
| | | | |
|
|
504
|
+
|
|
505
|
+
## Compound Decisions
|
|
506
|
+
| Insight | Trigger pattern | Action rule for next run |
|
|
507
|
+
|---|---|---|
|
|
508
|
+
| | | |
|
|
509
|
+
|
|
510
|
+
## Knowledge Writes
|
|
511
|
+
- Compound entries appended to \`.cclaw/knowledge.jsonl\`: <N>
|
|
512
|
+
- Entry ids / timestamps:
|
|
513
|
+
|
|
514
|
+
## Retro Completion
|
|
515
|
+
- RETRO_COMPLETE: yes
|
|
516
|
+
- Completed at (UTC):
|
|
517
|
+
- Notes:
|
|
488
518
|
`
|
|
489
519
|
};
|
|
490
520
|
export const RULEBOOK_MARKDOWN = `# Cclaw Rulebook
|