cclaw-cli 0.51.29 → 0.55.2
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/README.md +22 -16
- package/dist/artifact-linter/brainstorm.d.ts +2 -0
- package/dist/artifact-linter/brainstorm.js +245 -0
- package/dist/artifact-linter/design.d.ts +2 -0
- package/dist/artifact-linter/design.js +323 -0
- package/dist/artifact-linter/plan.d.ts +2 -0
- package/dist/artifact-linter/plan.js +162 -0
- package/dist/artifact-linter/review-army.d.ts +24 -0
- package/dist/artifact-linter/review-army.js +365 -0
- package/dist/artifact-linter/review.d.ts +2 -0
- package/dist/artifact-linter/review.js +65 -0
- package/dist/artifact-linter/scope.d.ts +2 -0
- package/dist/artifact-linter/scope.js +115 -0
- package/dist/artifact-linter/shared.d.ts +246 -0
- package/dist/artifact-linter/shared.js +1488 -0
- package/dist/artifact-linter/ship.d.ts +2 -0
- package/dist/artifact-linter/ship.js +46 -0
- package/dist/artifact-linter/spec.d.ts +2 -0
- package/dist/artifact-linter/spec.js +108 -0
- package/dist/artifact-linter/tdd.d.ts +2 -0
- package/dist/artifact-linter/tdd.js +124 -0
- package/dist/artifact-linter.d.ts +4 -76
- package/dist/artifact-linter.js +56 -2949
- package/dist/cli.d.ts +2 -18
- package/dist/cli.js +8 -246
- package/dist/codex-feature-flag.d.ts +1 -1
- package/dist/codex-feature-flag.js +1 -1
- package/dist/config.d.ts +3 -2
- package/dist/config.js +67 -3
- package/dist/constants.d.ts +1 -7
- package/dist/constants.js +9 -15
- package/dist/content/cancel-command.js +2 -2
- package/dist/content/closeout-guidance.js +13 -10
- package/dist/content/core-agents.d.ts +18 -0
- package/dist/content/core-agents.js +51 -7
- package/dist/content/decision-protocol.d.ts +1 -1
- package/dist/content/decision-protocol.js +1 -1
- package/dist/content/examples.js +6 -6
- package/dist/content/harness-doc.js +20 -2
- package/dist/content/hook-inline-snippets.d.ts +17 -4
- package/dist/content/hook-inline-snippets.js +218 -5
- package/dist/content/hook-manifest.d.ts +2 -2
- package/dist/content/hook-manifest.js +2 -2
- package/dist/content/hooks.d.ts +1 -0
- package/dist/content/hooks.js +32 -137
- package/dist/content/idea-command.d.ts +8 -0
- package/dist/content/{ideate-command.js → idea-command.js} +57 -50
- package/dist/content/idea-frames.d.ts +31 -0
- package/dist/content/{ideate-frames.js → idea-frames.js} +9 -9
- package/dist/content/idea-ranking.d.ts +25 -0
- package/dist/content/{ideate-ranking.js → idea-ranking.js} +5 -5
- package/dist/content/iron-laws.d.ts +0 -1
- package/dist/content/iron-laws.js +31 -16
- package/dist/content/learnings.js +1 -1
- package/dist/content/meta-skill.js +11 -13
- package/dist/content/node-hooks.d.ts +10 -0
- package/dist/content/node-hooks.js +45 -11
- package/dist/content/opencode-plugin.js +3 -3
- package/dist/content/session-hooks.js +1 -1
- package/dist/content/skills.js +19 -7
- package/dist/content/stage-command.js +1 -1
- package/dist/content/stage-schema.js +44 -2
- package/dist/content/stages/_lint-metadata/index.js +26 -2
- package/dist/content/stages/brainstorm.js +13 -7
- package/dist/content/stages/design.js +16 -11
- package/dist/content/stages/plan.js +9 -6
- package/dist/content/stages/review.js +4 -4
- package/dist/content/stages/schema-types.d.ts +1 -1
- package/dist/content/stages/scope.js +15 -12
- package/dist/content/stages/ship.js +2 -2
- package/dist/content/stages/spec.js +9 -3
- package/dist/content/stages/tdd.js +14 -4
- package/dist/content/start-command.d.ts +2 -2
- package/dist/content/start-command.js +24 -21
- package/dist/content/status-command.js +8 -8
- package/dist/content/subagents.js +61 -7
- package/dist/content/templates.d.ts +1 -1
- package/dist/content/templates.js +104 -152
- package/dist/content/tree-command.js +2 -2
- package/dist/content/utility-skills.d.ts +2 -2
- package/dist/content/utility-skills.js +2 -2
- package/dist/content/view-command.js +4 -2
- package/dist/delegation.d.ts +2 -0
- package/dist/delegation.js +2 -1
- package/dist/early-loop.d.ts +66 -0
- package/dist/early-loop.js +275 -0
- package/dist/flow-state.d.ts +1 -1
- package/dist/flow-state.js +1 -1
- package/dist/gate-evidence.d.ts +8 -0
- package/dist/gate-evidence.js +141 -5
- package/dist/harness-adapters.d.ts +2 -2
- package/dist/harness-adapters.js +54 -122
- package/dist/harness-selection.d.ts +31 -0
- package/dist/harness-selection.js +214 -0
- package/dist/install.js +166 -38
- package/dist/internal/advance-stage/advance.d.ts +50 -0
- package/dist/internal/advance-stage/advance.js +480 -0
- package/dist/internal/advance-stage/cancel-run.d.ts +8 -0
- package/dist/internal/advance-stage/cancel-run.js +19 -0
- package/dist/internal/advance-stage/flow-state-coercion.d.ts +3 -0
- package/dist/internal/advance-stage/flow-state-coercion.js +81 -0
- package/dist/internal/advance-stage/helpers.d.ts +14 -0
- package/dist/internal/advance-stage/helpers.js +145 -0
- package/dist/internal/advance-stage/hook.d.ts +8 -0
- package/dist/internal/advance-stage/hook.js +40 -0
- package/dist/internal/advance-stage/parsers.d.ts +54 -0
- package/dist/internal/advance-stage/parsers.js +307 -0
- package/dist/internal/advance-stage/review-loop.d.ts +7 -0
- package/dist/internal/advance-stage/review-loop.js +170 -0
- package/dist/internal/advance-stage/rewind.d.ts +14 -0
- package/dist/internal/advance-stage/rewind.js +108 -0
- package/dist/internal/advance-stage/start-flow.d.ts +11 -0
- package/dist/internal/advance-stage/start-flow.js +136 -0
- package/dist/internal/advance-stage/verify.d.ts +29 -0
- package/dist/internal/advance-stage/verify.js +225 -0
- package/dist/internal/advance-stage.js +21 -1470
- package/dist/internal/compound-readiness.d.ts +1 -1
- package/dist/internal/compound-readiness.js +2 -2
- package/dist/internal/early-loop-status.d.ts +7 -0
- package/dist/internal/early-loop-status.js +90 -0
- package/dist/internal/runtime-integrity.d.ts +7 -0
- package/dist/internal/runtime-integrity.js +288 -0
- package/dist/internal/tdd-red-evidence.js +1 -1
- package/dist/knowledge-store.d.ts +3 -8
- package/dist/knowledge-store.js +16 -29
- package/dist/managed-resources.js +24 -2
- package/dist/policy.js +5 -7
- package/dist/run-archive.d.ts +1 -1
- package/dist/run-archive.js +16 -16
- package/dist/run-persistence.js +112 -12
- package/dist/tdd-cycle.d.ts +3 -3
- package/dist/tdd-cycle.js +1 -1
- package/dist/types.d.ts +18 -10
- package/package.json +1 -1
- package/dist/content/finish-command.d.ts +0 -2
- package/dist/content/finish-command.js +0 -26
- package/dist/content/ideate-command.d.ts +0 -8
- package/dist/content/ideate-frames.d.ts +0 -31
- package/dist/content/ideate-ranking.d.ts +0 -25
- package/dist/content/next-command.d.ts +0 -20
- package/dist/content/next-command.js +0 -298
- package/dist/content/seed-shelf.d.ts +0 -36
- package/dist/content/seed-shelf.js +0 -301
- package/dist/content/stage-common-guidance.d.ts +0 -1
- package/dist/content/stage-common-guidance.js +0 -106
- package/dist/doctor-registry.d.ts +0 -10
- package/dist/doctor-registry.js +0 -186
- package/dist/doctor.d.ts +0 -17
- package/dist/doctor.js +0 -2206
- package/dist/internal/hook-manifest.d.ts +0 -16
- package/dist/internal/hook-manifest.js +0 -77
package/dist/install.js
CHANGED
|
@@ -5,17 +5,15 @@ import { promisify } from "node:util";
|
|
|
5
5
|
import { CCLAW_VERSION, FLOW_VERSION, REQUIRED_DIRS, RUNTIME_ROOT } from "./constants.js";
|
|
6
6
|
import { writeConfig, createDefaultConfig, readConfig, configPath, detectLanguageRulePacks, detectAdvancedKeys } from "./config.js";
|
|
7
7
|
import { learnSkillMarkdown } from "./content/learnings.js";
|
|
8
|
-
import { nextCommandContract, nextCommandSkillMarkdown } from "./content/next-command.js";
|
|
9
8
|
import { stageCommandShimMarkdown } from "./content/stage-command.js";
|
|
10
|
-
import {
|
|
9
|
+
import { ideaCommandContract, ideaCommandSkillMarkdown } from "./content/idea-command.js";
|
|
11
10
|
import { startCommandContract, startCommandSkillMarkdown } from "./content/start-command.js";
|
|
12
11
|
import { viewCommandContract, viewCommandSkillMarkdown } from "./content/view-command.js";
|
|
13
|
-
import { finishCommandContract, finishCommandSkillMarkdown } from "./content/finish-command.js";
|
|
14
12
|
import { cancelCommandContract, cancelCommandSkillMarkdown } from "./content/cancel-command.js";
|
|
15
13
|
import { subagentDrivenDevSkill, parallelAgentsSkill } from "./content/subagents.js";
|
|
16
14
|
import { sessionHooksSkillMarkdown } from "./content/session-hooks.js";
|
|
17
15
|
import { ironLawRuntimeDocument, ironLawsSkillMarkdown } from "./content/iron-laws.js";
|
|
18
|
-
import { stageCompleteScript, startFlowScript, runHookCmdScript, delegationRecordScript, opencodePluginJs, claudeHooksJson, codexHooksJson, cursorHooksJson } from "./content/hooks.js";
|
|
16
|
+
import { stageCompleteScript, startFlowScript, cancelRunScript, runHookCmdScript, delegationRecordScript, opencodePluginJs, claudeHooksJson, codexHooksJson, cursorHooksJson } from "./content/hooks.js";
|
|
19
17
|
import { nodeHookRuntimeScript } from "./content/node-hooks.js";
|
|
20
18
|
import { META_SKILL_NAME, usingCclawSkillMarkdown } from "./content/meta-skill.js";
|
|
21
19
|
import { ARTIFACT_TEMPLATES, CURSOR_WORKFLOW_RULE_MDC, RULEBOOK_MARKDOWN, buildRulesJson } from "./content/templates.js";
|
|
@@ -30,10 +28,11 @@ import { createInitialFlowState } from "./flow-state.js";
|
|
|
30
28
|
import { ensureDir, exists, writeFileSafe } from "./fs-utils.js";
|
|
31
29
|
import { ManagedResourceSession, setActiveManagedResourceSession } from "./managed-resources.js";
|
|
32
30
|
import { ensureGitignore, removeGitignorePatterns } from "./gitignore.js";
|
|
33
|
-
import { HARNESS_ADAPTERS, harnessShimFileNames, syncHarnessShims, removeCclawFromAgentsMd } from "./harness-adapters.js";
|
|
31
|
+
import { HARNESS_ADAPTERS, harnessShimFileNames, harnessShimSkillNames, syncHarnessShims, removeCclawFromAgentsMd } from "./harness-adapters.js";
|
|
34
32
|
import { validateHookDocument } from "./hook-schema.js";
|
|
35
33
|
import { detectHarnesses } from "./init-detect.js";
|
|
36
|
-
import {
|
|
34
|
+
import { classifyCodexHooksFlag, codexConfigPath, readCodexConfig } from "./codex-feature-flag.js";
|
|
35
|
+
import { CorruptFlowStateError, ensureRunSystem } from "./runs.js";
|
|
37
36
|
import { FLOW_STAGES } from "./types.js";
|
|
38
37
|
const OPENCODE_PLUGIN_REL_PATH = ".opencode/plugins/cclaw-plugin.mjs";
|
|
39
38
|
const CURSOR_RULE_REL_PATH = ".cursor/rules/cclaw-workflow.mdc";
|
|
@@ -50,6 +49,23 @@ async function writeInitSentinel(projectRoot, operation) {
|
|
|
50
49
|
await writeFileSafe(sentinelPath, `${JSON.stringify({ operation, startedAt: new Date().toISOString() }, null, 2)}\n`);
|
|
51
50
|
return sentinelPath;
|
|
52
51
|
}
|
|
52
|
+
async function warnStaleInitSentinel(projectRoot, operation) {
|
|
53
|
+
const sentinelPath = runtimePath(projectRoot, "state", INIT_SENTINEL_FILE);
|
|
54
|
+
if (!(await exists(sentinelPath)))
|
|
55
|
+
return;
|
|
56
|
+
let startedAt = "unknown time";
|
|
57
|
+
try {
|
|
58
|
+
const raw = await fs.readFile(sentinelPath, "utf8");
|
|
59
|
+
const parsed = JSON.parse(raw);
|
|
60
|
+
if (parsed && typeof parsed.startedAt === "string" && parsed.startedAt.trim().length > 0) {
|
|
61
|
+
startedAt = parsed.startedAt;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// best-effort parse of stale sentinel metadata
|
|
66
|
+
}
|
|
67
|
+
process.stderr.write(`[${operation}] Detected stale .init-in-progress sentinel from ${startedAt}; previous run may have crashed. Continuing.\n`);
|
|
68
|
+
}
|
|
53
69
|
async function removeBestEffort(targetPath, recursive = false) {
|
|
54
70
|
try {
|
|
55
71
|
await fs.rm(targetPath, { recursive, force: true });
|
|
@@ -90,6 +106,16 @@ const DEPRECATED_UTILITY_SKILL_FOLDERS = [
|
|
|
90
106
|
"flow-tree",
|
|
91
107
|
"flow-diff"
|
|
92
108
|
];
|
|
109
|
+
const DEPRECATED_STAGE_SKILL_FOLDERS = [
|
|
110
|
+
"brainstorming",
|
|
111
|
+
"scope-shaping",
|
|
112
|
+
"engineering-design-lock",
|
|
113
|
+
"specification-authoring",
|
|
114
|
+
"planning-and-task-breakdown",
|
|
115
|
+
"test-driven-development",
|
|
116
|
+
"two-layer-review",
|
|
117
|
+
"shipping-and-handoff"
|
|
118
|
+
];
|
|
93
119
|
const DEPRECATED_AGENT_FILES = [
|
|
94
120
|
"securityer.md",
|
|
95
121
|
"spec-reviewer.md",
|
|
@@ -102,6 +128,7 @@ const DEPRECATED_AGENT_FILES = [
|
|
|
102
128
|
];
|
|
103
129
|
const DEPRECATED_COMMAND_FILES = [
|
|
104
130
|
"learn.md",
|
|
131
|
+
"finish.md",
|
|
105
132
|
"status.md",
|
|
106
133
|
"tree.md",
|
|
107
134
|
"diff.md",
|
|
@@ -114,6 +141,7 @@ const DEPRECATED_COMMAND_FILES = [
|
|
|
114
141
|
"rewind.md"
|
|
115
142
|
];
|
|
116
143
|
const DEPRECATED_SKILL_FILES = [
|
|
144
|
+
["flow-finish", "SKILL.md"],
|
|
117
145
|
["flow-ops", "SKILL.md"],
|
|
118
146
|
["tdd-cycle-log", "SKILL.md"],
|
|
119
147
|
["flow-retro", "SKILL.md"],
|
|
@@ -450,11 +478,9 @@ async function writeSkills(projectRoot, config) {
|
|
|
450
478
|
}
|
|
451
479
|
// Utility skills (not flow stages)
|
|
452
480
|
await writeFileSafe(runtimePath(projectRoot, "skills", "learnings", "SKILL.md"), learnSkillMarkdown());
|
|
453
|
-
await writeFileSafe(runtimePath(projectRoot, "skills", "flow-
|
|
454
|
-
await writeFileSafe(runtimePath(projectRoot, "skills", "flow-ideate", "SKILL.md"), ideateCommandSkillMarkdown());
|
|
481
|
+
await writeFileSafe(runtimePath(projectRoot, "skills", "flow-idea", "SKILL.md"), ideaCommandSkillMarkdown());
|
|
455
482
|
await writeFileSafe(runtimePath(projectRoot, "skills", "flow-start", "SKILL.md"), startCommandSkillMarkdown());
|
|
456
483
|
await writeFileSafe(runtimePath(projectRoot, "skills", "flow-view", "SKILL.md"), viewCommandSkillMarkdown());
|
|
457
|
-
await writeFileSafe(runtimePath(projectRoot, "skills", "flow-finish", "SKILL.md"), finishCommandSkillMarkdown());
|
|
458
484
|
await writeFileSafe(runtimePath(projectRoot, "skills", "flow-cancel", "SKILL.md"), cancelCommandSkillMarkdown());
|
|
459
485
|
await writeFileSafe(runtimePath(projectRoot, "skills", "subagent-dev", "SKILL.md"), subagentDrivenDevSkill());
|
|
460
486
|
await writeFileSafe(runtimePath(projectRoot, "skills", "parallel-dispatch", "SKILL.md"), parallelAgentsSkill());
|
|
@@ -516,10 +542,8 @@ async function writeSkills(projectRoot, config) {
|
|
|
516
542
|
}
|
|
517
543
|
async function writeEntryCommands(projectRoot) {
|
|
518
544
|
await writeFileSafe(runtimePath(projectRoot, "commands", "start.md"), startCommandContract());
|
|
519
|
-
await writeFileSafe(runtimePath(projectRoot, "commands", "
|
|
520
|
-
await writeFileSafe(runtimePath(projectRoot, "commands", "ideate.md"), ideateCommandContract());
|
|
545
|
+
await writeFileSafe(runtimePath(projectRoot, "commands", "idea.md"), ideaCommandContract());
|
|
521
546
|
await writeFileSafe(runtimePath(projectRoot, "commands", "view.md"), viewCommandContract());
|
|
522
|
-
await writeFileSafe(runtimePath(projectRoot, "commands", "finish.md"), finishCommandContract());
|
|
523
547
|
await writeFileSafe(runtimePath(projectRoot, "commands", "cancel.md"), cancelCommandContract());
|
|
524
548
|
for (const stage of FLOW_STAGES) {
|
|
525
549
|
await writeFileSafe(runtimePath(projectRoot, "commands", `${stage}.md`), stageCommandShimMarkdown(stage));
|
|
@@ -859,14 +883,16 @@ async function writeMergedHookJson(projectRoot, hookFilePath, generatedJson) {
|
|
|
859
883
|
if (harness) {
|
|
860
884
|
const generatedSchema = validateHookDocument(harness, generatedDoc);
|
|
861
885
|
if (!generatedSchema.ok) {
|
|
862
|
-
throw new Error(`
|
|
886
|
+
throw new Error(`[sync fail-fast] Hook document drift detected for ${harness}: generated hook document is invalid (${generatedSchema.errors.join("; ")}). ` +
|
|
887
|
+
"Run `npx cclaw-cli sync` to regenerate managed hooks or repair the generated hook shape manually.");
|
|
863
888
|
}
|
|
864
889
|
}
|
|
865
890
|
const mergedDoc = mergeHookDocuments(existingDoc, generatedDoc);
|
|
866
891
|
if (harness) {
|
|
867
892
|
const mergedSchema = validateHookDocument(harness, mergedDoc);
|
|
868
893
|
if (!mergedSchema.ok) {
|
|
869
|
-
throw new Error(`
|
|
894
|
+
throw new Error(`[sync fail-fast] Hook document drift detected for ${harness}: merged hook document is invalid (${mergedSchema.errors.join("; ")}). ` +
|
|
895
|
+
"Run `npx cclaw-cli sync` after fixing the custom hook entry or remove the malformed user-authored hook block.");
|
|
870
896
|
}
|
|
871
897
|
}
|
|
872
898
|
await writeFileSafe(hookFilePath, `${JSON.stringify(mergedDoc, null, 2)}\n`);
|
|
@@ -884,11 +910,14 @@ async function writeHooks(projectRoot, config) {
|
|
|
884
910
|
}), null, 2)}\n`);
|
|
885
911
|
await writeFileSafe(path.join(hooksDir, "stage-complete.mjs"), stageCompleteScript());
|
|
886
912
|
await writeFileSafe(path.join(hooksDir, "start-flow.mjs"), startFlowScript());
|
|
913
|
+
await writeFileSafe(path.join(hooksDir, "cancel-run.mjs"), cancelRunScript());
|
|
887
914
|
await writeFileSafe(path.join(hooksDir, "run-hook.mjs"), nodeHookRuntimeScript({
|
|
888
915
|
strictness: effectiveStrictness,
|
|
889
916
|
tddTestPathPatterns: config.tdd?.testPathPatterns ?? config.tddTestGlobs,
|
|
890
917
|
tddProductionPathPatterns: config.tdd?.productionPathPatterns,
|
|
891
|
-
compoundRecurrenceThreshold: config.compound?.recurrenceThreshold
|
|
918
|
+
compoundRecurrenceThreshold: config.compound?.recurrenceThreshold,
|
|
919
|
+
earlyLoopEnabled: config.earlyLoop?.enabled,
|
|
920
|
+
earlyLoopMaxIterations: config.earlyLoop?.maxIterations
|
|
892
921
|
}));
|
|
893
922
|
await writeFileSafe(path.join(hooksDir, "run-hook.cmd"), runHookCmdScript());
|
|
894
923
|
await writeFileSafe(path.join(hooksDir, "delegation-record.mjs"), delegationRecordScript());
|
|
@@ -901,7 +930,8 @@ async function writeHooks(projectRoot, config) {
|
|
|
901
930
|
"run-hook.mjs",
|
|
902
931
|
"run-hook.cmd",
|
|
903
932
|
"delegation-record.mjs",
|
|
904
|
-
"opencode-plugin.mjs"
|
|
933
|
+
"opencode-plugin.mjs",
|
|
934
|
+
"cancel-run.mjs"
|
|
905
935
|
]) {
|
|
906
936
|
await fs.chmod(path.join(hooksDir, script), 0o755);
|
|
907
937
|
}
|
|
@@ -939,8 +969,8 @@ async function writeHooks(projectRoot, config) {
|
|
|
939
969
|
// flag in `~/.codex/config.toml`. cclaw always writes the file so
|
|
940
970
|
// the moment the flag flips on, the cclaw hooks start firing. See
|
|
941
971
|
// `codexHooksJsonWithObservation` for the Bash-only caveat on
|
|
942
|
-
// PreToolUse/PostToolUse.
|
|
943
|
-
//
|
|
972
|
+
// PreToolUse/PostToolUse. If the feature flag is off, hooks remain
|
|
973
|
+
// inert until the user enables codex_hooks in ~/.codex/config.toml.
|
|
944
974
|
const codexDir = path.join(projectRoot, ".codex");
|
|
945
975
|
await ensureDir(codexDir);
|
|
946
976
|
await writeMergedHookJson(projectRoot, path.join(codexDir, "hooks.json"), codexHooksJson());
|
|
@@ -990,7 +1020,7 @@ async function syncDisabledHarnessArtifacts(projectRoot, harnesses) {
|
|
|
990
1020
|
for (const entry of managedHookFiles) {
|
|
991
1021
|
if (enabled.has(entry.harness))
|
|
992
1022
|
continue;
|
|
993
|
-
await removeManagedHookEntries(entry.hookPath);
|
|
1023
|
+
await removeManagedHookEntries(entry.hookPath, { failOnParseError: true });
|
|
994
1024
|
}
|
|
995
1025
|
if (!enabled.has("opencode")) {
|
|
996
1026
|
try {
|
|
@@ -999,8 +1029,22 @@ async function syncDisabledHarnessArtifacts(projectRoot, harnesses) {
|
|
|
999
1029
|
catch {
|
|
1000
1030
|
// best-effort cleanup
|
|
1001
1031
|
}
|
|
1032
|
+
try {
|
|
1033
|
+
await fs.rm(path.join(projectRoot, ".opencode/agents"), { recursive: true, force: true });
|
|
1034
|
+
}
|
|
1035
|
+
catch {
|
|
1036
|
+
// best-effort cleanup
|
|
1037
|
+
}
|
|
1002
1038
|
await removeManagedOpenCodePluginConfig(projectRoot, OPENCODE_PLUGIN_REL_PATH);
|
|
1003
1039
|
}
|
|
1040
|
+
if (!enabled.has("codex")) {
|
|
1041
|
+
try {
|
|
1042
|
+
await fs.rm(path.join(projectRoot, ".codex/agents"), { recursive: true, force: true });
|
|
1043
|
+
}
|
|
1044
|
+
catch {
|
|
1045
|
+
// best-effort cleanup
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1004
1048
|
}
|
|
1005
1049
|
async function writeState(projectRoot, config, forceReset = false) {
|
|
1006
1050
|
const statePath = runtimePath(projectRoot, "state", "flow-state.json");
|
|
@@ -1014,6 +1058,9 @@ async function cleanLegacyArtifacts(projectRoot) {
|
|
|
1014
1058
|
for (const legacyFolder of DEPRECATED_UTILITY_SKILL_FOLDERS) {
|
|
1015
1059
|
await removeBestEffort(runtimePath(projectRoot, "skills", legacyFolder), true);
|
|
1016
1060
|
}
|
|
1061
|
+
for (const legacyFolder of DEPRECATED_STAGE_SKILL_FOLDERS) {
|
|
1062
|
+
await removeBestEffort(runtimePath(projectRoot, "skills", legacyFolder), true);
|
|
1063
|
+
}
|
|
1017
1064
|
for (const legacyAgentFile of DEPRECATED_AGENT_FILES) {
|
|
1018
1065
|
await removeBestEffort(runtimePath(projectRoot, "agents", legacyAgentFile));
|
|
1019
1066
|
}
|
|
@@ -1038,16 +1085,30 @@ async function cleanLegacyArtifacts(projectRoot) {
|
|
|
1038
1085
|
for (const legacyRuntimeDir of DEPRECATED_RUNTIME_DIRS) {
|
|
1039
1086
|
await removeBestEffort(runtimePath(projectRoot, legacyRuntimeDir), true);
|
|
1040
1087
|
}
|
|
1041
|
-
//
|
|
1042
|
-
//
|
|
1088
|
+
// Archive storage migration: `.cclaw/runs` is legacy and no longer a valid
|
|
1089
|
+
// archive root. Remove only when empty; otherwise keep it so users can
|
|
1090
|
+
// manually migrate or inspect old data.
|
|
1091
|
+
const legacyRunsDir = runtimePath(projectRoot, "runs");
|
|
1092
|
+
try {
|
|
1093
|
+
const entries = await fs.readdir(legacyRunsDir);
|
|
1094
|
+
if (entries.length === 0) {
|
|
1095
|
+
await fs.rm(legacyRunsDir, { recursive: true, force: true });
|
|
1096
|
+
}
|
|
1097
|
+
}
|
|
1098
|
+
catch {
|
|
1099
|
+
// missing or unreadable legacy dir; keep best-effort behavior
|
|
1100
|
+
}
|
|
1101
|
+
// D-4 terminology migration: rename historical ideation artifact prefixes to
|
|
1102
|
+
// the canonical idea-* naming without deleting user-authored content.
|
|
1103
|
+
const legacyIdeaArtifactPattern = /^ideation-(.+\.md)$/u;
|
|
1043
1104
|
const artifactsDir = runtimePath(projectRoot, "artifacts");
|
|
1044
1105
|
try {
|
|
1045
1106
|
const entries = await fs.readdir(artifactsDir);
|
|
1046
1107
|
for (const entry of entries) {
|
|
1047
|
-
const match =
|
|
1108
|
+
const match = legacyIdeaArtifactPattern.exec(entry);
|
|
1048
1109
|
if (!match)
|
|
1049
1110
|
continue;
|
|
1050
|
-
const nextName = `
|
|
1111
|
+
const nextName = `idea-${match[1]}`;
|
|
1051
1112
|
const from = path.join(artifactsDir, entry);
|
|
1052
1113
|
const to = path.join(artifactsDir, nextName);
|
|
1053
1114
|
if (await exists(to)) {
|
|
@@ -1062,13 +1123,8 @@ async function cleanLegacyArtifacts(projectRoot) {
|
|
|
1062
1123
|
}
|
|
1063
1124
|
async function cleanStaleFiles(projectRoot) {
|
|
1064
1125
|
const expectedShimFiles = new Set(harnessShimFileNames());
|
|
1126
|
+
const expectedShimSkills = new Set(harnessShimFileNames().map((fileName) => fileName.replace(/\.md$/u, "")));
|
|
1065
1127
|
for (const adapter of Object.values(HARNESS_ADAPTERS)) {
|
|
1066
|
-
// Skill-kind shims (Codex) live in per-skill directories, not flat
|
|
1067
|
-
// markdown files, so the regex-based stale sweep below would never
|
|
1068
|
-
// match them anyway. The legacy `.codex/commands/` cleanup happens in
|
|
1069
|
-
// `cleanupLegacyCodexSurfaces` inside syncHarnessShims().
|
|
1070
|
-
if (adapter.shimKind === "skill")
|
|
1071
|
-
continue;
|
|
1072
1128
|
const commandDir = path.join(projectRoot, adapter.commandDir);
|
|
1073
1129
|
if (!(await exists(commandDir)))
|
|
1074
1130
|
continue;
|
|
@@ -1079,6 +1135,16 @@ async function cleanStaleFiles(projectRoot) {
|
|
|
1079
1135
|
catch {
|
|
1080
1136
|
entries = [];
|
|
1081
1137
|
}
|
|
1138
|
+
if (adapter.shimKind === "skill") {
|
|
1139
|
+
for (const entry of entries) {
|
|
1140
|
+
if (!/^cc(?:-.*)?$/u.test(entry))
|
|
1141
|
+
continue;
|
|
1142
|
+
if (expectedShimSkills.has(entry))
|
|
1143
|
+
continue;
|
|
1144
|
+
await fs.rm(path.join(commandDir, entry), { recursive: true, force: true });
|
|
1145
|
+
}
|
|
1146
|
+
continue;
|
|
1147
|
+
}
|
|
1082
1148
|
for (const entry of entries) {
|
|
1083
1149
|
if (!/^cc(?:-.*)?\.md$/u.test(entry))
|
|
1084
1150
|
continue;
|
|
@@ -1090,7 +1156,34 @@ async function cleanStaleFiles(projectRoot) {
|
|
|
1090
1156
|
// Keep user-owned custom assets under .cclaw/agents and .cclaw/skills.
|
|
1091
1157
|
// Legacy managed removals happen in cleanLegacyArtifacts() with explicit paths.
|
|
1092
1158
|
}
|
|
1159
|
+
async function assertExpectedHarnessShims(projectRoot, harnesses) {
|
|
1160
|
+
const expectedFiles = harnessShimFileNames();
|
|
1161
|
+
const expectedSkillFolders = harnessShimSkillNames();
|
|
1162
|
+
for (const harness of harnesses) {
|
|
1163
|
+
const adapter = HARNESS_ADAPTERS[harness];
|
|
1164
|
+
const base = path.join(projectRoot, adapter.commandDir);
|
|
1165
|
+
for (const fileName of expectedFiles) {
|
|
1166
|
+
const target = adapter.shimKind === "skill"
|
|
1167
|
+
? path.join(base, fileName.replace(/\.md$/u, ""), "SKILL.md")
|
|
1168
|
+
: path.join(base, fileName);
|
|
1169
|
+
if (!(await exists(target))) {
|
|
1170
|
+
throw new Error(`[sync fail-fast] Harness shim drift detected for ${harness}: missing ${target}. ` +
|
|
1171
|
+
`Run \`npx cclaw-cli sync\` again; if the file is still missing, inspect harness permissions/paths.`);
|
|
1172
|
+
}
|
|
1173
|
+
}
|
|
1174
|
+
if (adapter.shimKind === "skill") {
|
|
1175
|
+
for (const folder of expectedSkillFolders) {
|
|
1176
|
+
const skillPath = path.join(base, folder, "SKILL.md");
|
|
1177
|
+
if (!(await exists(skillPath))) {
|
|
1178
|
+
throw new Error(`[sync fail-fast] Harness skill shim drift detected for ${harness}: missing ${skillPath}. ` +
|
|
1179
|
+
`Run \`npx cclaw-cli sync\` again; if the issue persists, inspect generated .agents/skills surfaces.`);
|
|
1180
|
+
}
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1093
1185
|
async function materializeRuntime(projectRoot, config, forceStateReset, operation = "sync") {
|
|
1186
|
+
await warnStaleInitSentinel(projectRoot, operation);
|
|
1094
1187
|
const sentinelPath = await writeInitSentinel(projectRoot, operation);
|
|
1095
1188
|
const managedSession = await ManagedResourceSession.create({ projectRoot, operation });
|
|
1096
1189
|
setActiveManagedResourceSession(managedSession);
|
|
@@ -1106,25 +1199,51 @@ async function materializeRuntime(projectRoot, config, forceStateReset, operatio
|
|
|
1106
1199
|
writeRulebook(projectRoot)
|
|
1107
1200
|
]);
|
|
1108
1201
|
await writeState(projectRoot, config, forceStateReset);
|
|
1109
|
-
|
|
1202
|
+
try {
|
|
1203
|
+
await ensureRunSystem(projectRoot, { createIfMissing: false });
|
|
1204
|
+
}
|
|
1205
|
+
catch (error) {
|
|
1206
|
+
if (error instanceof CorruptFlowStateError) {
|
|
1207
|
+
throw new Error(`[sync fail-fast] Corrupt flow state detected: ${error.message} ` +
|
|
1208
|
+
`Resolve the quarantined flow-state file and re-run \`npx cclaw-cli sync\`.`);
|
|
1209
|
+
}
|
|
1210
|
+
throw error;
|
|
1211
|
+
}
|
|
1110
1212
|
await ensureKnowledgeStore(projectRoot);
|
|
1111
1213
|
await writeHooks(projectRoot, config);
|
|
1112
1214
|
await syncDisabledHarnessArtifacts(projectRoot, harnesses);
|
|
1113
1215
|
await syncManagedGitHooks(projectRoot, config);
|
|
1114
1216
|
await syncHarnessShims(projectRoot, harnesses);
|
|
1217
|
+
await assertExpectedHarnessShims(projectRoot, harnesses);
|
|
1115
1218
|
await writeCursorWorkflowRule(projectRoot, harnesses);
|
|
1116
1219
|
await ensureGitignore(projectRoot);
|
|
1117
1220
|
await managedSession.commit();
|
|
1118
1221
|
await fs.unlink(sentinelPath).catch(() => undefined);
|
|
1119
1222
|
}
|
|
1120
1223
|
catch (error) {
|
|
1121
|
-
// Leave the sentinel in place so
|
|
1224
|
+
// Leave the sentinel in place so the interrupted run is visible.
|
|
1122
1225
|
throw error;
|
|
1123
1226
|
}
|
|
1124
1227
|
finally {
|
|
1125
1228
|
setActiveManagedResourceSession(null);
|
|
1126
1229
|
}
|
|
1127
1230
|
}
|
|
1231
|
+
async function warnCodexHooksFeatureFlagIfDisabled(harnesses) {
|
|
1232
|
+
if (!harnesses.includes("codex"))
|
|
1233
|
+
return;
|
|
1234
|
+
const codexTomlPath = codexConfigPath();
|
|
1235
|
+
let existing;
|
|
1236
|
+
try {
|
|
1237
|
+
existing = await readCodexConfig(codexTomlPath);
|
|
1238
|
+
}
|
|
1239
|
+
catch (error) {
|
|
1240
|
+
process.stderr.write(`cclaw: could not read ${codexTomlPath} to validate codex_hooks flag: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
1241
|
+
return;
|
|
1242
|
+
}
|
|
1243
|
+
if (classifyCodexHooksFlag(existing) === "enabled")
|
|
1244
|
+
return;
|
|
1245
|
+
process.stderr.write(`cclaw: Codex hooks file written, but [features] codex_hooks is not true in ${codexTomlPath} — hooks are inert until you enable it.\n`);
|
|
1246
|
+
}
|
|
1128
1247
|
export async function initCclaw(options) {
|
|
1129
1248
|
if (options.harnesses !== undefined && options.harnesses.length === 0) {
|
|
1130
1249
|
throw new Error("Select at least one harness.");
|
|
@@ -1153,7 +1272,7 @@ export async function syncCclaw(projectRoot, options = {}) {
|
|
|
1153
1272
|
// Prefer detected harness markers over the hardcoded default list.
|
|
1154
1273
|
// Without this, a user running `cclaw sync` in a `.claude`-only
|
|
1155
1274
|
// project ends up with a config that also enables cursor/opencode/
|
|
1156
|
-
// codex, which then
|
|
1275
|
+
// codex, which then creates invalid harness expectations.
|
|
1157
1276
|
// Fall back to the previous default (config.harnesses) if no markers
|
|
1158
1277
|
// are found so brand-new projects still bootstrap cleanly.
|
|
1159
1278
|
const detected = await detectHarnesses(projectRoot);
|
|
@@ -1173,6 +1292,7 @@ export async function syncCclaw(projectRoot, options = {}) {
|
|
|
1173
1292
|
});
|
|
1174
1293
|
}
|
|
1175
1294
|
await materializeRuntime(projectRoot, config, false, "sync");
|
|
1295
|
+
await warnCodexHooksFeatureFlagIfDisabled(config.harnesses);
|
|
1176
1296
|
}
|
|
1177
1297
|
/**
|
|
1178
1298
|
* Refresh generated files in `.cclaw/` without touching user-authored
|
|
@@ -1270,20 +1390,28 @@ function isManagedRuntimeHookCommand(command) {
|
|
|
1270
1390
|
// Codex UserPromptSubmit non-blocking state nudge.
|
|
1271
1391
|
return /internal verify-current-state(?:\s|$)/u.test(normalized);
|
|
1272
1392
|
}
|
|
1273
|
-
async function removeManagedHookEntries(hookFilePath) {
|
|
1393
|
+
async function removeManagedHookEntries(hookFilePath, options = {}) {
|
|
1274
1394
|
if (!(await exists(hookFilePath)))
|
|
1275
1395
|
return;
|
|
1276
1396
|
let parsed = null;
|
|
1277
1397
|
try {
|
|
1278
1398
|
const raw = await fs.readFile(hookFilePath, "utf8");
|
|
1279
1399
|
const recovered = tryParseHookDocument(raw);
|
|
1280
|
-
|
|
1400
|
+
if (recovered === null) {
|
|
1401
|
+
if (options.failOnParseError === true) {
|
|
1402
|
+
throw new Error(`[sync fail-fast] Cannot strip managed hook entries from ${hookFilePath} — JSON is unparseable. ` +
|
|
1403
|
+
`Run \`rm ${hookFilePath}\` and rerun \`npx cclaw-cli sync\`.`);
|
|
1404
|
+
}
|
|
1405
|
+
return;
|
|
1406
|
+
}
|
|
1407
|
+
parsed = recovered.parsed;
|
|
1281
1408
|
}
|
|
1282
|
-
catch {
|
|
1409
|
+
catch (error) {
|
|
1410
|
+
if (options.failOnParseError === true) {
|
|
1411
|
+
throw new Error(`[sync fail-fast] Cannot strip managed hook entries from ${hookFilePath} — ${error instanceof Error ? error.message : String(error)}. Run \`rm ${hookFilePath}\` and rerun \`npx cclaw-cli sync\`.`);
|
|
1412
|
+
}
|
|
1283
1413
|
return;
|
|
1284
1414
|
}
|
|
1285
|
-
if (parsed === null)
|
|
1286
|
-
return;
|
|
1287
1415
|
const { updated, changed } = stripManagedHookCommands(parsed);
|
|
1288
1416
|
if (!changed)
|
|
1289
1417
|
return;
|
|
@@ -1365,7 +1493,7 @@ export async function uninstallCclaw(projectRoot) {
|
|
|
1365
1493
|
try {
|
|
1366
1494
|
const entries = await fs.readdir(codexSkillsRoot);
|
|
1367
1495
|
for (const entry of entries) {
|
|
1368
|
-
if (/^(?:cclaw-)?cc(?:-(?:next|view|finish|cancel|ops|
|
|
1496
|
+
if (/^(?:cclaw-)?cc(?:-(?:next|view|finish|cancel|ops|idea|brainstorm|scope|design|spec|plan|tdd|review|ship))?$/u.test(entry)) {
|
|
1369
1497
|
await fs.rm(path.join(codexSkillsRoot, entry), { recursive: true, force: true });
|
|
1370
1498
|
}
|
|
1371
1499
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type FlowState } from "../../flow-state.js";
|
|
2
|
+
import { type FlowStage } from "../../types.js";
|
|
3
|
+
import type { AdvanceStageArgs } from "./parsers.js";
|
|
4
|
+
import type { Writable } from "node:stream";
|
|
5
|
+
interface InternalIo {
|
|
6
|
+
stdout: Writable;
|
|
7
|
+
stderr: Writable;
|
|
8
|
+
}
|
|
9
|
+
interface InternalValidationReport {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
stage: FlowStage;
|
|
12
|
+
delegation: {
|
|
13
|
+
satisfied: boolean;
|
|
14
|
+
missing: string[];
|
|
15
|
+
waived: string[];
|
|
16
|
+
missingEvidence: string[];
|
|
17
|
+
missingDispatchProof: string[];
|
|
18
|
+
legacyInferredCompletions: string[];
|
|
19
|
+
corruptEventLines: number[];
|
|
20
|
+
staleWorkers: string[];
|
|
21
|
+
expectedMode: string;
|
|
22
|
+
};
|
|
23
|
+
gates: {
|
|
24
|
+
ok: boolean;
|
|
25
|
+
complete: boolean;
|
|
26
|
+
issues: string[];
|
|
27
|
+
missingRequired: string[];
|
|
28
|
+
missingTriggeredConditional: string[];
|
|
29
|
+
};
|
|
30
|
+
completedStages: {
|
|
31
|
+
ok: boolean;
|
|
32
|
+
issues: string[];
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
export declare function hydrateReviewLoopEvidenceFromArtifact(projectRoot: string, stage: FlowStage, track: FlowState["track"], selectedGateIds: string[], evidenceByGate: Record<string, string>): Promise<void>;
|
|
36
|
+
export declare function buildValidationReport(projectRoot: string, flowState: FlowState, options?: {
|
|
37
|
+
allowBlockedReviewRoute?: boolean;
|
|
38
|
+
}): Promise<InternalValidationReport>;
|
|
39
|
+
interface HarvestLearningsResult {
|
|
40
|
+
ok: boolean;
|
|
41
|
+
markerWritten: boolean;
|
|
42
|
+
parsedEntries: number;
|
|
43
|
+
appendedEntries: number;
|
|
44
|
+
skippedDuplicates: number;
|
|
45
|
+
details: string;
|
|
46
|
+
}
|
|
47
|
+
export declare function withLearningsHarvestMarker(artifactMarkdown: string, appendedEntries: number, skippedDuplicates: number): string;
|
|
48
|
+
export declare function harvestStageLearnings(projectRoot: string, stage: FlowStage, track: FlowState["track"]): Promise<HarvestLearningsResult>;
|
|
49
|
+
export declare function runAdvanceStage(projectRoot: string, args: AdvanceStageArgs, io: InternalIo): Promise<number>;
|
|
50
|
+
export {};
|