cclaw-cli 6.14.3 → 7.0.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.
Files changed (86) hide show
  1. package/README.md +0 -2
  2. package/dist/artifact-linter/brainstorm.js +1 -1
  3. package/dist/artifact-linter/design.js +2 -2
  4. package/dist/artifact-linter/findings-dedup.js +1 -1
  5. package/dist/artifact-linter/plan.js +6 -6
  6. package/dist/artifact-linter/review-army.d.ts +1 -1
  7. package/dist/artifact-linter/review-army.js +1 -1
  8. package/dist/artifact-linter/scope.js +6 -6
  9. package/dist/artifact-linter/shared.d.ts +37 -73
  10. package/dist/artifact-linter/shared.js +30 -37
  11. package/dist/artifact-linter/spec.js +1 -1
  12. package/dist/artifact-linter/tdd.d.ts +20 -33
  13. package/dist/artifact-linter/tdd.js +89 -617
  14. package/dist/artifact-linter.js +11 -32
  15. package/dist/cli.js +1 -1
  16. package/dist/config.js +1 -1
  17. package/dist/constants.js +1 -1
  18. package/dist/content/core-agents.d.ts +8 -26
  19. package/dist/content/core-agents.js +48 -94
  20. package/dist/content/examples.d.ts +1 -1
  21. package/dist/content/examples.js +4 -4
  22. package/dist/content/hooks.js +62 -149
  23. package/dist/content/idea.js +2 -2
  24. package/dist/content/iron-laws.js +1 -1
  25. package/dist/content/node-hooks.js +2 -2
  26. package/dist/content/skills-elicitation.js +2 -2
  27. package/dist/content/skills.d.ts +4 -6
  28. package/dist/content/skills.js +14 -53
  29. package/dist/content/stage-schema.d.ts +3 -3
  30. package/dist/content/stage-schema.js +8 -46
  31. package/dist/content/stages/brainstorm.js +5 -5
  32. package/dist/content/stages/plan.js +2 -2
  33. package/dist/content/stages/review.js +1 -1
  34. package/dist/content/stages/schema-types.d.ts +1 -1
  35. package/dist/content/stages/scope.js +1 -1
  36. package/dist/content/stages/spec.js +2 -2
  37. package/dist/content/stages/tdd.js +43 -108
  38. package/dist/content/start-command.js +3 -3
  39. package/dist/content/subagent-context-skills.js +5 -3
  40. package/dist/content/subagents.js +13 -74
  41. package/dist/content/templates.d.ts +6 -6
  42. package/dist/content/templates.js +23 -24
  43. package/dist/content/utility-skills.d.ts +1 -1
  44. package/dist/content/utility-skills.js +1 -1
  45. package/dist/delegation.d.ts +79 -139
  46. package/dist/delegation.js +83 -215
  47. package/dist/early-loop.js +1 -1
  48. package/dist/flow-state.d.ts +24 -129
  49. package/dist/flow-state.js +5 -30
  50. package/dist/gate-evidence.d.ts +2 -7
  51. package/dist/gate-evidence.js +2 -59
  52. package/dist/harness-adapters.d.ts +1 -1
  53. package/dist/harness-adapters.js +11 -10
  54. package/dist/install.js +24 -459
  55. package/dist/internal/advance-stage/advance.d.ts +5 -5
  56. package/dist/internal/advance-stage/advance.js +9 -24
  57. package/dist/internal/advance-stage/parsers.d.ts +1 -1
  58. package/dist/internal/advance-stage/review-loop.d.ts +1 -1
  59. package/dist/internal/advance-stage/review-loop.js +3 -3
  60. package/dist/internal/advance-stage/start-flow.js +1 -3
  61. package/dist/internal/advance-stage.js +4 -23
  62. package/dist/internal/cohesion-contract-stub.d.ts +8 -13
  63. package/dist/internal/cohesion-contract-stub.js +18 -24
  64. package/dist/internal/flow-state-repair.d.ts +1 -1
  65. package/dist/internal/plan-split-waves.d.ts +44 -7
  66. package/dist/internal/plan-split-waves.js +113 -12
  67. package/dist/internal/wave-status.d.ts +3 -6
  68. package/dist/internal/wave-status.js +5 -27
  69. package/dist/policy.js +1 -1
  70. package/dist/run-persistence.js +10 -44
  71. package/dist/runtime/run-hook.mjs +3 -3
  72. package/dist/track-heuristics.js +1 -1
  73. package/dist/types.d.ts +2 -2
  74. package/package.json +1 -1
  75. package/dist/integration-fanin.d.ts +0 -44
  76. package/dist/integration-fanin.js +0 -180
  77. package/dist/internal/set-checkpoint-mode.d.ts +0 -16
  78. package/dist/internal/set-checkpoint-mode.js +0 -72
  79. package/dist/internal/set-integration-overseer-mode.d.ts +0 -14
  80. package/dist/internal/set-integration-overseer-mode.js +0 -69
  81. package/dist/internal/set-worktree-mode.d.ts +0 -10
  82. package/dist/internal/set-worktree-mode.js +0 -28
  83. package/dist/worktree-manager.d.ts +0 -50
  84. package/dist/worktree-manager.js +0 -136
  85. package/dist/worktree-types.d.ts +0 -36
  86. package/dist/worktree-types.js +0 -6
