cclaw-cli 0.5.17 → 0.6.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 CHANGED
@@ -1,9 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import type { HarnessId } from "./types.js";
2
+ import type { FlowTrack, HarnessId } from "./types.js";
3
3
  type CommandName = "init" | "sync" | "doctor" | "upgrade" | "uninstall" | "archive";
4
4
  interface ParsedArgs {
5
5
  command?: CommandName;
6
6
  harnesses?: HarnessId[];
7
+ track?: FlowTrack;
7
8
  reconcileGates?: boolean;
8
9
  archiveName?: string;
9
10
  showHelp?: boolean;
@@ -11,5 +12,6 @@ interface ParsedArgs {
11
12
  }
12
13
  export declare function usage(): string;
13
14
  declare function parseHarnesses(raw: string): HarnessId[];
15
+ declare function parseTrack(raw: string): FlowTrack;
14
16
  declare function parseArgs(argv: string[]): ParsedArgs;
15
- export { parseArgs, parseHarnesses };
17
+ export { parseArgs, parseHarnesses, parseTrack };
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import { readFileSync, realpathSync } from "node:fs";
3
3
  import process from "node:process";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
- import { HARNESS_IDS } from "./types.js";
6
+ import { FLOW_TRACKS, HARNESS_IDS } from "./types.js";
7
7
  import { doctorChecks, doctorSucceeded } from "./doctor.js";
8
8
  import { initCclaw, syncCclaw, uninstallCclaw, upgradeCclaw } from "./install.js";
9
9
  import { error, info } from "./logger.js";
@@ -20,6 +20,7 @@ Usage:
20
20
  Commands:
21
21
  init Bootstrap .cclaw runtime, state, and harness shims in this project.
22
22
  Flags: --harnesses=<list> Comma list of harnesses (claude,cursor,opencode,codex).
23
+ --track=<id> Flow track for new runs (standard | quick). Default: standard.
23
24
  sync Regenerate harness shim files from the current .cclaw config (non-destructive).
24
25
  doctor Run health checks against the local .cclaw runtime. Exit code 2 on failure.
25
26
  Flags: --reconcile-gates Recompute current-stage gate evidence before checks.
@@ -77,6 +78,13 @@ function parseHarnesses(raw) {
77
78
  }
78
79
  return requested;
79
80
  }
