cclaw-cli 7.1.1 → 7.3.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 +1 -0
- package/dist/cli.js +12 -0
- package/dist/config.d.ts +6 -1
- package/dist/config.js +46 -4
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -0
- package/dist/content/core-agents.js +2 -1
- package/dist/content/hooks.js +239 -40
- package/dist/content/stage-schema.js +7 -32
- package/dist/delegation.d.ts +5 -0
- package/dist/delegation.js +1 -0
- package/dist/flow-state.d.ts +1 -1
- package/dist/install.d.ts +1 -0
- package/dist/install.js +51 -1
- package/dist/internal/advance-stage/start-flow.js +6 -18
- package/dist/internal/advance-stage/verify.js +6 -18
- package/dist/internal/slice-commit.js +179 -10
- package/dist/runtime/run-hook.mjs +1 -0
- package/dist/stack-detection.d.ts +22 -0
- package/dist/stack-detection.js +58 -0
- package/dist/types.d.ts +12 -0
- package/dist/worktree-manager.d.ts +20 -0
- package/dist/worktree-manager.js +108 -0
- package/package.json +1 -1
package/dist/cli.d.ts
CHANGED
package/dist/cli.js
CHANGED
|
@@ -40,6 +40,7 @@ Commands:
|
|
|
40
40
|
sync Reconcile generated runtime files with the current config.
|
|
41
41
|
Flags: --harnesses=<list> Update configured harnesses before syncing.
|
|
42
42
|
--interactive Pick harnesses from a numbered TTY menu.
|
|
43
|
+
--check Verify managed hook files are byte-identical to canonical generators.
|
|
43
44
|
upgrade Refresh generated files in .cclaw. Preserves your config.yaml.
|
|
44
45
|
archive Archive the active run and reset flow state for the next run.
|
|
45
46
|
Flags: --name=<slug> Override archive folder suffix.
|
|
@@ -57,6 +58,7 @@ Examples:
|
|
|
57
58
|
npx cclaw-cli
|
|
58
59
|
npx cclaw-cli init --harnesses=claude,cursor --no-interactive
|
|
59
60
|
npx cclaw-cli sync --interactive
|
|
61
|
+
npx cclaw-cli sync --check
|
|
60
62
|
npx cclaw-cli archive --name=my-run
|
|
61
63
|
npx cclaw-cli archive --disposition=cancelled --reason="deprioritized"
|
|
62
64
|
npx cclaw-cli upgrade
|
|
@@ -334,6 +336,7 @@ function parseArgs(argv) {
|
|
|
334
336
|
return flag.startsWith("--harnesses=") ||
|
|
335
337
|
(parsed.command === "init" && flag.startsWith("--track=")) ||
|
|
336
338
|
(parsed.command === "init" && flag.startsWith("--profile=")) ||
|
|
339
|
+
(parsed.command === "sync" && flag === "--check") ||
|
|
337
340
|
flag === "--interactive" ||
|
|
338
341
|
flag === "--no-interactive" ||
|
|
339
342
|
(parsed.command === "init" && flag === "--dry-run");
|
|
@@ -374,6 +377,10 @@ function parseArgs(argv) {
|
|
|
374
377
|
parsed.dryRun = true;
|
|
375
378
|
continue;
|
|
376
379
|
}
|
|
380
|
+
if (flag === "--check") {
|
|
381
|
+
parsed.syncCheck = true;
|
|
382
|
+
continue;
|
|
383
|
+
}
|
|
377
384
|
if (flag.startsWith("--name=")) {
|
|
378
385
|
parsed.archiveName = flag.replace("--name=", "").trim();
|
|
379
386
|
continue;
|
|
@@ -446,6 +453,11 @@ async function runCommand(parsed, ctx) {
|
|
|
446
453
|
return 0;
|
|
447
454
|
}
|
|
448
455
|
if (command === "sync") {
|
|
456
|
+
if (parsed.syncCheck === true) {
|
|
457
|
+
await syncCclaw(ctx.cwd, { check: true });
|
|
458
|
+
info(ctx, "Managed hook drift check passed (no sync required).");
|
|
459
|
+
return 0;
|
|
460
|
+
}
|
|
449
461
|
const resolved = await resolveSyncInputs(parsed, ctx);
|
|
450
462
|
await syncCclaw(ctx.cwd, { harnesses: resolved.harnesses });
|
|
451
463
|
const harnessNote = resolved.harnesses ? ` (${resolved.harnesses.join(", ")})` : "";
|
package/dist/config.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
|
-
import type { CclawConfig, FlowTrack, HarnessId, LanguageRulePack, TddCommitMode } from "./types.js";
|
|
1
|
+
import type { CclawConfig, FlowTrack, HarnessId, LanguageRulePack, TddCommitMode, TddIsolationMode } from "./types.js";
|
|
2
2
|
export declare const TDD_COMMIT_MODES: readonly ["managed-per-slice", "agent-required", "checkpoint-only", "off"];
|
|
3
3
|
export declare const DEFAULT_TDD_COMMIT_MODE: TddCommitMode;
|
|
4
|
+
export declare const TDD_ISOLATION_MODES: readonly ["worktree", "in-place", "auto"];
|
|
5
|
+
export declare const DEFAULT_TDD_ISOLATION_MODE: TddIsolationMode;
|
|
6
|
+
export declare const DEFAULT_TDD_WORKTREE_ROOT = ".cclaw/worktrees";
|
|
4
7
|
export declare const DEFAULT_TDD_TEST_PATH_PATTERNS: readonly string[];
|
|
5
8
|
export declare const DEFAULT_TDD_TEST_GLOBS: readonly string[];
|
|
6
9
|
export declare const DEFAULT_TDD_PRODUCTION_PATH_PATTERNS: readonly string[];
|
|
@@ -19,6 +22,8 @@ export declare class InvalidConfigError extends Error {
|
|
|
19
22
|
export declare function configPath(projectRoot: string): string;
|
|
20
23
|
export declare function createDefaultConfig(harnesses?: HarnessId[], _defaultTrack?: FlowTrack): CclawConfig;
|
|
21
24
|
export declare function resolveTddCommitMode(config: Pick<CclawConfig, "tdd"> | null | undefined): TddCommitMode;
|
|
25
|
+
export declare function resolveTddIsolationMode(config: Pick<CclawConfig, "tdd"> | null | undefined): TddIsolationMode;
|
|
26
|
+
export declare function resolveTddWorktreeRoot(config: Pick<CclawConfig, "tdd"> | null | undefined): string;
|
|
22
27
|
export declare function detectLanguageRulePacks(_projectRoot: string): Promise<LanguageRulePack[]>;
|
|
23
28
|
export declare function readConfig(projectRoot: string, _options?: ReadConfigOptions): Promise<CclawConfig>;
|
|
24
29
|
export interface WriteConfigOptions {
|
package/dist/config.js
CHANGED
|
@@ -16,6 +16,10 @@ export const TDD_COMMIT_MODES = [
|
|
|
16
16
|
];
|
|
17
17
|
const TDD_COMMIT_MODE_SET = new Set(TDD_COMMIT_MODES);
|
|
18
18
|
export const DEFAULT_TDD_COMMIT_MODE = "managed-per-slice";
|
|
19
|
+
export const TDD_ISOLATION_MODES = ["worktree", "in-place", "auto"];
|
|
20
|
+
const TDD_ISOLATION_MODE_SET = new Set(TDD_ISOLATION_MODES);
|
|
21
|
+
export const DEFAULT_TDD_ISOLATION_MODE = "worktree";
|
|
22
|
+
export const DEFAULT_TDD_WORKTREE_ROOT = `${RUNTIME_ROOT}/worktrees`;
|
|
19
23
|
// Kept for runtime modules that use these defaults directly.
|
|
20
24
|
export const DEFAULT_TDD_TEST_PATH_PATTERNS = [
|
|
21
25
|
"**/*.test.*",
|
|
@@ -40,7 +44,9 @@ function configFixExample() {
|
|
|
40
44
|
- claude
|
|
41
45
|
- cursor
|
|
42
46
|
tdd:
|
|
43
|
-
commitMode: managed-per-slice
|
|
47
|
+
commitMode: managed-per-slice
|
|
48
|
+
isolationMode: worktree
|
|
49
|
+
worktreeRoot: .cclaw/worktrees`;
|
|
44
50
|
}
|
|
45
51
|
function configValidationError(configFilePath, reason) {
|
|
46
52
|
return new InvalidConfigError(`Invalid cclaw config at ${configFilePath}: ${reason}\n` +
|
|
@@ -60,7 +66,9 @@ export function createDefaultConfig(harnesses = DEFAULT_HARNESSES, _defaultTrack
|
|
|
60
66
|
flowVersion: FLOW_VERSION,
|
|
61
67
|
harnesses: [...new Set(harnesses)],
|
|
62
68
|
tdd: {
|
|
63
|
-
commitMode: DEFAULT_TDD_COMMIT_MODE
|
|
69
|
+
commitMode: DEFAULT_TDD_COMMIT_MODE,
|
|
70
|
+
isolationMode: DEFAULT_TDD_ISOLATION_MODE,
|
|
71
|
+
worktreeRoot: DEFAULT_TDD_WORKTREE_ROOT
|
|
64
72
|
}
|
|
65
73
|
};
|
|
66
74
|
}
|
|
@@ -71,6 +79,20 @@ export function resolveTddCommitMode(config) {
|
|
|
71
79
|
}
|
|
72
80
|
return DEFAULT_TDD_COMMIT_MODE;
|
|
73
81
|
}
|
|
82
|
+
export function resolveTddIsolationMode(config) {
|
|
83
|
+
const raw = config?.tdd?.isolationMode;
|
|
84
|
+
if (typeof raw === "string" && TDD_ISOLATION_MODE_SET.has(raw)) {
|
|
85
|
+
return raw;
|
|
86
|
+
}
|
|
87
|
+
return DEFAULT_TDD_ISOLATION_MODE;
|
|
88
|
+
}
|
|
89
|
+
export function resolveTddWorktreeRoot(config) {
|
|
90
|
+
const raw = config?.tdd?.worktreeRoot;
|
|
91
|
+
if (typeof raw === "string" && raw.trim().length > 0) {
|
|
92
|
+
return raw.trim();
|
|
93
|
+
}
|
|
94
|
+
return DEFAULT_TDD_WORKTREE_ROOT;
|
|
95
|
+
}
|
|
74
96
|
function assertOnlySupportedKeys(parsed, fullPath) {
|
|
75
97
|
const unknownKeys = Object.keys(parsed).filter((key) => !ALLOWED_CONFIG_KEYS.has(key));
|
|
76
98
|
if (unknownKeys.length === 0)
|
|
@@ -129,19 +151,37 @@ export async function readConfig(projectRoot, _options = {}) {
|
|
|
129
151
|
: FLOW_VERSION;
|
|
130
152
|
const parsedTdd = isRecord(parsed.tdd) ? parsed.tdd : {};
|
|
131
153
|
const rawCommitMode = parsedTdd.commitMode;
|
|
154
|
+
const rawIsolationMode = parsedTdd.isolationMode;
|
|
155
|
+
const rawWorktreeRoot = parsedTdd.worktreeRoot;
|
|
132
156
|
if (rawCommitMode !== undefined &&
|
|
133
157
|
(typeof rawCommitMode !== "string" || !TDD_COMMIT_MODE_SET.has(rawCommitMode))) {
|
|
134
158
|
throw configValidationError(fullPath, `"tdd.commitMode" must be one of: ${TDD_COMMIT_MODES.join(", ")}`);
|
|
135
159
|
}
|
|
160
|
+
if (rawIsolationMode !== undefined &&
|
|
161
|
+
(typeof rawIsolationMode !== "string" || !TDD_ISOLATION_MODE_SET.has(rawIsolationMode))) {
|
|
162
|
+
throw configValidationError(fullPath, `"tdd.isolationMode" must be one of: ${TDD_ISOLATION_MODES.join(", ")}`);
|
|
163
|
+
}
|
|
164
|
+
if (rawWorktreeRoot !== undefined &&
|
|
165
|
+
(typeof rawWorktreeRoot !== "string" || rawWorktreeRoot.trim().length === 0)) {
|
|
166
|
+
throw configValidationError(fullPath, `"tdd.worktreeRoot" must be a non-empty string when provided`);
|
|
167
|
+
}
|
|
136
168
|
const commitMode = typeof rawCommitMode === "string"
|
|
137
169
|
? rawCommitMode
|
|
138
170
|
: DEFAULT_TDD_COMMIT_MODE;
|
|
171
|
+
const isolationMode = typeof rawIsolationMode === "string"
|
|
172
|
+
? rawIsolationMode
|
|
173
|
+
: DEFAULT_TDD_ISOLATION_MODE;
|
|
174
|
+
const worktreeRoot = typeof rawWorktreeRoot === "string" && rawWorktreeRoot.trim().length > 0
|
|
175
|
+
? rawWorktreeRoot.trim()
|
|
176
|
+
: DEFAULT_TDD_WORKTREE_ROOT;
|
|
139
177
|
return {
|
|
140
178
|
version,
|
|
141
179
|
flowVersion,
|
|
142
180
|
harnesses: normalizedHarnesses,
|
|
143
181
|
tdd: {
|
|
144
|
-
commitMode
|
|
182
|
+
commitMode,
|
|
183
|
+
isolationMode,
|
|
184
|
+
worktreeRoot
|
|
145
185
|
}
|
|
146
186
|
};
|
|
147
187
|
}
|
|
@@ -151,7 +191,9 @@ export async function writeConfig(projectRoot, config, _options = {}) {
|
|
|
151
191
|
flowVersion: config.flowVersion,
|
|
152
192
|
harnesses: config.harnesses,
|
|
153
193
|
tdd: {
|
|
154
|
-
commitMode: resolveTddCommitMode(config)
|
|
194
|
+
commitMode: resolveTddCommitMode(config),
|
|
195
|
+
isolationMode: resolveTddIsolationMode(config),
|
|
196
|
+
worktreeRoot: resolveTddWorktreeRoot(config)
|
|
155
197
|
}
|
|
156
198
|
};
|
|
157
199
|
await writeFileSafe(configPath(projectRoot), stringify(serialisable));
|
package/dist/constants.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export declare const FLOW_VERSION = "1.0.0";
|
|
|
10
10
|
export declare const SHIP_FINALIZATION_MODES: readonly ["FINALIZE_MERGE_LOCAL", "FINALIZE_OPEN_PR", "FINALIZE_KEEP_BRANCH", "FINALIZE_DISCARD_BRANCH", "FINALIZE_NO_VCS"];
|
|
11
11
|
export type ShipFinalizationMode = (typeof SHIP_FINALIZATION_MODES)[number];
|
|
12
12
|
export declare const DEFAULT_HARNESSES: HarnessId[];
|
|
13
|
-
export declare const REQUIRED_DIRS: readonly [".cclaw", ".cclaw/commands", ".cclaw/skills", ".cclaw/templates", ".cclaw/templates/state-contracts", ".cclaw/artifacts", ".cclaw/wave-plans", ".cclaw/archive", ".cclaw/state", ".cclaw/rules", ".cclaw/agents", ".cclaw/hooks", ".cclaw/skills/review-prompts"];
|
|
13
|
+
export declare const REQUIRED_DIRS: readonly [".cclaw", ".cclaw/commands", ".cclaw/skills", ".cclaw/templates", ".cclaw/templates/state-contracts", ".cclaw/artifacts", ".cclaw/wave-plans", ".cclaw/archive", ".cclaw/worktrees", ".cclaw/state", ".cclaw/rules", ".cclaw/agents", ".cclaw/hooks", ".cclaw/skills/review-prompts"];
|
|
14
14
|
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", ".agents/skills/cc/SKILL.md", ".agents/skills/cc-*/SKILL.md", ".claude/hooks/hooks.json", ".cursor/hooks.json", ".codex/hooks.json", ".opencode/plugins/cclaw-plugin.mjs", ".cursor/rules/cclaw-workflow.mdc"];
|
|
15
15
|
/**
|
|
16
16
|
* Canonical stage -> skill folder mapping.
|
package/dist/constants.js
CHANGED
|
@@ -97,7 +97,7 @@ node .cclaw/hooks/delegation-record.mjs \\
|
|
|
97
97
|
--json
|
|
98
98
|
\`\`\`
|
|
99
99
|
|
|
100
|
-
Reuse the same \`<spanId>\` and \`<dispatchId>\` across both rows. **GREEN evidence freshness** (slice-builder): the FIRST \`--evidence-ref\` MUST (1) reference the same test the matching \`phase=red\` row cited (basename/stem substring; reject \`green_evidence_red_test_mismatch\`), (2) include a recognized passing-runner line such as \`=> N passed; 0 failed\`, \`N passed in 0.42s\`, or \`ok pkg 0.12s\` (reject \`green_evidence_passing_assertion_missing\`), AND (3) be captured AFTER \`ackTs\` of this span — \`completedTs - ackTs\` must be ≥ \`flow-state.json::tddGreenMinElapsedMs\` (default 4000ms; reject \`green_evidence_too_fresh\`). Escape clause for legitimate observational GREEN: pass
|
|
100
|
+
Reuse the same \`<spanId>\` and \`<dispatchId>\` across both rows. **GREEN evidence freshness** (slice-builder): the FIRST \`--evidence-ref\` MUST (1) reference the same test the matching \`phase=red\` row cited (basename/stem substring; reject \`green_evidence_red_test_mismatch\`), (2) include a recognized passing-runner line such as \`=> N passed; 0 failed\`, \`N passed in 0.42s\`, or \`ok pkg 0.12s\` (reject \`green_evidence_passing_assertion_missing\`), AND (3) be captured AFTER \`ackTs\` of this span — \`completedTs - ackTs\` must be ≥ \`flow-state.json::tddGreenMinElapsedMs\` (default 4000ms; reject \`green_evidence_too_fresh\`). Escape clause for legitimate observational GREEN: pass \`--green-mode=observational\`. \`--ack-ts\` and \`--completed-ts\` must be monotonic on the span (\`startTs ≤ launchedTs ≤ ackTs ≤ completedTs\`); the helper rejects out-of-order writes with \`delegation_timestamp_non_monotonic\`. If the helper rejects with \`dispatch_active_span_collision\` against a stale span, surface the conflicting \`spanId\` to the parent — do NOT silently retry with \`--allow-parallel\`.`;
|
|
101
101
|
}
|
|
102
102
|
function formatReturnSchema(schema) {
|
|
103
103
|
const lines = [
|
|
@@ -156,6 +156,7 @@ export function sliceBuilderProtocol() {
|
|
|
156
156
|
"### Invariants",
|
|
157
157
|
"- Produce failing RED evidence (or cite the delegated RED artifact) **before** production edits.",
|
|
158
158
|
"- Stay inside the slice contract: `claimedPaths`, acceptance mapping, and forbidden-change lists from the parent.",
|
|
159
|
+
"- When `tdd.isolationMode=worktree|auto`, run the slice inside the assigned worktree path (never in the shared repo root) so filesystem isolation enforces the claimed-path fence.",
|
|
159
160
|
"- When `tdd.commitMode=managed-per-slice`, do **not** hand-edit git state for slice files (no manual `git add/commit` on claimed paths). Let `.cclaw/hooks/slice-commit.mjs` own per-slice commits.",
|
|
160
161
|
"- After GREEN, refactor inline **or** record deferred refactor via the same `--refactor-outcome` mechanics the controller specifies.",
|
|
161
162
|
"- Own the prose slice summary at `<artifacts-dir>/tdd-slices/S-<id>.md` yourself.",
|