package/dist/install.js CHANGED
@@ -25,16 +25,16 @@ import { LANGUAGE_RULE_PACK_DIR, LEGACY_LANGUAGE_RULE_PACK_FOLDERS } from "./con
25
25
  import { RESEARCH_PLAYBOOKS } from "./content/research-playbooks.js";
26
26
  import { SUBAGENT_CONTEXT_SKILLS } from "./content/subagent-context-skills.js";
27
27
  import { CCLAW_AGENTS } from "./content/core-agents.js";
28
- import { createInitialFlowState, effectiveWorktreeExecutionMode } from "./flow-state.js";
28
+ import { createInitialFlowState } from "./flow-state.js";
29
29
  import { ensureDir, exists, writeFileSafe } from "./fs-utils.js";
30
- import { ManagedResourceSession, setActiveManagedResourceSession } from "./managed-resources.js";
30
+ import { ManagedResourceSession, readManagedResourceManifest, setActiveManagedResourceSession } from "./managed-resources.js";
31
31
  import { ensureGitignore, removeGitignorePatterns } from "./gitignore.js";
32
32
  import { HARNESS_ADAPTERS, harnessShimFileNames, harnessShimSkillNames, syncHarnessShims, removeCclawFromAgentsMd } from "./harness-adapters.js";
33
33
  import { validateHookDocument } from "./hook-schema.js";
34
34
  import { detectHarnesses } from "./init-detect.js";
35
35
  import { classifyCodexHooksFlag, codexConfigPath, readCodexConfig } from "./codex-feature-flag.js";
36
- import { CorruptFlowStateError, ensureRunSystem, readFlowState, writeFlowState } from "./runs.js";
37
- import { PLAN_SPLIT_DEFAULT_WAVE_SIZE, buildParallelExecutionPlanSection, formatNextParallelWaveSyncHint, mergeParallelWaveDefinitions, parseParallelExecutionPlanWaves, parseWavePlanDirectory, planArtifactLacksV613ParallelMetadata, upsertParallelExecutionPlanSection } from "./internal/plan-split-waves.js";
36
+ import { CorruptFlowStateError, ensureRunSystem } from "./runs.js";
37
+ import { formatNextParallelWaveSyncHint, mergeParallelWaveDefinitions, parseParallelExecutionPlanWaves, parseWavePlanDirectory } from "./internal/plan-split-waves.js";
38
38
  import { FLOW_STAGES } from "./types.js";
39
39
  const OPENCODE_PLUGIN_REL_PATH = ".opencode/plugins/cclaw-plugin.mjs";
40
40
  const CURSOR_RULE_REL_PATH = ".cursor/rules/cclaw-workflow.mdc";
@@ -165,16 +165,12 @@ const DEPRECATED_STATE_FILES = [
165
165
  "context-mode.json",
166
166
  "session-digest.md",
167
167
  "context-warnings.jsonl",
168
- // Runtime Honesty 6.9.0 removed the per-run TDD cycle JSONL: gate evidence
169
- // now reads cycle phase progression directly from the artifact table.
170
168
  "tdd-cycle-log.jsonl"
171
169
  ];
172
- // v6.11.0 (R5): files under `<runtime>/artifacts/` that previous releases
173
- // generated and v6.11.0 removed. `cclaw-cli sync` deletes each so existing
170
+ // Files under `<runtime>/artifacts/` that older releases generated and
171
+ // the current release removes. `cclaw-cli sync` deletes each so existing
174
172
  // installs lose the obsolete sidecar without requiring manual cleanup.
175
173
  const DEPRECATED_ARTIFACT_FILES = [
176
- // v6.10.0 sidecar — replaced in v6.11.0 by phase events in
177
- // delegation-events.jsonl + auto-rendered tables in 06-tdd.md.
178
174
  "06-tdd-slices.jsonl"
179
175
  ];
180
176
  const DEPRECATED_HOOK_FILES = [
@@ -207,10 +203,10 @@ async function resolveGitHooksDir(projectRoot) {
207
203
  return null;
208
204
  }
209
205
  }
210
- // Legacy cleanup: prior versions installed Node-based git pre-commit/pre-push relays
211
- // under .git/hooks/* and a runtime tree at .cclaw/hooks/git/. Runtime Honesty 6.9.0
212
- // removed managed git hooks entirely; the cleanup below stays so existing installs
213
- // shed the leftover files on next sync/uninstall.
206
+ // Older versions installed Node-based git pre-commit/pre-push relays under
207
+ // `.git/hooks/*` and a runtime tree at `.cclaw/hooks/git/`. cclaw no longer
208
+ // manages git hooks; the cleanup below stays so existing installs shed the
209
+ // leftover files on next sync/uninstall.
214
210
  const LEGACY_GIT_HOOK_MANAGED_MARKER = "cclaw-managed-git-hook";
