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 +4 -2
- package/dist/cli.js +18 -4
- package/dist/config.d.ts +2 -2
- package/dist/config.js +19 -5
- package/dist/constants.d.ts +2 -2
- package/dist/constants.js +3 -2
- package/dist/content/hooks.d.ts +1 -0
- package/dist/content/hooks.js +145 -0
- package/dist/content/learnings.js +25 -5
- package/dist/content/meta-skill.js +12 -0
- package/dist/content/next-command.js +8 -0
- package/dist/content/observe.js +18 -0
- package/dist/content/session-hooks.js +1 -1
- package/dist/content/stage-schema.js +12 -2
- package/dist/content/status-command.d.ts +9 -0
- package/dist/content/status-command.js +132 -0
- package/dist/content/templates.js +14 -0
- package/dist/content/utility-skills.d.ts +6 -2
- package/dist/content/utility-skills.js +431 -3
- package/dist/flow-state.d.ts +16 -4
- package/dist/flow-state.js +50 -11
- package/dist/harness-adapters.js +1 -0
- package/dist/install.d.ts +2 -1
- package/dist/install.js +107 -6
- package/dist/runs.js +24 -3
- package/dist/types.d.ts +13 -0
- package/dist/types.js +13 -0
- package/package.json +1 -1
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
|
-
|
|
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) {
|
package/dist/constants.d.ts
CHANGED
|
@@ -4,9 +4,9 @@ export declare const RUNTIME_ROOT = ".cclaw";
|
|
|
4
4
|
export declare const CCLAW_VERSION = "0.1.1";
|
|
5
5
|
export declare const FLOW_VERSION = "1.0.0";
|
|
6
6
|
export declare const DEFAULT_HARNESSES: HarnessId[];
|
|
7
|
-
export declare const REQUIRED_DIRS: readonly [".cclaw", ".cclaw/commands", ".cclaw/skills", ".cclaw/contexts", ".cclaw/templates", ".cclaw/artifacts", ".cclaw/state", ".cclaw/runs", ".cclaw/rules", ".cclaw/adapters", ".cclaw/agents", ".cclaw/hooks"];
|
|
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"
|
package/dist/content/hooks.d.ts
CHANGED
|
@@ -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";
|
package/dist/content/hooks.js
CHANGED
|
@@ -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:
|
package/dist/content/observe.js
CHANGED
|
@@ -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
|
|
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;
|