81
+ function parseTrack(raw) {
82
+ const trimmed = raw.trim();
83
+ if (!FLOW_TRACKS.includes(trimmed)) {
84
+ throw new Error(`Unknown track: ${trimmed}. Supported: ${FLOW_TRACKS.join(", ")}`);
85
+ }
86
+ return trimmed;
87
+ }
80
88
  function parseArgs(argv) {
81
89
  const parsed = {};
82
90
  const helpFlag = argv.find((arg) => arg === "--help" || arg === "-h");
@@ -96,6 +104,10 @@ function parseArgs(argv) {
96
104
  parsed.harnesses = parseHarnesses(flag.replace("--harnesses=", ""));
97
105
  continue;
98
106
  }
107
+ if (flag.startsWith("--track=")) {
108
+ parsed.track = parseTrack(flag.replace("--track=", ""));
109
+ continue;
110
+ }
99
111
  if (flag === "--reconcile-gates") {
100
112
  parsed.reconcileGates = true;
101
113
  continue;
@@ -123,9 +135,11 @@ async function runCommand(parsed, ctx) {
123
135
  if (command === "init") {
124
136
  await initCclaw({
125
137
  projectRoot: ctx.cwd,
126
- harnesses: parsed.harnesses
138
+ harnesses: parsed.harnesses,
139
+ track: parsed.track
127
140
  });
128
- info(ctx, "Initialized .cclaw runtime and generated harness shims");
141
+ const trackNote = parsed.track ? ` (track: ${parsed.track})` : "";
142
+ info(ctx, `Initialized .cclaw runtime and generated harness shims${trackNote}`);
129
143
  return 0;
130
144
  }
131
145
  if (command === "sync") {
@@ -190,4 +204,4 @@ function isDirectExecution() {
190
204
  if (isDirectExecution()) {
191
205
  void main();
192
206
  }
193
- export { parseArgs, parseHarnesses };
207
+ export { parseArgs, parseHarnesses, parseTrack };
package/dist/config.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import type { HarnessId, VibyConfig } from "./types.js";
1
+ import type { FlowTrack, HarnessId, VibyConfig } from "./types.js";
2
2
  export declare function configPath(projectRoot: string): string;
3
- export declare function createDefaultConfig(harnesses?: HarnessId[]): VibyConfig;
3
+ export declare function createDefaultConfig(harnesses?: HarnessId[], defaultTrack?: FlowTrack): VibyConfig;
4
4
  export declare function readConfig(projectRoot: string): Promise<VibyConfig>;
5
5
  export declare function writeConfig(projectRoot: string, config: VibyConfig): Promise<void>;
package/dist/config.js CHANGED
@@ -3,17 +3,20 @@ import path from "node:path";
3
3
  import { parse, stringify } from "yaml";
4
4
  import { CCLAW_VERSION, DEFAULT_HARNESSES, FLOW_VERSION, RUNTIME_ROOT } from "./constants.js";
5
5
  import { exists, writeFileSafe } from "./fs-utils.js";
6
- import { HARNESS_IDS } from "./types.js";
6
+ import { FLOW_TRACKS, HARNESS_IDS } from "./types.js";
7
7
  const CONFIG_PATH = `${RUNTIME_ROOT}/config.yaml`;
8
8
  const HARNESS_ID_SET = new Set(HARNESS_IDS);
9
+ const FLOW_TRACK_SET = new Set(FLOW_TRACKS);
9
10
  const SUPPORTED_HARNESSES_TEXT = HARNESS_IDS.join(", ");
11
+ const SUPPORTED_TRACKS_TEXT = FLOW_TRACKS.join(", ");
10
12
  const ALLOWED_CONFIG_KEYS = new Set([
11
13
  "version",
12
14
  "flowVersion",
13
15
  "harnesses",
14
16
  "autoAdvance",
15
17
  "promptGuardMode",
16
- "gitHookGuards"
18
+ "gitHookGuards",
19
+ "defaultTrack"
17
20
  ]);
18
21
  function configFixExample() {
19
22
  return `harnesses:
@@ -23,20 +26,22 @@ function configFixExample() {
23
26
  function configValidationError(configFilePath, reason) {
24
27
  return new Error(`Invalid cclaw config at ${configFilePath}: ${reason}\n` +
25
28
  `Supported harnesses: ${SUPPORTED_HARNESSES_TEXT}\n` +
29
+ `Supported tracks: ${SUPPORTED_TRACKS_TEXT}\n` +
26
30
  `Example config:\n${configFixExample()}\n` +
27
31
  `After fixing, run: cclaw sync`);
28
32
  }
29
33
  export function configPath(projectRoot) {
30
34
  return path.join(projectRoot, CONFIG_PATH);
31
35
  }
32
- export function createDefaultConfig(harnesses = DEFAULT_HARNESSES) {
36
+ export function createDefaultConfig(harnesses = DEFAULT_HARNESSES, defaultTrack = "standard") {
33
37
  return {
34
38
  version: CCLAW_VERSION,
35
39
  flowVersion: FLOW_VERSION,
36
40
  harnesses,
37
41
  autoAdvance: false,
38
42
  promptGuardMode: "advisory",
39
- gitHookGuards: false
43
+ gitHookGuards: false,
44
+ defaultTrack
40
45
  };
41
46
  }
42
47
  export async function readConfig(projectRoot) {
@@ -89,13 +94,22 @@ export async function readConfig(projectRoot) {
89
94
  throw configValidationError(fullPath, `"gitHookGuards" must be a boolean`);
90
95
  }
91
96
  const gitHookGuards = typeof gitHookGuardsRaw === "boolean" ? gitHookGuardsRaw : false;
97
+ const defaultTrackRaw = parsed.defaultTrack;
98
+ if (Object.prototype.hasOwnProperty.call(parsed, "defaultTrack") &&
99
+ (typeof defaultTrackRaw !== "string" || !FLOW_TRACK_SET.has(defaultTrackRaw))) {
100
+ throw configValidationError(fullPath, `"defaultTrack" must be one of: ${SUPPORTED_TRACKS_TEXT}`);
101
+ }
102
+ const defaultTrack = typeof defaultTrackRaw === "string" && FLOW_TRACK_SET.has(defaultTrackRaw)
103
+ ? defaultTrackRaw
104
+ : "standard";
92
105
  return {
93
106
  version: parsed.version ?? CCLAW_VERSION,
94
107
  flowVersion: parsed.flowVersion ?? FLOW_VERSION,
95
108
  harnesses,
96
109
  autoAdvance,
97
110
  promptGuardMode,
98
- gitHookGuards
111
+ gitHookGuards,
112
+ defaultTrack
99
113
  };
100
114
  }
101
115
  export async function writeConfig(projectRoot, config) {
@@ -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"];
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"];
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"];
10
+ export declare const UTILITY_COMMANDS: readonly ["learn", "next", "status"];
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
@@ -20,7 +20,8 @@ export const REQUIRED_DIRS = [
20
20
  `${RUNTIME_ROOT}/rules`,
21
21
  `${RUNTIME_ROOT}/adapters`,
22
22
  `${RUNTIME_ROOT}/agents`,
23
- `${RUNTIME_ROOT}/hooks`
23
+ `${RUNTIME_ROOT}/hooks`,
24
+ `${RUNTIME_ROOT}/custom-skills`
24
25
  ];
25
26
  export const REQUIRED_GITIGNORE_PATTERNS = [
26
27
  "# cclaw generated artifacts",
@@ -49,7 +50,7 @@ export const COMMAND_FILE_ORDER = [
49
50
  "review",
50
51
  "ship"
51
52
  ];
52
- export const UTILITY_COMMANDS = ["learn", "next"];
53
+ export const UTILITY_COMMANDS = ["learn", "next", "status"];
53
54
  export const SUBAGENT_SKILL_FOLDERS = [
54
55
  "subagent-dev",
55
56
  "parallel-dispatch"
@@ -11,6 +11,7 @@ export interface HookRuntimeOptions {
11
11
  export declare const RUNTIME_SHELL_DETECT_ROOT = "HARNESS=\"codex\"\nif [ -n \"${CLAUDE_PROJECT_DIR:-}\" ]; then\n HARNESS=\"claude\"\nelif [ -n \"${CURSOR_PROJECT_DIR:-}\" ] || [ -n \"${CURSOR_PROJECT_ROOT:-}\" ]; then\n HARNESS=\"cursor\"\nelif [ -n \"${OPENCODE_PROJECT_DIR:-}\" ] || [ -n \"${OPENCODE_PROJECT_ROOT:-}\" ]; then\n HARNESS=\"opencode\"\nfi\n\nROOT=\"\"\nfor candidate in \"${CCLAW_PROJECT_ROOT:-}\" \"${CLAUDE_PROJECT_DIR:-}\" \"${CURSOR_PROJECT_DIR:-}\" \"${CURSOR_PROJECT_ROOT:-}\" \"${OPENCODE_PROJECT_DIR:-}\" \"${OPENCODE_PROJECT_ROOT:-}\" \"${PWD:-}\"; do\n if [ -n \"$candidate\" ] && [ -d \"$candidate/.cclaw\" ]; then\n ROOT=\"$candidate\"\n break\n fi\ndone\nif [ -z \"$ROOT\" ]; then\n ROOT=\"${CCLAW_PROJECT_ROOT:-${CLAUDE_PROJECT_DIR:-${CURSOR_PROJECT_DIR:-${CURSOR_PROJECT_ROOT:-${OPENCODE_PROJECT_DIR:-${OPENCODE_PROJECT_ROOT:-${PWD}}}}}}}\"\nfi";
12
12
  export declare function sessionStartScript(_options?: HookRuntimeOptions): string;
13
13
  export declare function stopCheckpointScript(): string;
14
+ export declare function preCompactScript(): string;
14
15
  export { claudeHooksJsonWithObservation as claudeHooksJson } from "./observe.js";
15
16
  export { cursorHooksJsonWithObservation as cursorHooksJson } from "./observe.js";
16
17
  export { codexHooksJsonWithObservation as codexHooksJson } from "./observe.js";
@@ -616,6 +616,151 @@ case "$HARNESS" in
616
616
  esac
617
617
  `;
618
618
  }
619
+ export function preCompactScript() {
620
+ return `#!/usr/bin/env bash
621
+ # cclaw pre-compact hook — generated by cclaw sync
622
+ # Persists a session digest before the harness compacts/clears context, so the
623
+ # next session-start hook can restore the most important state without the agent
624
+ # having to re-derive it from scratch.
625
+ set -uo pipefail
626
+
627
+ ${DETECT_ROOT}
628
+
629
+ INPUT=$(cat 2>/dev/null || echo '{}')
630
+
631
+ STATE_DIR="$ROOT/${RUNTIME_ROOT}/state"
632
+ STATE_FILE="$STATE_DIR/flow-state.json"
633
+ DELEGATION_FILE="$STATE_DIR/delegation-log.json"
634
+ KNOWLEDGE_FILE="$ROOT/${RUNTIME_ROOT}/knowledge.md"
635
+ DIGEST_FILE="$STATE_DIR/session-digest.md"
636
+ DIGEST_TMP="$STATE_DIR/session-digest.md.tmp.$$"
637
+
638
+ mkdir -p "$STATE_DIR" 2>/dev/null || true
639
+
640
+ cleanup_digest_tmp() {
641
+ rm -f "$DIGEST_TMP" 2>/dev/null || true
642
+ }
643
+ trap cleanup_digest_tmp EXIT INT TERM
644
+
645
+ STAGE="none"
646
+ TRACK="standard"
647
+ COMPLETED="0"
648
+ SKIPPED=""
649
+ ACTIVE_RUN="none"
650
+ PASSED_GATES=""
651
+ BLOCKED_GATES=""
652
+
653
+ if [ -f "$STATE_FILE" ]; then
654
+ if command -v jq >/dev/null 2>&1; then
655
+ STAGE=$(jq -r '.currentStage // "none"' "$STATE_FILE" 2>/dev/null || echo "none")
656
+ TRACK=$(jq -r '.track // "standard"' "$STATE_FILE" 2>/dev/null || echo "standard")
657
+ COMPLETED=$(jq -r '(.completedStages // []) | length' "$STATE_FILE" 2>/dev/null || echo "0")
658
+ SKIPPED=$(jq -r '(.skippedStages // []) | join(",")' "$STATE_FILE" 2>/dev/null || echo "")
659
+ ACTIVE_RUN=$(jq -r '.activeRunId // "none"' "$STATE_FILE" 2>/dev/null || echo "none")
660
+ PASSED_GATES=$(jq -r --arg stage "$STAGE" '(.stageGates[$stage].passed // []) | join(",")' "$STATE_FILE" 2>/dev/null || echo "")
661
+ BLOCKED_GATES=$(jq -r --arg stage "$STAGE" '(.stageGates[$stage].blocked // []) | join(",")' "$STATE_FILE" 2>/dev/null || echo "")
662
+ elif command -v python3 >/dev/null 2>&1; then
663
+ OUTPUT=$(python3 - "$STATE_FILE" <<'PY'
664
+ import json, sys
665
+ try:
666
+ with open(sys.argv[1], "r", encoding="utf-8") as fh:
667
+ data = json.load(fh)
668
+ except Exception:
669
+ data = {}
670
+ stage = data.get("currentStage") or "none"
671
+ track = data.get("track") or "standard"
672
+ completed = data.get("completedStages") or []
673
+ skipped = data.get("skippedStages") or []
674
+ run = data.get("activeRunId") or "none"
675
+ gates = (data.get("stageGates") or {}).get(stage) or {}
676
+ passed = gates.get("passed") or []
677
+ blocked = gates.get("blocked") or []
678
+ print(stage)
679
+ print(track)
680
+ print(len(completed) if isinstance(completed, list) else 0)
681
+ print(",".join(skipped) if isinstance(skipped, list) else "")
682
+ print(run)
683
+ print(",".join(passed) if isinstance(passed, list) else "")
684
+ print(",".join(blocked) if isinstance(blocked, list) else "")
685
+ PY
686
+ )
687
+ {
688
+ IFS= read -r STAGE
689
+ IFS= read -r TRACK
690
+ IFS= read -r COMPLETED
691
+ IFS= read -r SKIPPED
692
+ IFS= read -r ACTIVE_RUN
693
+ IFS= read -r PASSED_GATES
694
+ IFS= read -r BLOCKED_GATES
695
+ } <<EOF
696
+ $OUTPUT
697
+ EOF
698
+ fi
699
+ fi
700
+
701
+ DELEGATION_PENDING=""
702
+ if [ -f "$DELEGATION_FILE" ] && command -v jq >/dev/null 2>&1; then
703
+ DELEGATION_PENDING=$(jq -r --arg stage "$STAGE" '
704
+ (.entries // [])
705
+ | map(select((.stage // "") == $stage and (.status // "") != "completed" and (.status // "") != "waived"))
706
+ | map(.agent // "unknown")
707
+ | unique
708
+ | join(",")
709
+ ' "$DELEGATION_FILE" 2>/dev/null || echo "")
710
+ fi
711
+
712
+ KNOWLEDGE_TAIL=""
713
+ if [ -f "$KNOWLEDGE_FILE" ] && [ -s "$KNOWLEDGE_FILE" ]; then
714
+ KNOWLEDGE_TAIL=$(tail -n 12 "$KNOWLEDGE_FILE" 2>/dev/null || echo "")
715
+ fi
716
+
717
+ GIT_HEAD=""
718
+ GIT_BRANCH=""
719
+ GIT_DIRTY="unknown"
720
+ if command -v git >/dev/null 2>&1 && git -C "$ROOT" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
721
+ GIT_HEAD=$(git -C "$ROOT" rev-parse --short HEAD 2>/dev/null || echo "")
722
+ GIT_BRANCH=$(git -C "$ROOT" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
723
+ if [ -n "$(git -C "$ROOT" status --porcelain 2>/dev/null)" ]; then
724
+ GIT_DIRTY="dirty"
725
+ else
726
+ GIT_DIRTY="clean"
727
+ fi
728
+ fi
729
+
730
+ TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ" 2>/dev/null || echo "")
731
+
732
+ {
733
+ printf '# Session Digest\n'
734
+ printf '_Generated by pre-compact hook at %s_\n\n' "$TS"
735
+ printf '## Flow snapshot\n'
736
+ printf '- track: %s\n' "$TRACK"
737
+ printf '- current stage: %s\n' "$STAGE"
738
+ printf '- completed: %s stages\n' "$COMPLETED"
739
+ printf '- skipped: %s\n' "\${SKIPPED:-(none)}"
740
+ printf '- run: %s\n\n' "$ACTIVE_RUN"
741
+ printf '## Gates (current stage)\n'
742
+ printf '- passed: %s\n' "\${PASSED_GATES:-(none)}"
743
+ printf '- blocked: %s\n\n' "\${BLOCKED_GATES:-(none)}"
744
+ printf '## Outstanding delegations\n'
745
+ printf '- pending: %s\n\n' "\${DELEGATION_PENDING:-(none)}"
746
+ printf '## Git\n'
747
+ printf '- branch: %s\n' "\${GIT_BRANCH:-(unknown)}"
748
+ printf '- head: %s\n' "\${GIT_HEAD:-(unknown)}"
749
+ printf '- worktree: %s\n\n' "$GIT_DIRTY"
750
+ if [ -n "$KNOWLEDGE_TAIL" ]; then
751
+ printf '## Knowledge tail\n'
752
+ printf '%s\n' "$KNOWLEDGE_TAIL"
753
+ fi
754
+ } > "$DIGEST_TMP" 2>/dev/null || true
755
+
756
+ if [ -s "$DIGEST_TMP" ]; then
757
+ mv "$DIGEST_TMP" "$DIGEST_FILE" 2>/dev/null || rm -f "$DIGEST_TMP" 2>/dev/null || true
758
+ fi
759
+
760
+ trap - EXIT INT TERM
761
+ exit 0
762
+ `;
763
+ }
619
764
  // ---------------------------------------------------------------------------
620
765
  // hooks.json generators are defined in observe.ts (shared across harnesses).
621
766
  // ---------------------------------------------------------------------------
@@ -20,6 +20,7 @@ Use it to keep durable knowledge that should survive sessions:
20
20
  - **rule**: hard constraint to follow every time
21
21
  - **pattern**: repeatable way that works well in this project
22
22
  - **lesson**: non-obvious outcome from a failure or trade-off
23
+ - **compound**: post-ship insight about how to make the *next* feature faster (process accelerator, not domain rule)
23
24
 
24
25
  ## HARD-GATE
25
26
 
@@ -33,12 +34,25 @@ Under \`/cc-learn\`, only modify the knowledge store (\`${KNOWLEDGE_PATH}\`) or
33
34
  - Context: one short line
34
35
  - Insight: one short line
35
36
  - Reuse: one short line
37
+ - Confidence: high | medium | low (optional)
38
+ - Domain: api | infra | ui | testing | … (optional)
39
+ - Project: <repo or scope name> (optional)
36
40
  \`\`\`
37
41
 
38
42
  Rules:
39
- - Type must be exactly one of \`rule\`, \`pattern\`, \`lesson\` (lowercase).
40
- - Never rewrite history silently; append a newer correction entry instead.
43
+ - Type must be exactly one of \`rule\`, \`pattern\`, \`lesson\`, \`compound\` (lowercase).
44
+ - Never rewrite history silently; append a newer correction entry instead. To replace, prefix the new entry with \`Supersedes: <old-title>\`.
41
45
  - Keep entries concise and actionable.
46
+ - Optional fields (\`Confidence\`, \`Domain\`, \`Project\`) are forward-compatible and used by the **knowledge-curation** skill — fill them when known.
47
+
48
+ ## Curation policy (target: ≤ 50 active entries)
49
+
50
+ The knowledge file is append-only, but entries can be **superseded** rather than deleted:
51
+
52
+ - When you discover a more correct rule, append a new entry with \`Supersedes: <old-title>\`.
53
+ - During \`/cc-learn curate\`, the assistant surfaces candidates for soft-archive (move to \`.cclaw/knowledge.archive.md\`) when the active file exceeds 50 entries or contains stale/duplicate entries.
54
+
55
+ See the **knowledge-curation** utility skill for the full curation protocol.
42
56
 
43
57
  ## Subcommands
44
58
 
@@ -52,8 +66,13 @@ Rules:
52
66
 
53
67
  ### \`/cc-learn add\`
54
68
  - Ask for: \`type\`, \`short title\`, \`context\`, \`insight\`, \`reuse\`.
69
+ - Optionally ask for: \`confidence\`, \`domain\`, \`project\`.
55
70
  - Append one entry using current UTC timestamp.
56
71
  - Re-read the file tail and confirm the entry was written.
72
+
73
+ ### \`/cc-learn curate\`
74
+ - Hand off to the **knowledge-curation** skill (read-only audit + soft-archive plan).
75
+ - Never deletes from \`${KNOWLEDGE_PATH}\` without an explicit user-approved archive plan.
57
76
  `;
58
77
  }
59
78
  export function learnCommandContract() {
@@ -73,7 +92,8 @@ Do not edit source code from this command. Only operate on \`${KNOWLEDGE_PATH}\`
73
92
  |---|---|---|
74
93
  | (default) | — | Show recent knowledge entries (tail view). |
75
94
  | \`search\` | \`<query>\` | Search knowledge text for relevant prior rules/patterns/lessons. |
76
- | \`add\` | — | Append a new entry with type \`rule\` / \`pattern\` / \`lesson\`. |
95
+ | \`add\` | — | Append a new entry with type \`rule\` / \`pattern\` / \`lesson\` / \`compound\`. |
96
+ | \`curate\` | — | Hand off to the **knowledge-curation** skill: read-only audit + soft-archive plan when the active file exceeds the curation threshold. |
77
97
  `;
78
98
  }
79
99
  export function selfImprovementBlock(stageName) {
@@ -96,7 +116,7 @@ cat >> ${KNOWLEDGE_PATH} <<EOF
96
116
  EOF
97
117
  \`\`\`
98
118
 
99
- Type must be exactly one of: \`rule\`, \`pattern\`, \`lesson\`.
119
+ Type must be exactly one of: \`rule\`, \`pattern\`, \`lesson\`, \`compound\`.
100
120
  `;
101
121
  }
102
122
  export function learningsSearchPreamble(stage) {
@@ -110,7 +130,7 @@ If the file is empty, continue normally.
110
130
  export function learningsAgentsMdBlock() {
111
131
  return `### Knowledge Store
112
132
 
113
- \`${KNOWLEDGE_PATH}\` — append-only markdown memory with entry types \`rule\`, \`pattern\`, \`lesson\`.
133
+ \`${KNOWLEDGE_PATH}\` — append-only markdown memory with entry types \`rule\`, \`pattern\`, \`lesson\`, \`compound\`.
114
134
  At session start and stage transitions, load recent entries and apply relevant ones.
115
135
  If a non-obvious reusable rule/pattern/lesson is discovered, append a new entry.
116
136
  `;
@@ -89,6 +89,18 @@ These skills live in \`.cclaw/skills/\` but have no slash commands. They activat
89
89
 
90
90
  **Activation rule:** When a contextual skill applies, read its SKILL.md and follow it as a supplementary lens alongside the current stage. Do not skip the stage workflow — the contextual skill adds depth, not a detour.
91
91
 
92
+ ## Custom Skills (project-owned, sync-safe)
93
+
94
+ \`.cclaw/custom-skills/\` is a sync-safe directory. \`cclaw sync\` and \`cclaw upgrade\` **never overwrite** files there.
95
+
96
+ Use it to add **project-specific** skills that complement the managed library:
97
+
98
+ - Each skill: \`.cclaw/custom-skills/<folder>/SKILL.md\` with the same frontmatter format as managed skills (\`name\` + \`description\` triggering routing).
99
+ - Activate by mentioning the skill name explicitly, or rely on semantic routing from the description.
100
+ - See \`.cclaw/custom-skills/README.md\` for the full convention and a starter template under \`.cclaw/custom-skills/example/\`.
101
+
102
+ If a custom skill turns out to generalize (e.g. another project would want the same lens), promote it to a managed skill via a contribution to the cclaw repo — managed skills get versioning and maintenance.
103
+
92
104
  ## Progressive Disclosure (Depth / See Also)
93
105
 
94
106
  Use this loading order to keep context lean while preserving depth:
@@ -56,6 +56,14 @@ This is the only progression command the user needs to drive the entire flow. St
56
56
  → If current stage's \`next\` is **\`done\`**: report **"Flow complete. All stages finished."** and stop.
57
57
  → Otherwise: load **\`${RUNTIME_ROOT}/skills/<skillFolder>/SKILL.md\`** and **\`${RUNTIME_ROOT}/commands/<nextStage>.md\`** for the successor stage. Execute that stage's protocol.
58
58
 
59
+ ### Track-aware successor resolution
60
+
61
+ \`flow-state.json\` carries a \`track\` field (\`"quick"\` or \`"standard"\`) and a \`skippedStages\` array.
62
+
63
+ - If \`track === "quick"\`, the critical path is **spec → tdd → review → ship**. When advancing, skip any stage listed in \`skippedStages\` — i.e. after the current stage completes, pick the next stage that is NOT in \`skippedStages\`.
64
+ - If \`track === "standard"\`, advance through all 8 stages in their natural order.
65
+ - Never reintroduce a skipped stage mid-run. If the user wants upstream scoping work, they must archive the run and start a new one with \`track: "standard"\`.
66
+
59
67
  ## Resume Semantics
60
68
 
61
69
  \`/cc-next\` in a **new session** = resume from where you left off:
@@ -1613,6 +1613,14 @@ export function claudeHooksJsonWithObservation() {
1613
1613
  command: `bash ${RUNTIME_ROOT}/hooks/stop-checkpoint.sh`,
1614
1614
  timeout: 10
1615
1615
  }]
1616
+ }],
1617
+ PreCompact: [{
1618
+ matcher: "manual|auto",
1619
+ hooks: [{
1620
+ type: "command",
1621
+ command: `bash ${RUNTIME_ROOT}/hooks/pre-compact.sh`,
1622
+ timeout: 10
1623
+ }]
1616
1624
  }]
1617
1625
  }
1618
1626
  }, null, 2);
@@ -1632,6 +1640,8 @@ export function cursorHooksJsonWithObservation() {
1632
1640
  command: `bash ${RUNTIME_ROOT}/hooks/session-start.sh`
1633
1641
  }],
1634
1642
  sessionCompact: [{
1643
+ command: `bash ${RUNTIME_ROOT}/hooks/pre-compact.sh`
1644
+ }, {
1635
1645
  command: `bash ${RUNTIME_ROOT}/hooks/session-start.sh`
1636
1646
  }],
1637
1647
  preToolUse: [{
@@ -1684,6 +1694,14 @@ export function codexHooksJsonWithObservation() {
1684
1694
  command: `bash ${RUNTIME_ROOT}/hooks/stop-checkpoint.sh`,
1685
1695
  timeout: 10
1686
1696
  }]
1697
+ }],
1698
+ PreCompact: [{
1699
+ matcher: "manual|auto",
1700
+ hooks: [{
1701
+ type: "command",
1702
+ command: `bash ${RUNTIME_ROOT}/hooks/pre-compact.sh`,
1703
+ timeout: 10
1704
+ }]
1687
1705
  }]
1688
1706
  }
1689
1707
  }, null, 2);
@@ -34,7 +34,7 @@ When a new session begins in any harness:
34
34
  ### What to show the user at session start
35
35
 
36
36
  \`\`\`
37
- Cclaw flow state: [current stage] ([N] of 9 stages completed)
37
+ Cclaw flow state: [current stage] ([N] of 8 stages completed)
38
38
  Knowledge highlights: [rule/pattern 1], [rule/pattern 2], [rule/pattern 3]
39
39
  Next action: /cc-[stage] to continue, or describe what you'd like to do.
40
40
  \`\`\`
@@ -1147,7 +1147,8 @@ const REVIEW = {
1147
1147
  { id: "review_layer2_architecture", description: "Architecture fit review completed." },
1148
1148
  { id: "review_severity_classified", description: "All findings are severity-tagged." },
1149
1149
  { id: "review_criticals_resolved", description: "No unresolved critical blockers remain." },
1150
- { id: "review_army_json_valid", description: "07-review-army.json passes schema validation (validateReviewArmy)." }
1150
+ { id: "review_army_json_valid", description: "07-review-army.json passes schema validation (validateReviewArmy)." },
1151
+ { id: "review_completeness_scored", description: "Completeness score is computed and recorded (AC coverage, task coverage, slice coverage, adversarial pass)." }
1151
1152
  ],
1152
1153
  requiredEvidence: [
1153
1154
  "Artifact written to `.cclaw/artifacts/07-review.md`.",
@@ -1289,6 +1290,7 @@ const REVIEW = {
1289
1290
  { section: "Layer 2 Findings", required: true, validationRule: "Each finding has severity, description, and resolution status." },
1290
1291
  { section: "Review Army Contract", required: true, validationRule: "Structured findings include id/severity/confidence/fingerprint/reportedBy/status with dedup reconciliation summary." },
1291
1292
  { section: "Review Readiness Dashboard", required: true, validationRule: "At least 4 readiness checklist lines including blocker and recommendation status." },
1293
+ { section: "Completeness Score", required: true, validationRule: "Records AC coverage, task coverage, test-slice coverage, and adversarial-review pass status as numeric or boolean values. At minimum, a line like 'AC coverage: N/M' or 'AC coverage: 100%'." },
1292
1294
  { section: "Severity Summary", required: true, validationRule: "Per-severity count lines for critical, important, and suggestion buckets." },
1293
1295
  { section: "Final Verdict", required: true, validationRule: "Exactly one of: APPROVED, APPROVED_WITH_CONCERNS, BLOCKED." }
1294
1296
  ],
@@ -1450,7 +1452,8 @@ const SHIP = {
1450
1452
  { section: "Rollback Plan", required: true, validationRule: "Trigger conditions, rollback steps (exact commands), verification steps." },
1451
1453
  { section: "Monitoring", required: false, validationRule: "If applicable: what metrics/logs to watch post-deploy. Risk note if no monitoring." },
1452
1454
  { section: "Finalization", required: true, validationRule: "Exactly one finalization enum token selected. Execution result documented. Worktree cleaned if applicable." },
1453
- { section: "Completion Status", required: false, validationRule: "If present: exactly one of SHIPPED, SHIPPED_WITH_EXCEPTIONS, BLOCKED. Exceptions documented when applicable." }
1455
+ { section: "Completion Status", required: false, validationRule: "If present: exactly one of SHIPPED, SHIPPED_WITH_EXCEPTIONS, BLOCKED. Exceptions documented when applicable." },
1456
+ { section: "Compound Step", required: false, validationRule: "Optional retrospective: at least one bullet of the form 'Insight: ... | Action: append [compound] entry to .cclaw/knowledge.md', or an explicit 'No compound insight this run.' line." }
1454
1457
  ],
1455
1458
  namedAntiPattern: {
1456
1459
  title: "Green CI Means Safe to Merge",
@@ -1512,6 +1515,13 @@ const STAGE_AUTO_SUBAGENT_DISPATCH = {
1512
1515
  when: "When acceptance criteria are unclear or constraints conflict.",
1513
1516
  purpose: "Normalize measurable criteria and testability mapping.",
1514
1517
  requiresUserGate: false
1518
+ },
1519
+ {
1520
+ agent: "spec-reviewer",
1521
+ mode: "proactive",
1522
+ when: "When acceptance criteria and edge cases are drafted and need independent validation before plan stage.",
1523
+ purpose: "Independent review of spec against measurability, testability, and completeness before locking the contract for plan.",
1524
+ requiresUserGate: false
1515
1525
  }
1516
1526
  ],
1517
1527
  plan: [
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Command contract for /cc-status — a read-only snapshot command.
3
+ * Does not mutate state. Always safe to run.
4
+ */
5
+ export declare function statusCommandContract(): string;
6
+ /**
7
+ * Skill body for /cc-status — read-only status snapshot.
8
+ */
9
+ export declare function statusCommandSkillMarkdown(): string;