215
211
  const LEGACY_GIT_HOOK_RUNTIME_REL_DIR = `${RUNTIME_ROOT}/hooks/git`;
216
212
  async function cleanupLegacyManagedGitHookRelays(projectRoot) {
@@ -257,10 +253,12 @@ async function writeWavePlansScaffold(projectRoot) {
257
253
  }
258
254
  async function writeSkills(projectRoot, config) {
259
255
  void config;
256
+ const manifest = await readManagedResourceManifest(projectRoot).catch(() => null);
257
+ const packageVersion = manifest?.packageVersion ?? null;
260
258
  const skillTrack = "standard";
261
259
  for (const stage of FLOW_STAGES) {
262
260
  const folder = stageSkillFolder(stage);
263
- await writeFileSafe(runtimePath(projectRoot, "skills", folder, "SKILL.md"), stageSkillMarkdown(stage, skillTrack));
261
+ await writeFileSafe(runtimePath(projectRoot, "skills", folder, "SKILL.md"), stageSkillMarkdown(stage, skillTrack, packageVersion));
264
262
  }
265
263
  // Utility skills (not flow stages)
266
264
  await writeFileSafe(runtimePath(projectRoot, "skills", "learnings", "SKILL.md"), learnSkillMarkdown());
@@ -285,7 +283,6 @@ async function writeSkills(projectRoot, config) {
285
283
  for (const [folderName, markdown] of Object.entries(SUBAGENT_CONTEXT_SKILLS)) {
286
284
  await writeFileSafe(runtimePath(projectRoot, "skills", folderName, "SKILL.md"), markdown);
287
285
  }
288
- // Wave 21: language packs are no longer materialized from config.
289
286
  await fs.rm(runtimePath(projectRoot, ...LANGUAGE_RULE_PACK_DIR), { recursive: true, force: true });
290
287
  for (const legacyFolder of LEGACY_LANGUAGE_RULE_PACK_FOLDERS) {
291
288
  const legacyPath = runtimePath(projectRoot, "skills", legacyFolder);
@@ -735,13 +732,12 @@ async function writeHooks(projectRoot, config) {
735
732
  await writeMergedHookJson(projectRoot, path.join(cursorDir, "hooks.json"), cursorHooksJson());
736
733
  }
737
734
  else if (harness === "codex") {
738
- // Codex CLI v0.114 (Mar 2026) supports lifecycle hooks at
739
- // `.codex/hooks.json`, gated behind the `[features] codex_hooks = true`
740
- // flag in `~/.codex/config.toml`. cclaw always writes the file so
741
- // the moment the flag flips on, the cclaw hooks start firing. See
742
- // `codexHooksJsonWithObservation` for the Bash-only caveat on
743
- // PreToolUse/PostToolUse. If the feature flag is off, hooks remain
744
- // inert until the user enables codex_hooks in ~/.codex/config.toml.
735
+ // Codex CLI lifecycle hooks live at `.codex/hooks.json`, gated by
736
+ // `[features] codex_hooks = true` in `~/.codex/config.toml`. cclaw
737
+ // always writes the file so the moment the flag flips on, cclaw
738
+ // hooks start firing. PreToolUse/PostToolUse remain Bash-only on
739
+ // Codex; if the feature flag is off, hooks stay inert until the
740
+ // user enables `codex_hooks` in `~/.codex/config.toml`.
745
741
  const codexDir = path.join(projectRoot, ".codex");
746
742
  await ensureDir(codexDir);
747
743
  await writeMergedHookJson(projectRoot, path.join(codexDir, "hooks.json"), codexHooksJson());
@@ -784,10 +780,6 @@ async function writeCursorWorkflowRule(projectRoot, harnesses) {
784
780
  }
785
781
  async function syncDisabledHarnessArtifacts(projectRoot, harnesses) {
786
782
  const enabled = new Set(harnesses);
787
- // v0.40.0: `.codex/hooks.json` is back on the managed list now that
788
- // Codex CLI actually consumes it (v0.114+, Mar 2026). Legacy
789
- // `.codex/commands/` cleanup still happens unconditionally from
790
- // `cleanupLegacyCodexSurfaces` inside `syncHarnessShims`.
791
783
  const managedHookFiles = [
792
784
  { harness: "claude", hookPath: path.join(projectRoot, ".claude/hooks/hooks.json") },
793
785
  { harness: "cursor", hookPath: path.join(projectRoot, ".cursor/hooks.json") },
@@ -836,414 +828,6 @@ async function writeState(projectRoot, config, forceReset = false) {
836
828
  const state = createInitialFlowState({ track: "standard" });
837
829
  await writeFileSafe(statePath, `${JSON.stringify(state, null, 2)}\n`, { mode: 0o600 });
838
830
  }
839
- /**
840
- * v6.12.0 — TDD auto-cutover sync. When sync detects a legacy `06-tdd.md`
841
- * (no auto-render markers) carrying observable slice activity (e.g. `S-N`
842
- * referenced ≥3 times in slice-section bodies), insert the v6.11.0 marker
843
- * skeleton + a one-line cutover banner and stamp the highest legacy slice
844
- * id into `flow-state.json::tddCutoverSliceId`. Idempotent: re-running sync
845
- * is byte-stable once markers are present.
846
- *
847
- * Design notes:
848
- * - Best-effort: read failures, missing flow-state, or unparseable JSON
849
- * all short-circuit silently. We never throw inside sync for migration
850
- * bookkeeping.
851
- * - We use `writeFlowState({ allowReset: true })` so we don't trip
852
- * `validateFlowTransition` (the only field we mutate is the new
853
- * additive `tddCutoverSliceId`; transition rules don't apply).
854
- * - The banner mirrors the language in the `## Per-Slice Ritual`
855
- * skill section so a reader of `06-tdd.md` who hasn't seen v6.12.0
856
- * understands the contract change.
857
- */
858
- async function applyTddCutoverIfNeeded(projectRoot) {
859
- const tddArtifactPath = runtimePath(projectRoot, "artifacts", "06-tdd.md");
860
- let existing;
861
- try {
862
- existing = await fs.readFile(tddArtifactPath, "utf8");
863
- }
864
- catch {
865
- return;
866
- }
867
- if (existing.includes("<!-- auto-start: tdd-slice-summary -->")) {
868
- return;
869
- }
870
- const sliceMatches = [...existing.matchAll(/\bS-(\d+)\b/gu)];
871
- if (sliceMatches.length < 3) {
872
- return;
873
- }
874
- let maxSliceNum = 0;
875
- for (const match of sliceMatches) {
876
- const n = Number.parseInt(match[1], 10);
877
- if (Number.isFinite(n) && n > maxSliceNum) {
878
- maxSliceNum = n;
879
- }
880
- }
881
- if (maxSliceNum <= 0) {
882
- return;
883
- }
884
- const cutoverSliceId = `S-${maxSliceNum}`;
885
- const banner = [
886
- `<!-- v6.12.0 cutover: slices S-1..${cutoverSliceId} use legacy per-slice tables.`,
887
- ` New slices (S-${maxSliceNum + 1}+) use phase events + tdd-slices/<id>.md.`,
888
- " Controller MUST NOT add new rows to legacy sections. -->"
889
- ].join("\n");
890
- const slicesIndexBlock = [
891
- "<!-- auto-start: slices-index -->",
892
- "## Slices Index",
893
- "",
894
- "_Auto-rendered from `tdd-slices/S-*.md` once slice-documenter or controller writes per-slice files. Do not edit by hand._",
895
- "<!-- auto-end: slices-index -->"
896
- ].join("\n");
897
- const summaryBlock = [
898
- "<!-- auto-start: tdd-slice-summary -->",
899
- "<!-- auto-end: tdd-slice-summary -->"
900
- ].join("\n");
901
- let nextRaw;
902
- if (existing.startsWith("---\n")) {
903
- const fmEnd = existing.indexOf("\n---", 4);
904
- if (fmEnd >= 0) {
905
- const fmClose = fmEnd + 4;
906
- const head = existing.slice(0, fmClose);
907
- const tail = existing.slice(fmClose);
908
- nextRaw = `${head}\n\n${banner}\n\n${slicesIndexBlock}\n\n${summaryBlock}\n${tail}`;
909
- }
910
- else {
911
- nextRaw = `${banner}\n\n${slicesIndexBlock}\n\n${summaryBlock}\n\n${existing}`;
912
- }
913
- }
914
- else {
915
- nextRaw = `${banner}\n\n${slicesIndexBlock}\n\n${summaryBlock}\n\n${existing}`;
916
- }
917
- await writeFileSafe(tddArtifactPath, nextRaw);
918
- const slicesDir = runtimePath(projectRoot, "artifacts", "tdd-slices");
919
- await ensureDir(slicesDir);
920
- const flowStatePath = runtimePath(projectRoot, "state", "flow-state.json");
921
- let flowStateRaw;
922
- try {
923
- flowStateRaw = await fs.readFile(flowStatePath, "utf8");
924
- }
925
- catch {
926
- return;
927
- }
928
- let parsed;
929
- try {
930
- parsed = JSON.parse(flowStateRaw);
931
- }
932
- catch {
933
- return;
934
- }
935
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
936
- return;
937
- }
938
- const obj = parsed;
939
- if (typeof obj.tddCutoverSliceId === "string" && obj.tddCutoverSliceId.length > 0) {
940
- return;
941
- }
942
- // v6.14.3 — refresh the SHA256 sidecar by writing through
943
- // `writeFlowState`. The previous direct `writeFileSafe` invocation
944
- // left the sidecar stale, so the very next guarded hook on a synced
945
- // legacy project rejected its own `tddCutoverSliceId` stamp.
946
- try {
947
- const state = await readFlowState(projectRoot);
948
- await writeFlowState(projectRoot, { ...state, tddCutoverSliceId: cutoverSliceId }, {
949
- allowReset: true,
950
- writerSubsystem: "sync-v6.12-tdd-cutover-stamp"
951
- });
952
- }
953
- catch {
954
- // Best-effort: corrupt/missing state is handled elsewhere on sync.
955
- }
956
- }
957
- const V613_LEGACY_PLAN_BANNER = "<!-- legacy-continuation: predates v6.13 parallel metadata. New units MAY add dependsOn/claimedPaths/parallelizable; existing units treated as best-effort serial. -->";
958
- /**
959
- * v6.13.0 — when `05-plan.md` lacks parallel-metadata bullets on any
960
- * implementation unit, stamp `flow-state.json::legacyContinuation`, insert
961
- * a banner + managed Parallel Execution Plan stub, and keep behavior idempotent.
962
- */
963
- async function applyPlanLegacyContinuationIfNeeded(projectRoot) {
964
- const planArtifactPath = runtimePath(projectRoot, "artifacts", "05-plan.md");
965
- let existingPlan;
966
- try {
967
- existingPlan = await fs.readFile(planArtifactPath, "utf8");
968
- }
969
- catch {
970
- return;
971
- }
972
- if (!planArtifactLacksV613ParallelMetadata(existingPlan)) {
973
- return;
974
- }
975
- let nextPlan = existingPlan;
976
- if (!nextPlan.includes("legacy-continuation: predates v6.13")) {
977
- if (nextPlan.startsWith("---\n")) {
978
- const fmEnd = nextPlan.indexOf("\n---", 4);
979
- if (fmEnd >= 0) {
980
- const fmClose = fmEnd + 4;
981
- const head = nextPlan.slice(0, fmClose);
982
- const tail = nextPlan.slice(fmClose);
983
- nextPlan = `${head}\n\n${V613_LEGACY_PLAN_BANNER}\n${tail}`;
984
- }
985
- else {
986
- nextPlan = `${V613_LEGACY_PLAN_BANNER}\n\n${nextPlan}`;
987
- }
988
- }
989
- else {
990
- nextPlan = `${V613_LEGACY_PLAN_BANNER}\n\n${nextPlan}`;
991
- }
992
- }
993
- const parallelStub = buildParallelExecutionPlanSection([], PLAN_SPLIT_DEFAULT_WAVE_SIZE);
994
- if (!nextPlan.includes("<!-- parallel-exec-managed-start -->")) {
995
- nextPlan = upsertParallelExecutionPlanSection(nextPlan, parallelStub);
996
- }
997
- if (nextPlan !== existingPlan) {
998
- await writeFileSafe(planArtifactPath, nextPlan);
999
- }
1000
- const flowStatePath = runtimePath(projectRoot, "state", "flow-state.json");
1001
- if (!(await exists(flowStatePath))) {
1002
- return;
1003
- }
1004
- try {
1005
- const state = await readFlowState(projectRoot);
1006
- if (state.legacyContinuation === true) {
1007
- return;
1008
- }
1009
- await writeFlowState(projectRoot, { ...state, legacyContinuation: true }, {
1010
- allowReset: true,
1011
- writerSubsystem: "plan-legacy-continuation-sync"
1012
- });
1013
- }
1014
- catch {
1015
- // Best-effort: corrupt/missing state is handled elsewhere on sync.
1016
- }
1017
- }
1018
- /**
1019
- * v6.14.0 — set stream-style defaults on `cclaw-cli sync` and print a
1020
- * one-line hint when defaults change.
1021
- *
1022
- * v6.14.2 update — flip the legacyContinuation defaults from
1023
- * `global-red`/`always` to `per-slice`/`conditional`. Rationale: hox-shape
1024
- * projects ran into S-17 misroutes precisely because the default
1025
- * preserved the v6.12 wave barrier even after the project itself had
1026
- * moved to stream-mode. Existing flow-state values are NEVER overwritten
1027
- * — operators who want to pin `global-red`/`always` may set them
1028
- * explicitly via `cclaw-cli internal set-checkpoint-mode global-red` and
1029
- * `set-integration-overseer-mode always`.
1030
- *
1031
- * Strategy:
1032
- *
1033
- * - When `legacyContinuation: true` and `tddCheckpointMode` is unset,
1034
- * default to `tddCheckpointMode: "per-slice"` (v6.14.2 — was
1035
- * `global-red` in v6.14.0/v6.14.1).
1036
- * - When `legacyContinuation: true` and `integrationOverseerMode` is
1037
- * unset, default to `integrationOverseerMode: "conditional"` (v6.14.2
1038
- * — was `always` in v6.14.0/v6.14.1).
1039
- * - When `legacyContinuation` is NOT true (new / standard projects) and
1040
- * neither field is set, default to `tddCheckpointMode: "per-slice"`,
1041
- * `integrationOverseerMode: "conditional"`. Also default
1042
- * `worktreeExecutionMode: "worktree-first"` if unset.
1043
- *
1044
- * Returns a one-line hint string (or `null` if nothing changed) so callers
1045
- * can print it through the standard sync hint surface.
1046
- */
1047
- async function applyV614DefaultsIfNeeded(projectRoot) {
1048
- // Defensive read — match `applyTddCutoverIfNeeded`'s pattern (raw +
1049
- // JSON.parse) so corrupt state is left untouched for the downstream
1050
- // fail-fast check in `materializeRuntime` (which expects to see the
1051
- // CorruptFlowStateError surfaced via `ensureRunSystem`). Calling
1052
- // `readFlowState` directly would quarantine the corrupt file and hide
1053
- // the failure from the caller.
1054
- const flowStatePath = runtimePath(projectRoot, "state", "flow-state.json");
1055
- let flowStateRaw;
1056
- try {
1057
- flowStateRaw = await fs.readFile(flowStatePath, "utf8");
1058
- }
1059
- catch {
1060
- return null;
1061
- }
1062
- let parsed;
1063
- try {
1064
- parsed = JSON.parse(flowStateRaw);
1065
- }
1066
- catch {
1067
- return null;
1068
- }
1069
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1070
- return null;
1071
- }
1072
- const obj = parsed;
1073
- const updates = {};
1074
- const summary = [];
1075
- const tddCheckpointModeSet = obj.tddCheckpointMode === "per-slice" || obj.tddCheckpointMode === "global-red";
1076
- const integrationOverseerModeSet = obj.integrationOverseerMode === "conditional" || obj.integrationOverseerMode === "always";
1077
- const worktreeExecutionModeSet = obj.worktreeExecutionMode === "worktree-first" || obj.worktreeExecutionMode === "single-tree";
1078
- const legacyContinuation = obj.legacyContinuation === true;
1079
- if (legacyContinuation) {
1080
- if (!tddCheckpointModeSet) {
1081
- updates.tddCheckpointMode = "per-slice";
1082
- summary.push("tddCheckpointMode=per-slice (legacyContinuation, v6.14.2 default flip)");
1083
- }
1084
- if (!integrationOverseerModeSet) {
1085
- updates.integrationOverseerMode = "conditional";
1086
- summary.push("integrationOverseerMode=conditional (legacyContinuation, v6.14.2 default flip)");
1087
- }
1088
- }
1089
- else {
1090
- if (!tddCheckpointModeSet) {
1091
- updates.tddCheckpointMode = "per-slice";
1092
- summary.push("tddCheckpointMode=per-slice");
1093
- }
1094
- if (!integrationOverseerModeSet) {
1095
- updates.integrationOverseerMode = "conditional";
1096
- summary.push("integrationOverseerMode=conditional");
1097
- }
1098
- if (!worktreeExecutionModeSet) {
1099
- updates.worktreeExecutionMode = "worktree-first";
1100
- summary.push("worktreeExecutionMode=worktree-first");
1101
- }
1102
- }
1103
- if (summary.length === 0) {
1104
- return null;
1105
- }
1106
- // v6.14.3 — refresh the SHA256 sidecar in lockstep so guarded reads
1107
- // (verify-current-state, advance-stage, etc.) don't trip a guard
1108
- // mismatch immediately after `cclaw-cli sync`/`upgrade` writes the
1109
- // v6.14.2 stream-style defaults.
1110
- try {
1111
- const state = await readFlowState(projectRoot);
1112
- await writeFlowState(projectRoot, { ...state, ...updates }, {
1113
- allowReset: true,
1114
- writerSubsystem: "sync-v6.14.2-stream-defaults"
1115
- });
1116
- }
1117
- catch {
1118
- return null;
1119
- }
1120
- return `v6.14.2 stream-style defaults applied: ${summary.join(", ")}. To opt out, run cclaw-cli internal set-checkpoint-mode global-red --reason="..." and/or cclaw-cli internal set-integration-overseer-mode always --reason="...".`;
1121
- }
1122
- /**
1123
- * v6.14.2 — auto-stamp `tddWorktreeCutoverSliceId` for legacyContinuation
1124
- * projects in worktree-first mode that haven't yet recorded a boundary.
1125
- *
1126
- * Detection ("any-metadata" rule): scan the active run's
1127
- * `slice-implementer` rows. The boundary is the highest `S-N` whose
1128
- * rows for the active run lack ALL of `claimToken`, `ownerLaneId`, and
1129
- * `leasedUntil`. When no such slice exists (every slice carries at
1130
- * least one worktree field), fall back to `tddCutoverSliceId` so the
1131
- * v6.12 cutover marker still confers exemption.
1132
- *
1133
- * Idempotent: when the field is already set, returns null without
1134
- * writing. Best-effort: read failures, missing ledger, or unparseable
1135
- * rows all short-circuit silently — the existing flow-state.json is
1136
- * never corrupted.
1137
- *
1138
- * Returns a one-line hint string (or `null` if nothing changed).
1139
- */
1140
- async function applyV6142WorktreeCutoverIfNeeded(projectRoot) {
1141
- const flowStatePath = runtimePath(projectRoot, "state", "flow-state.json");
1142
- let flowStateRaw;
1143
- try {
1144
- flowStateRaw = await fs.readFile(flowStatePath, "utf8");
1145
- }
1146
- catch {
1147
- return null;
1148
- }
1149
- let parsed;
1150
- try {
1151
- parsed = JSON.parse(flowStateRaw);
1152
- }
1153
- catch {
1154
- return null;
1155
- }
1156
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
1157
- return null;
1158
- }
1159
- const obj = parsed;
1160
- if (obj.legacyContinuation !== true)
1161
- return null;
1162
- if (obj.worktreeExecutionMode !== "worktree-first")
1163
- return null;
1164
- if (typeof obj.tddWorktreeCutoverSliceId === "string" &&
1165
- obj.tddWorktreeCutoverSliceId.trim().length > 0) {
1166
- return null;
1167
- }
1168
- const ledgerPath = runtimePath(projectRoot, "state", "delegation-log.json");
1169
- const activeRunId = typeof obj.activeRunId === "string" ? obj.activeRunId : "";
1170
- let ledgerRaw;
1171
- try {
1172
- ledgerRaw = await fs.readFile(ledgerPath, "utf8");
1173
- }
1174
- catch {
1175
- ledgerRaw = "";
1176
- }
1177
- let ledgerParsed = null;
1178
- if (ledgerRaw.length > 0) {
1179
- try {
1180
- ledgerParsed = JSON.parse(ledgerRaw);
1181
- }
1182
- catch {
1183
- ledgerParsed = null;
1184
- }
1185
- }
1186
- const entries = ledgerParsed &&
1187
- typeof ledgerParsed === "object" &&
1188
- !Array.isArray(ledgerParsed) &&
1189
- Array.isArray(ledgerParsed.entries)
1190
- ? ledgerParsed.entries
1191
- : [];
1192
- let boundary = -1;
1193
- for (const entry of entries) {
1194
- if (entry.agent !== "slice-implementer")
1195
- continue;
1196
- if (entry.status !== "completed")
1197
- continue;
1198
- if (typeof entry.sliceId !== "string")
1199
- continue;
1200
- if (activeRunId && entry.runId && entry.runId !== activeRunId)
1201
- continue;
1202
- const tok = typeof entry.claimToken === "string" ? entry.claimToken.trim() : "";
1203
- const lane = typeof entry.ownerLaneId === "string" ? entry.ownerLaneId.trim() : "";
1204
- const lease = typeof entry.leasedUntil === "string" ? entry.leasedUntil.trim() : "";
1205
- if (tok.length > 0 || lane.length > 0 || lease.length > 0)
1206
- continue;
1207
- const m = /^S-(\d+)$/u.exec(entry.sliceId);
1208
- if (!m)
1209
- continue;
1210
- const n = Number.parseInt(m[1], 10);
1211
- if (!Number.isFinite(n))
1212
- continue;
1213
- if (n > boundary)
1214
- boundary = n;
1215
- }
1216
- let stamped = null;
1217
- if (boundary >= 0) {
1218
- stamped = `S-${boundary}`;
1219
- }
1220
- else if (typeof obj.tddCutoverSliceId === "string" &&
1221
- /^S-\d+$/u.test(obj.tddCutoverSliceId)) {
1222
- stamped = obj.tddCutoverSliceId;
1223
- }
1224
- if (!stamped)
1225
- return null;
1226
- // v6.14.3 — go through `writeFlowState` so the SHA256 sidecar
1227
- // (`.cclaw/.flow-state.guard.json`) is refreshed in lockstep with
1228
- // the on-disk flow-state.json. The previous v6.14.2 implementation
1229
- // wrote the field via `writeFileSafe` directly, which left the
1230
- // sidecar pointing at the pre-stamp digest; the next guarded hook
1231
- // (e.g. `cclaw internal verify-current-state`) then failed with
1232
- // `flow-state guard mismatch` and demanded a manual repair.
1233
- try {
1234
- const state = await readFlowState(projectRoot);
1235
- await writeFlowState(projectRoot, { ...state, tddWorktreeCutoverSliceId: stamped }, {
1236
- allowReset: true,
1237
- writerSubsystem: "sync-v6.14.2-worktree-cutover-stamp"
1238
- });
1239
- }
1240
- catch {
1241
- return null;
1242
- }
1243
- return (`v6.14.2 stamped tddWorktreeCutoverSliceId=${stamped}; closed slices ≤ ${stamped} ` +
1244
- "are exempt from worktree-first findings under legacyContinuation. " +
1245
- "Edit .cclaw/state/flow-state.json to override (advisory).");
1246
- }
1247
831
  async function cleanLegacyArtifacts(projectRoot) {
1248
832
  for (const legacyFolder of DEPRECATED_UTILITY_SKILL_FOLDERS) {
1249
833
  await removeBestEffort(runtimePath(projectRoot, "skills", legacyFolder), true);
@@ -1381,9 +965,6 @@ async function maybeLogParallelWaveDispatchHint(projectRoot) {
1381
965
  if (!(await exists(flowPath)))
1382
966
  return;
1383
967
  try {
1384
- const state = await readFlowState(projectRoot);
1385
- if (effectiveWorktreeExecutionMode(state) !== "worktree-first")
1386
- return;
1387
968
  const planPath = runtimePath(projectRoot, "artifacts", "05-plan.md");
1388
969
  if (!(await exists(planPath)))
1389
970
  return;
@@ -1416,18 +997,6 @@ async function materializeRuntime(projectRoot, config, forceStateReset, operatio
1416
997
  writeRulebook(projectRoot)
1417
998
  ]);
1418
999
  await writeState(projectRoot, config, forceStateReset);
1419
- if (operation === "sync" || operation === "upgrade") {
1420
- await applyTddCutoverIfNeeded(projectRoot);
1421
- await applyPlanLegacyContinuationIfNeeded(projectRoot);
1422
- const v614Hint = await applyV614DefaultsIfNeeded(projectRoot);
1423
- if (v614Hint) {
1424
- process.stdout.write(`cclaw: ${v614Hint}\n`);
1425
- }
1426
- const v6142Hint = await applyV6142WorktreeCutoverIfNeeded(projectRoot);
1427
- if (v6142Hint) {
1428
- process.stdout.write(`cclaw: ${v6142Hint}\n`);
1429
- }
1430
- }
1431
1000
  try {
1432
1001
  await ensureRunSystem(projectRoot, { createIfMissing: false });
1433
1002
  }
@@ -1481,7 +1050,6 @@ export async function initCclaw(options) {
1481
1050
  throw new Error("Select at least one harness.");
1482
1051
  }
1483
1052
  const config = createDefaultConfig(options.harnesses, options.track);
1484
- // Wave 21: config is always minimal and harness-only.
1485
1053
  await writeConfig(options.projectRoot, config, { mode: "minimal" });
1486
1054
  // Init should scaffold runtime surfaces but leave flow-state creation to the
1487
1055
  // first managed start-flow invocation.
@@ -1701,14 +1269,11 @@ export async function uninstallCclaw(projectRoot) {
1701
1269
  // directory not present
1702
1270
  }
1703
1271
  }
1704
- // Codex shim location history:
1705
- // - < v0.39.0: `.codex/commands/cc*.md` (never consumed by Codex CLI)
1706
- // - v0.39.0 / v0.39.1: `.agents/skills/cclaw-cc*/SKILL.md`
1707
- // - ≥ v0.40.0: `.agents/skills/cc*/SKILL.md` (matches Codex's `/use cc`
1708
- // prompt verbatim)
1709
- // Remove all three legacy layouts on uninstall so orphans can't linger.
1710
- // We only touch cclaw-owned folder names — other tools share
1711
- // `.agents/skills/` with us.
1272
+ // Codex shims live at `.agents/skills/cc*/SKILL.md`, matching Codex's
1273
+ // `/use cc` prompt verbatim. Older layouts (`.codex/commands/cc*.md` and
1274
+ // `.agents/skills/cclaw-cc*/SKILL.md`) are purged on uninstall so orphans
1275
+ // can't linger. We only touch cclaw-owned folder names — other tools
1276
+ // share `.agents/skills/` with us.
1712
1277
  const codexSkillsRoot = path.join(projectRoot, ".agents/skills");
1713
1278
  try {
1714
1279
  const entries = await fs.readdir(codexSkillsRoot);
@@ -19,7 +19,7 @@ interface InternalValidationReport {
19
19
  corruptEventLines: number[];
20
20
  staleWorkers: string[];
21
21
  expectedMode: string;
22
- /** Wave 24: true when mandatoryAgentsFor returned [] for the run's track / taskClass. */
22
+ /** True when mandatoryAgentsFor returned [] for the run's track / taskClass. */
23
23
  skippedByTrack: boolean;
24
24
  };
25
25
  gates: {
@@ -35,23 +35,23 @@ interface InternalValidationReport {
35
35
  };
36
36
  }
37
37
  /**
38
- * Wave 24 entry point — auto-hydrate evidence for an auto-hydratable
38
+ * entry point — auto-hydrate evidence for an auto-hydratable
39
39
  * gate that the agent already included in --passed but for which they
40
40
  * forgot to provide --evidence-json. Returns silently when no
41
41
  * hydration is possible (no auto-hydratable gate, no artifact, no
42
42
  * envelope, etc.).
43
43
  *
44
- * Wave 25 (v6.1.0) layered `tryAutoHydrateAndSelectReviewLoopGate` on
44
+ * layered `tryAutoHydrateAndSelectReviewLoopGate` on
45
45
  * top of this so the gate is also auto-included in selectedGateIds
46
46
  * when the artifact yields a valid envelope. Together the two helpers
47
- * remove the contradiction the user reported in Wave 24:
47
+ * remove the contradiction:
48
48
  * - "omit this gate from --evidence-json so stage-complete can
49
49
  * auto-hydrate it" → "missing --evidence-json entries for passed
50
50
  * gates: design_diagram_freshness".
51
51
  */
52
52
  export declare function hydrateReviewLoopEvidenceFromArtifact(projectRoot: string, stage: FlowStage, track: FlowState["track"], selectedGateIds: string[], evidenceByGate: Record<string, string>): Promise<void>;
53
53
  /**
54
- * Wave 25 (v6.1.0) — auto-include an auto-hydratable review-loop gate
54
+ * auto-include an auto-hydratable review-loop gate
55
55
  * in `selectedGateIds` when:
56
56
  * - The stage has an auto-hydratable gate registered via
57
57
  * `AUTO_REVIEW_LOOP_GATE_BY_STAGE` (currently `design`).