cclaw-cli 7.7.1 → 8.1.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/README.md +210 -134
- package/dist/artifact-frontmatter.d.ts +51 -0
- package/dist/artifact-frontmatter.js +131 -0
- package/dist/artifact-paths.d.ts +7 -27
- package/dist/artifact-paths.js +20 -249
- package/dist/cancel.d.ts +16 -0
- package/dist/cancel.js +66 -0
- package/dist/cli.d.ts +2 -27
- package/dist/cli.js +90 -508
- package/dist/compound.d.ts +26 -0
- package/dist/compound.js +96 -0
- package/dist/config.d.ts +14 -51
- package/dist/config.js +23 -359
- package/dist/constants.d.ts +11 -18
- package/dist/constants.js +19 -106
- package/dist/content/antipatterns.d.ts +1 -0
- package/dist/content/antipatterns.js +109 -0
- package/dist/content/artifact-templates.d.ts +10 -0
- package/dist/content/artifact-templates.js +550 -0
- package/dist/content/cancel-command.d.ts +2 -2
- package/dist/content/cancel-command.js +25 -17
- package/dist/content/core-agents.d.ts +9 -233
- package/dist/content/core-agents.js +39 -768
- package/dist/content/decision-protocol.d.ts +1 -12
- package/dist/content/decision-protocol.js +27 -20
- package/dist/content/examples.d.ts +8 -42
- package/dist/content/examples.js +293 -425
- package/dist/content/idea-command.d.ts +2 -0
- package/dist/content/idea-command.js +38 -0
- package/dist/content/iron-laws.d.ts +4 -138
- package/dist/content/iron-laws.js +18 -197
- package/dist/content/meta-skill.d.ts +1 -3
- package/dist/content/meta-skill.js +57 -134
- package/dist/content/node-hooks.d.ts +12 -8
- package/dist/content/node-hooks.js +188 -838
- package/dist/content/recovery.d.ts +8 -0
- package/dist/content/recovery.js +179 -0
- package/dist/content/reference-patterns.d.ts +4 -13
- package/dist/content/reference-patterns.js +260 -389
- package/dist/content/research-playbooks.d.ts +8 -8
- package/dist/content/research-playbooks.js +108 -121
- package/dist/content/review-loop.d.ts +6 -192
- package/dist/content/review-loop.js +29 -731
- package/dist/content/skills.d.ts +8 -38
- package/dist/content/skills.js +681 -732
- package/dist/content/specialist-prompts/architect.d.ts +1 -0
- package/dist/content/specialist-prompts/architect.js +225 -0
- package/dist/content/specialist-prompts/brainstormer.d.ts +1 -0
- package/dist/content/specialist-prompts/brainstormer.js +168 -0
- package/dist/content/specialist-prompts/index.d.ts +2 -0
- package/dist/content/specialist-prompts/index.js +14 -0
- package/dist/content/specialist-prompts/planner.d.ts +1 -0
- package/dist/content/specialist-prompts/planner.js +182 -0
- package/dist/content/specialist-prompts/reviewer.d.ts +1 -0
- package/dist/content/specialist-prompts/reviewer.js +193 -0
- package/dist/content/specialist-prompts/security-reviewer.d.ts +1 -0
- package/dist/content/specialist-prompts/security-reviewer.js +133 -0
- package/dist/content/specialist-prompts/slice-builder.d.ts +1 -0
- package/dist/content/specialist-prompts/slice-builder.js +232 -0
- package/dist/content/stage-playbooks.d.ts +8 -0
- package/dist/content/stage-playbooks.js +404 -0
- package/dist/content/start-command.d.ts +2 -12
- package/dist/content/start-command.js +221 -207
- package/dist/flow-state.d.ts +21 -178
- package/dist/flow-state.js +67 -170
- package/dist/fs-utils.d.ts +6 -26
- package/dist/fs-utils.js +29 -162
- package/dist/gitignore.d.ts +2 -1
- package/dist/gitignore.js +51 -34
- package/dist/harness-detect.d.ts +10 -0
- package/dist/harness-detect.js +29 -0
- package/dist/install.d.ts +27 -15
- package/dist/install.js +230 -1342
- package/dist/knowledge-store.d.ts +19 -163
- package/dist/knowledge-store.js +56 -590
- package/dist/logger.d.ts +8 -3
- package/dist/logger.js +13 -4
- package/dist/orchestrator-routing.d.ts +29 -0
- package/dist/orchestrator-routing.js +156 -0
- package/dist/run-persistence.d.ts +7 -118
- package/dist/run-persistence.js +29 -845
- package/dist/runtime/run-hook.entry.d.ts +1 -3
- package/dist/runtime/run-hook.entry.js +19 -4
- package/dist/runtime/run-hook.mjs +13 -1024
- package/dist/types.d.ts +25 -261
- package/dist/types.js +8 -36
- package/package.json +6 -3
- package/dist/artifact-linter/brainstorm.d.ts +0 -2
- package/dist/artifact-linter/brainstorm.js +0 -353
- package/dist/artifact-linter/design.d.ts +0 -18
- package/dist/artifact-linter/design.js +0 -444
- package/dist/artifact-linter/findings-dedup.d.ts +0 -56
- package/dist/artifact-linter/findings-dedup.js +0 -232
- package/dist/artifact-linter/plan.d.ts +0 -2
- package/dist/artifact-linter/plan.js +0 -826
- package/dist/artifact-linter/review-army.d.ts +0 -49
- package/dist/artifact-linter/review-army.js +0 -520
- package/dist/artifact-linter/review.d.ts +0 -2
- package/dist/artifact-linter/review.js +0 -113
- package/dist/artifact-linter/scope.d.ts +0 -2
- package/dist/artifact-linter/scope.js +0 -158
- package/dist/artifact-linter/shared.d.ts +0 -637
- package/dist/artifact-linter/shared.js +0 -2163
- package/dist/artifact-linter/ship.d.ts +0 -2
- package/dist/artifact-linter/ship.js +0 -250
- package/dist/artifact-linter/spec.d.ts +0 -2
- package/dist/artifact-linter/spec.js +0 -176
- package/dist/artifact-linter/tdd.d.ts +0 -118
- package/dist/artifact-linter/tdd.js +0 -1404
- package/dist/artifact-linter.d.ts +0 -15
- package/dist/artifact-linter.js +0 -517
- package/dist/codex-feature-flag.d.ts +0 -58
- package/dist/codex-feature-flag.js +0 -193
- package/dist/content/closeout-guidance.d.ts +0 -14
- package/dist/content/closeout-guidance.js +0 -44
- package/dist/content/diff-command.d.ts +0 -1
- package/dist/content/diff-command.js +0 -43
- package/dist/content/harness-doc.d.ts +0 -1
- package/dist/content/harness-doc.js +0 -65
- package/dist/content/hook-events.d.ts +0 -9
- package/dist/content/hook-events.js +0 -23
- package/dist/content/hook-manifest.d.ts +0 -81
- package/dist/content/hook-manifest.js +0 -156
- package/dist/content/hooks.d.ts +0 -11
- package/dist/content/hooks.js +0 -1972
- package/dist/content/idea.d.ts +0 -60
- package/dist/content/idea.js +0 -416
- package/dist/content/language-policy.d.ts +0 -2
- package/dist/content/language-policy.js +0 -13
- package/dist/content/learnings.d.ts +0 -6
- package/dist/content/learnings.js +0 -141
- package/dist/content/observe.d.ts +0 -19
- package/dist/content/observe.js +0 -86
- package/dist/content/opencode-plugin.d.ts +0 -1
- package/dist/content/opencode-plugin.js +0 -635
- package/dist/content/review-prompts.d.ts +0 -1
- package/dist/content/review-prompts.js +0 -104
- package/dist/content/runtime-shared-snippets.d.ts +0 -8
- package/dist/content/runtime-shared-snippets.js +0 -80
- package/dist/content/session-hooks.d.ts +0 -7
- package/dist/content/session-hooks.js +0 -107
- package/dist/content/skills-elicitation.d.ts +0 -1
- package/dist/content/skills-elicitation.js +0 -167
- package/dist/content/stage-command.d.ts +0 -2
- package/dist/content/stage-command.js +0 -17
- package/dist/content/stage-schema.d.ts +0 -117
- package/dist/content/stage-schema.js +0 -955
- package/dist/content/stages/_lint-metadata/index.d.ts +0 -2
- package/dist/content/stages/_lint-metadata/index.js +0 -97
- package/dist/content/stages/brainstorm.d.ts +0 -2
- package/dist/content/stages/brainstorm.js +0 -184
- package/dist/content/stages/design.d.ts +0 -2
- package/dist/content/stages/design.js +0 -288
- package/dist/content/stages/index.d.ts +0 -8
- package/dist/content/stages/index.js +0 -11
- package/dist/content/stages/plan.d.ts +0 -2
- package/dist/content/stages/plan.js +0 -191
- package/dist/content/stages/review.d.ts +0 -2
- package/dist/content/stages/review.js +0 -240
- package/dist/content/stages/schema-types.d.ts +0 -203
- package/dist/content/stages/schema-types.js +0 -1
- package/dist/content/stages/scope.d.ts +0 -2
- package/dist/content/stages/scope.js +0 -254
- package/dist/content/stages/ship.d.ts +0 -2
- package/dist/content/stages/ship.js +0 -159
- package/dist/content/stages/spec.d.ts +0 -2
- package/dist/content/stages/spec.js +0 -170
- package/dist/content/stages/tdd.d.ts +0 -4
- package/dist/content/stages/tdd.js +0 -273
- package/dist/content/state-contracts.d.ts +0 -1
- package/dist/content/state-contracts.js +0 -63
- package/dist/content/status-command.d.ts +0 -4
- package/dist/content/status-command.js +0 -109
- package/dist/content/subagent-context-skills.d.ts +0 -4
- package/dist/content/subagent-context-skills.js +0 -279
- package/dist/content/subagents.d.ts +0 -3
- package/dist/content/subagents.js +0 -997
- package/dist/content/templates.d.ts +0 -26
- package/dist/content/templates.js +0 -1692
- package/dist/content/track-render-context.d.ts +0 -18
- package/dist/content/track-render-context.js +0 -53
- package/dist/content/tree-command.d.ts +0 -1
- package/dist/content/tree-command.js +0 -64
- package/dist/content/utility-skills.d.ts +0 -30
- package/dist/content/utility-skills.js +0 -160
- package/dist/content/view-command.d.ts +0 -2
- package/dist/content/view-command.js +0 -92
- package/dist/delegation.d.ts +0 -649
- package/dist/delegation.js +0 -1539
- package/dist/early-loop.d.ts +0 -70
- package/dist/early-loop.js +0 -302
- package/dist/execution-topology.d.ts +0 -44
- package/dist/execution-topology.js +0 -95
- package/dist/gate-evidence.d.ts +0 -85
- package/dist/gate-evidence.js +0 -631
- package/dist/harness-adapters.d.ts +0 -151
- package/dist/harness-adapters.js +0 -756
- package/dist/harness-selection.d.ts +0 -31
- package/dist/harness-selection.js +0 -214
- package/dist/hook-schema.d.ts +0 -6
- package/dist/hook-schema.js +0 -114
- package/dist/hook-schemas/claude-hooks.v1.json +0 -10
- package/dist/hook-schemas/codex-hooks.v1.json +0 -10
- package/dist/hook-schemas/cursor-hooks.v1.json +0 -13
- package/dist/init-detect.d.ts +0 -2
- package/dist/init-detect.js +0 -50
- package/dist/internal/advance-stage/advance.d.ts +0 -89
- package/dist/internal/advance-stage/advance.js +0 -655
- package/dist/internal/advance-stage/cancel-run.d.ts +0 -8
- package/dist/internal/advance-stage/cancel-run.js +0 -19
- package/dist/internal/advance-stage/flow-state-coercion.d.ts +0 -3
- package/dist/internal/advance-stage/flow-state-coercion.js +0 -81
- package/dist/internal/advance-stage/helpers.d.ts +0 -14
- package/dist/internal/advance-stage/helpers.js +0 -145
- package/dist/internal/advance-stage/hook.d.ts +0 -8
- package/dist/internal/advance-stage/hook.js +0 -40
- package/dist/internal/advance-stage/parsers.d.ts +0 -72
- package/dist/internal/advance-stage/parsers.js +0 -357
- package/dist/internal/advance-stage/proactive-delegation-trace.d.ts +0 -24
- package/dist/internal/advance-stage/proactive-delegation-trace.js +0 -56
- package/dist/internal/advance-stage/review-loop.d.ts +0 -16
- package/dist/internal/advance-stage/review-loop.js +0 -199
- package/dist/internal/advance-stage/rewind.d.ts +0 -14
- package/dist/internal/advance-stage/rewind.js +0 -108
- package/dist/internal/advance-stage/start-flow.d.ts +0 -13
- package/dist/internal/advance-stage/start-flow.js +0 -241
- package/dist/internal/advance-stage/verify.d.ts +0 -21
- package/dist/internal/advance-stage/verify.js +0 -185
- package/dist/internal/advance-stage.d.ts +0 -7
- package/dist/internal/advance-stage.js +0 -138
- package/dist/internal/cohesion-contract-stub.d.ts +0 -24
- package/dist/internal/cohesion-contract-stub.js +0 -148
- package/dist/internal/compound-readiness.d.ts +0 -23
- package/dist/internal/compound-readiness.js +0 -102
- package/dist/internal/detect-public-api-changes.d.ts +0 -5
- package/dist/internal/detect-public-api-changes.js +0 -45
- package/dist/internal/detect-supply-chain-changes.d.ts +0 -6
- package/dist/internal/detect-supply-chain-changes.js +0 -138
- package/dist/internal/early-loop-status.d.ts +0 -7
- package/dist/internal/early-loop-status.js +0 -93
- package/dist/internal/envelope-validate.d.ts +0 -7
- package/dist/internal/envelope-validate.js +0 -66
- package/dist/internal/flow-state-repair.d.ts +0 -20
- package/dist/internal/flow-state-repair.js +0 -104
- package/dist/internal/plan-split-waves.d.ts +0 -190
- package/dist/internal/plan-split-waves.js +0 -764
- package/dist/internal/runtime-integrity.d.ts +0 -7
- package/dist/internal/runtime-integrity.js +0 -268
- package/dist/internal/slice-commit.d.ts +0 -7
- package/dist/internal/slice-commit.js +0 -619
- package/dist/internal/tdd-loop-status.d.ts +0 -14
- package/dist/internal/tdd-loop-status.js +0 -68
- package/dist/internal/tdd-red-evidence.d.ts +0 -7
- package/dist/internal/tdd-red-evidence.js +0 -153
- package/dist/internal/waiver-grant.d.ts +0 -62
- package/dist/internal/waiver-grant.js +0 -294
- package/dist/internal/wave-status.d.ts +0 -74
- package/dist/internal/wave-status.js +0 -506
- package/dist/managed-resources.d.ts +0 -53
- package/dist/managed-resources.js +0 -313
- package/dist/policy.d.ts +0 -10
- package/dist/policy.js +0 -167
- package/dist/retro-gate.d.ts +0 -9
- package/dist/retro-gate.js +0 -47
- package/dist/run-archive.d.ts +0 -61
- package/dist/run-archive.js +0 -391
- package/dist/runs.d.ts +0 -2
- package/dist/runs.js +0 -2
- package/dist/stack-detection.d.ts +0 -116
- package/dist/stack-detection.js +0 -489
- package/dist/streaming/event-stream.d.ts +0 -31
- package/dist/streaming/event-stream.js +0 -114
- package/dist/tdd-cycle.d.ts +0 -107
- package/dist/tdd-cycle.js +0 -289
- package/dist/tdd-verification-evidence.d.ts +0 -17
- package/dist/tdd-verification-evidence.js +0 -122
- package/dist/track-heuristics.d.ts +0 -27
- package/dist/track-heuristics.js +0 -154
- package/dist/util/slice-id.d.ts +0 -58
- package/dist/util/slice-id.js +0 -89
- package/dist/worktree-manager.d.ts +0 -20
- package/dist/worktree-manager.js +0 -108
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { runEnvelopeValidateCommand } from "./envelope-validate.js";
|
|
2
|
-
import { runTddRedEvidenceCommand } from "./tdd-red-evidence.js";
|
|
3
|
-
import { runTddLoopStatusCommand } from "./tdd-loop-status.js";
|
|
4
|
-
import { runEarlyLoopStatusCommand } from "./early-loop-status.js";
|
|
5
|
-
import { runCompoundReadinessCommand } from "./compound-readiness.js";
|
|
6
|
-
import { runRuntimeIntegrityCommand } from "./runtime-integrity.js";
|
|
7
|
-
import { runAdvanceStage } from "./advance-stage/advance.js";
|
|
8
|
-
import { runStartFlow } from "./advance-stage/start-flow.js";
|
|
9
|
-
import { runCancelRun } from "./advance-stage/cancel-run.js";
|
|
10
|
-
import { runRewind } from "./advance-stage/rewind.js";
|
|
11
|
-
import { runVerifyFlowStateDiff, runVerifyCurrentState } from "./advance-stage/verify.js";
|
|
12
|
-
import { runHookCommand } from "./advance-stage/hook.js";
|
|
13
|
-
import { parseAdvanceStageArgs, parseCancelRunArgs, parseHookArgs, parseRewindArgs, parseStartFlowArgs, parseVerifyCurrentStateArgs, parseVerifyFlowStateDiffArgs } from "./advance-stage/parsers.js";
|
|
14
|
-
import { parseFlowStateRepairArgs, runFlowStateRepair } from "./flow-state-repair.js";
|
|
15
|
-
import { parseWaiverGrantArgs, runWaiverGrant } from "./waiver-grant.js";
|
|
16
|
-
import { FlowStateGuardMismatchError, verifyFlowStateGuard } from "../run-persistence.js";
|
|
17
|
-
import { DelegationTimestampError, DispatchCapError, DispatchClaimedPathProtectedError, DispatchDuplicateError, DispatchOverlapError, SliceAlreadyClosedError } from "../delegation.js";
|
|
18
|
-
import { parsePlanSplitWavesArgs, runPlanSplitWaves } from "./plan-split-waves.js";
|
|
19
|
-
import { runWaveStatusCommand } from "./wave-status.js";
|
|
20
|
-
import { runCohesionContractCommand } from "./cohesion-contract-stub.js";
|
|
21
|
-
import { runSliceCommitCommand } from "./slice-commit.js";
|
|
22
|
-
/**
|
|
23
|
-
* Subcommands that mutate or consult flow-state.json via the CLI runtime.
|
|
24
|
-
* They all require the sha256 sidecar to match before continuing so a
|
|
25
|
-
* manual edit hard-blocks with exit code 2 (same contract as the inline
|
|
26
|
-
* hook checks).
|
|
27
|
-
*/
|
|
28
|
-
const GUARD_ENFORCED_SUBCOMMANDS = new Set([
|
|
29
|
-
"advance-stage",
|
|
30
|
-
"start-flow",
|
|
31
|
-
"cancel-run",
|
|
32
|
-
"rewind",
|
|
33
|
-
"verify-flow-state-diff",
|
|
34
|
-
"verify-current-state"
|
|
35
|
-
]);
|
|
36
|
-
export async function runInternalCommand(projectRoot, argv, io) {
|
|
37
|
-
const [subcommand, ...tokens] = argv;
|
|
38
|
-
if (!subcommand) {
|
|
39
|
-
io.stderr.write("cclaw internal requires a subcommand: advance-stage | start-flow | cancel-run | rewind | verify-flow-state-diff | verify-current-state | envelope-validate | tdd-red-evidence | tdd-loop-status | early-loop-status | compound-readiness | runtime-integrity | hook | slice-commit | flow-state-repair | waiver-grant | plan-split-waves | wave-status | cohesion-contract\n");
|
|
40
|
-
return 1;
|
|
41
|
-
}
|
|
42
|
-
try {
|
|
43
|
-
if (GUARD_ENFORCED_SUBCOMMANDS.has(subcommand)) {
|
|
44
|
-
await verifyFlowStateGuard(projectRoot);
|
|
45
|
-
}
|
|
46
|
-
if (subcommand === "advance-stage") {
|
|
47
|
-
return await runAdvanceStage(projectRoot, parseAdvanceStageArgs(tokens), io);
|
|
48
|
-
}
|
|
49
|
-
if (subcommand === "start-flow") {
|
|
50
|
-
return await runStartFlow(projectRoot, parseStartFlowArgs(tokens), io);
|
|
51
|
-
}
|
|
52
|
-
if (subcommand === "cancel-run") {
|
|
53
|
-
return await runCancelRun(projectRoot, parseCancelRunArgs(tokens), io);
|
|
54
|
-
}
|
|
55
|
-
if (subcommand === "rewind") {
|
|
56
|
-
return await runRewind(projectRoot, parseRewindArgs(tokens), io);
|
|
57
|
-
}
|
|
58
|
-
if (subcommand === "verify-flow-state-diff") {
|
|
59
|
-
return await runVerifyFlowStateDiff(projectRoot, parseVerifyFlowStateDiffArgs(tokens), io);
|
|
60
|
-
}
|
|
61
|
-
if (subcommand === "verify-current-state") {
|
|
62
|
-
return await runVerifyCurrentState(projectRoot, parseVerifyCurrentStateArgs(tokens), io);
|
|
63
|
-
}
|
|
64
|
-
if (subcommand === "envelope-validate") {
|
|
65
|
-
return await runEnvelopeValidateCommand(projectRoot, tokens, io);
|
|
66
|
-
}
|
|
67
|
-
if (subcommand === "tdd-red-evidence") {
|
|
68
|
-
return await runTddRedEvidenceCommand(projectRoot, tokens, io);
|
|
69
|
-
}
|
|
70
|
-
if (subcommand === "tdd-loop-status") {
|
|
71
|
-
return await runTddLoopStatusCommand(projectRoot, tokens, io);
|
|
72
|
-
}
|
|
73
|
-
if (subcommand === "early-loop-status") {
|
|
74
|
-
return await runEarlyLoopStatusCommand(projectRoot, tokens, io);
|
|
75
|
-
}
|
|
76
|
-
if (subcommand === "compound-readiness") {
|
|
77
|
-
return await runCompoundReadinessCommand(projectRoot, tokens, io);
|
|
78
|
-
}
|
|
79
|
-
if (subcommand === "runtime-integrity") {
|
|
80
|
-
return await runRuntimeIntegrityCommand(projectRoot, tokens, io);
|
|
81
|
-
}
|
|
82
|
-
if (subcommand === "hook") {
|
|
83
|
-
return await runHookCommand(projectRoot, parseHookArgs(tokens), io);
|
|
84
|
-
}
|
|
85
|
-
if (subcommand === "slice-commit") {
|
|
86
|
-
return await runSliceCommitCommand(projectRoot, tokens, io);
|
|
87
|
-
}
|
|
88
|
-
if (subcommand === "flow-state-repair") {
|
|
89
|
-
return await runFlowStateRepair(projectRoot, parseFlowStateRepairArgs(tokens), io);
|
|
90
|
-
}
|
|
91
|
-
if (subcommand === "waiver-grant") {
|
|
92
|
-
return await runWaiverGrant(projectRoot, parseWaiverGrantArgs(tokens), io);
|
|
93
|
-
}
|
|
94
|
-
if (subcommand === "plan-split-waves") {
|
|
95
|
-
return await runPlanSplitWaves(projectRoot, parsePlanSplitWavesArgs(tokens), io);
|
|
96
|
-
}
|
|
97
|
-
if (subcommand === "wave-status") {
|
|
98
|
-
return await runWaveStatusCommand(projectRoot, tokens, io);
|
|
99
|
-
}
|
|
100
|
-
if (subcommand === "cohesion-contract") {
|
|
101
|
-
return await runCohesionContractCommand(projectRoot, tokens, io);
|
|
102
|
-
}
|
|
103
|
-
io.stderr.write(`Unknown internal subcommand: ${subcommand}. Expected advance-stage | start-flow | cancel-run | rewind | verify-flow-state-diff | verify-current-state | envelope-validate | tdd-red-evidence | tdd-loop-status | early-loop-status | compound-readiness | runtime-integrity | hook | slice-commit | flow-state-repair | waiver-grant | plan-split-waves | wave-status | cohesion-contract\n`);
|
|
104
|
-
return 1;
|
|
105
|
-
}
|
|
106
|
-
catch (err) {
|
|
107
|
-
if (err instanceof FlowStateGuardMismatchError) {
|
|
108
|
-
io.stderr.write(`cclaw internal ${subcommand}: ${err.message}\n`);
|
|
109
|
-
return 2;
|
|
110
|
-
}
|
|
111
|
-
if (err instanceof DelegationTimestampError) {
|
|
112
|
-
io.stderr.write(`error: delegation_timestamp_non_monotonic — ${err.field}: ${err.actual} < ${err.priorBound}\n`);
|
|
113
|
-
return 2;
|
|
114
|
-
}
|
|
115
|
-
if (err instanceof DispatchDuplicateError) {
|
|
116
|
-
io.stderr.write(`error: dispatch_duplicate — ${err.message}\n`);
|
|
117
|
-
return 2;
|
|
118
|
-
}
|
|
119
|
-
if (err instanceof DispatchOverlapError) {
|
|
120
|
-
io.stderr.write(`error: dispatch_overlap — ${err.message}\n`);
|
|
121
|
-
return 2;
|
|
122
|
-
}
|
|
123
|
-
if (err instanceof DispatchClaimedPathProtectedError) {
|
|
124
|
-
io.stderr.write(`error: dispatch_claimed_path_protected — ${err.message}\n`);
|
|
125
|
-
return 2;
|
|
126
|
-
}
|
|
127
|
-
if (err instanceof SliceAlreadyClosedError) {
|
|
128
|
-
io.stderr.write(`error: slice_already_closed — ${err.message}\n`);
|
|
129
|
-
return 2;
|
|
130
|
-
}
|
|
131
|
-
if (err instanceof DispatchCapError) {
|
|
132
|
-
io.stderr.write(`error: dispatch_cap — ${err.message}\n`);
|
|
133
|
-
return 2;
|
|
134
|
-
}
|
|
135
|
-
io.stderr.write(`cclaw internal ${subcommand} failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
136
|
-
return 1;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { Writable } from "node:stream";
|
|
2
|
-
interface InternalIo {
|
|
3
|
-
stdout: Writable;
|
|
4
|
-
stderr: Writable;
|
|
5
|
-
}
|
|
6
|
-
export interface CohesionContractArgs {
|
|
7
|
-
stub: boolean;
|
|
8
|
-
force: boolean;
|
|
9
|
-
reason: string | null;
|
|
10
|
-
}
|
|
11
|
-
export declare function parseCohesionContractArgs(tokens: string[]): CohesionContractArgs | null;
|
|
12
|
-
/**
|
|
13
|
-
* Scaffold a minimal cohesion-contract pair (`cohesion-contract.{md,json}`)
|
|
14
|
-
* so authors have a starting point when a multi-slice wave needs cross-slice
|
|
15
|
-
* cohesion documentation. The stub seeds `sharedTypes`, `touchpoints`, and
|
|
16
|
-
* `slices` from the active run delegation ledger and carries
|
|
17
|
-
* `status.verdict: "scaffold"` so reviewers know to fill in the real content
|
|
18
|
-
* before treating it as authoritative.
|
|
19
|
-
*
|
|
20
|
-
* Refuses to overwrite an existing contract unless `--force` is passed; the
|
|
21
|
-
* existing file is treated as authored work.
|
|
22
|
-
*/
|
|
23
|
-
export declare function runCohesionContractCommand(projectRoot: string, tokens: string[], io: InternalIo): Promise<number>;
|
|
24
|
-
export {};
|
|
@@ -1,148 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { RUNTIME_ROOT } from "../constants.js";
|
|
4
|
-
import { writeFileSafe } from "../fs-utils.js";
|
|
5
|
-
import { readDelegationLedger, isParallelTddSliceWorker } from "../delegation.js";
|
|
6
|
-
import { compareSliceIds } from "../util/slice-id.js";
|
|
7
|
-
export function parseCohesionContractArgs(tokens) {
|
|
8
|
-
const args = { stub: false, force: false, reason: null };
|
|
9
|
-
for (const token of tokens) {
|
|
10
|
-
if (token === "--stub") {
|
|
11
|
-
args.stub = true;
|
|
12
|
-
continue;
|
|
13
|
-
}
|
|
14
|
-
if (token === "--force") {
|
|
15
|
-
args.force = true;
|
|
16
|
-
continue;
|
|
17
|
-
}
|
|
18
|
-
if (token.startsWith("--reason=")) {
|
|
19
|
-
const raw = token.slice("--reason=".length).trim();
|
|
20
|
-
if (raw.length > 0)
|
|
21
|
-
args.reason = raw;
|
|
22
|
-
continue;
|
|
23
|
-
}
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
if (!args.stub)
|
|
27
|
-
return null;
|
|
28
|
-
return args;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Scaffold a minimal cohesion-contract pair (`cohesion-contract.{md,json}`)
|
|
32
|
-
* so authors have a starting point when a multi-slice wave needs cross-slice
|
|
33
|
-
* cohesion documentation. The stub seeds `sharedTypes`, `touchpoints`, and
|
|
34
|
-
* `slices` from the active run delegation ledger and carries
|
|
35
|
-
* `status.verdict: "scaffold"` so reviewers know to fill in the real content
|
|
36
|
-
* before treating it as authoritative.
|
|
37
|
-
*
|
|
38
|
-
* Refuses to overwrite an existing contract unless `--force` is passed; the
|
|
39
|
-
* existing file is treated as authored work.
|
|
40
|
-
*/
|
|
41
|
-
export async function runCohesionContractCommand(projectRoot, tokens, io) {
|
|
42
|
-
const parsed = parseCohesionContractArgs(tokens);
|
|
43
|
-
if (!parsed) {
|
|
44
|
-
io.stderr.write("cclaw internal cohesion-contract: usage: --stub [--force] [--reason=\"<short>\"]\n");
|
|
45
|
-
return 1;
|
|
46
|
-
}
|
|
47
|
-
const artifactsDir = path.join(projectRoot, RUNTIME_ROOT, "artifacts");
|
|
48
|
-
const mdPath = path.join(artifactsDir, "cohesion-contract.md");
|
|
49
|
-
const jsonPath = path.join(artifactsDir, "cohesion-contract.json");
|
|
50
|
-
if (!parsed.force) {
|
|
51
|
-
const mdExists = await fileExists(mdPath);
|
|
52
|
-
const jsonExists = await fileExists(jsonPath);
|
|
53
|
-
if (mdExists || jsonExists) {
|
|
54
|
-
io.stderr.write("cclaw internal cohesion-contract: existing cohesion-contract.{md,json} present; pass --force to overwrite.\n");
|
|
55
|
-
return 1;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
const ledger = await readDelegationLedger(projectRoot).catch(() => null);
|
|
59
|
-
const sliceIds = collectSliceIds(ledger?.entries ?? []);
|
|
60
|
-
const reasonNote = parsed.reason
|
|
61
|
-
? `Reason: ${parsed.reason}`
|
|
62
|
-
: "Reason: scaffold for a multi-slice wave that needs cohesion documentation.";
|
|
63
|
-
const md = [
|
|
64
|
-
"# Cohesion Contract",
|
|
65
|
-
"",
|
|
66
|
-
"_Scaffold generated by `cclaw-cli internal cohesion-contract --stub`._",
|
|
67
|
-
"_Status: `scaffold` — replace placeholders with real cross-slice data before review._",
|
|
68
|
-
"",
|
|
69
|
-
`${reasonNote}`,
|
|
70
|
-
"",
|
|
71
|
-
"## Shared Types & Interfaces",
|
|
72
|
-
"| Symbol | Path | Signature | Owner slice |",
|
|
73
|
-
"|---|---|---|---|",
|
|
74
|
-
"| (none recorded) | (none) | (none) | (n/a) |",
|
|
75
|
-
"",
|
|
76
|
-
"## Naming Conventions",
|
|
77
|
-
"- Stub: per-slice modules continue to follow the existing repo conventions.",
|
|
78
|
-
"",
|
|
79
|
-
"## Invariants",
|
|
80
|
-
"- Stub: no cross-slice invariants asserted; treat each slice as independent until upgraded.",
|
|
81
|
-
"",
|
|
82
|
-
"## Integration Touchpoints",
|
|
83
|
-
"| From slice | To slice | Surface | Integration test name |",
|
|
84
|
-
"|---|---|---|---|",
|
|
85
|
-
"| (none recorded) | (n/a) | (n/a) | (n/a) |",
|
|
86
|
-
"",
|
|
87
|
-
"## Behavior Specifications per Slice",
|
|
88
|
-
sliceIds.length === 0
|
|
89
|
-
? "- (none recorded)"
|
|
90
|
-
: sliceIds
|
|
91
|
-
.map((sid) => `### Slice ${sid}\n- Behavior: see \`tdd-slices/${sid}.md\` (if present).`)
|
|
92
|
-
.join("\n\n"),
|
|
93
|
-
"",
|
|
94
|
-
"## Status",
|
|
95
|
-
"| Slice | Implemented | Tests pass | Cohesion verified |",
|
|
96
|
-
"|---|---|---|---|",
|
|
97
|
-
sliceIds.length === 0
|
|
98
|
-
? "| (none) | n/a | n/a | n/a |"
|
|
99
|
-
: sliceIds.map((sid) => `| ${sid} | yes | yes | advisory |`).join("\n"),
|
|
100
|
-
""
|
|
101
|
-
].join("\n");
|
|
102
|
-
const jsonStub = {
|
|
103
|
-
version: 1,
|
|
104
|
-
sharedTypes: [],
|
|
105
|
-
touchpoints: [],
|
|
106
|
-
slices: sliceIds.map((sid) => ({
|
|
107
|
-
sliceId: sid,
|
|
108
|
-
description: `Scaffold entry for ${sid}; replace with real behavior summary.`,
|
|
109
|
-
implemented: true,
|
|
110
|
-
testsPass: true,
|
|
111
|
-
cohesionVerified: false
|
|
112
|
-
})),
|
|
113
|
-
status: {
|
|
114
|
-
verdict: "scaffold",
|
|
115
|
-
generatedBy: "cclaw-cli internal cohesion-contract --stub",
|
|
116
|
-
reason: parsed.reason ?? "scaffold for a multi-slice wave"
|
|
117
|
-
}
|
|
118
|
-
};
|
|
119
|
-
await writeFileSafe(mdPath, md);
|
|
120
|
-
await writeFileSafe(jsonPath, `${JSON.stringify(jsonStub, null, 2)}\n`);
|
|
121
|
-
io.stdout.write(`cclaw: cohesion-contract scaffold written (${sliceIds.length} slice(s) referenced). ` +
|
|
122
|
-
"Status: scaffold — replace placeholders with real cross-slice data.\n");
|
|
123
|
-
return 0;
|
|
124
|
-
}
|
|
125
|
-
async function fileExists(filePath) {
|
|
126
|
-
try {
|
|
127
|
-
await fs.access(filePath);
|
|
128
|
-
return true;
|
|
129
|
-
}
|
|
130
|
-
catch {
|
|
131
|
-
return false;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
function collectSliceIds(entries) {
|
|
135
|
-
const set = new Set();
|
|
136
|
-
for (const entry of entries) {
|
|
137
|
-
if (!isParallelTddSliceWorker(entry.agent ?? ""))
|
|
138
|
-
continue;
|
|
139
|
-
if (entry.status !== "completed")
|
|
140
|
-
continue;
|
|
141
|
-
if (typeof entry.sliceId !== "string")
|
|
142
|
-
continue;
|
|
143
|
-
if (entry.sliceId.length === 0)
|
|
144
|
-
continue;
|
|
145
|
-
set.add(entry.sliceId);
|
|
146
|
-
}
|
|
147
|
-
return [...set].sort(compareSliceIds);
|
|
148
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { Writable } from "node:stream";
|
|
2
|
-
import { type CompoundReadiness } from "../knowledge-store.js";
|
|
3
|
-
interface InternalIo {
|
|
4
|
-
stdout: Writable;
|
|
5
|
-
stderr: Writable;
|
|
6
|
-
}
|
|
7
|
-
/**
|
|
8
|
-
* Count archived runs as sub-directories under `.cclaw/archive/`. Missing
|
|
9
|
-
* dir / ENOENT is interpreted as zero — callers should NOT conflate
|
|
10
|
-
* that with "unknown" (undefined); we only return undefined on
|
|
11
|
-
* unexpected errors so the caller can choose to skip the relaxation
|
|
12
|
-
* rather than guess a number.
|
|
13
|
-
*/
|
|
14
|
-
export declare function countArchivedRunsSafely(projectRoot: string): Promise<number | undefined>;
|
|
15
|
-
/**
|
|
16
|
-
* Compact one-liner for bootstrap surfaces.
|
|
17
|
-
*
|
|
18
|
-
* Example: `Compound readiness: clusters=12, ready=2 (critical=1)`.
|
|
19
|
-
* When `ready === 0`, emit `Compound readiness: no candidates`.
|
|
20
|
-
*/
|
|
21
|
-
export declare function formatCompoundReadinessLine(status: CompoundReadiness): string;
|
|
22
|
-
export declare function runCompoundReadinessCommand(projectRoot: string, argv: string[], io: InternalIo): Promise<number>;
|
|
23
|
-
export {};
|
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { RUNTIME_ROOT } from "../constants.js";
|
|
4
|
-
import { writeFileSafe } from "../fs-utils.js";
|
|
5
|
-
import { computeCompoundReadiness, readKnowledgeSafely } from "../knowledge-store.js";
|
|
6
|
-
function parseArgs(tokens) {
|
|
7
|
-
const args = { json: false, quiet: false, write: true };
|
|
8
|
-
for (let i = 0; i < tokens.length; i += 1) {
|
|
9
|
-
const token = tokens[i];
|
|
10
|
-
if (token === "--json")
|
|
11
|
-
args.json = true;
|
|
12
|
-
else if (token === "--quiet")
|
|
13
|
-
args.quiet = true;
|
|
14
|
-
else if (token === "--no-write")
|
|
15
|
-
args.write = false;
|
|
16
|
-
else if (token === "--write")
|
|
17
|
-
args.write = true;
|
|
18
|
-
else if (token === "--threshold") {
|
|
19
|
-
const value = tokens[i + 1];
|
|
20
|
-
if (!value)
|
|
21
|
-
throw new Error("--threshold requires a numeric value");
|
|
22
|
-
// Strict: reject "2abc", "2.9", "-1", "" — parseInt would silently
|
|
23
|
-
// accept the first two and produce surprising behavior.
|
|
24
|
-
if (!/^\d+$/u.test(value)) {
|
|
25
|
-
throw new Error(`--threshold must be a positive integer, got ${value}`);
|
|
26
|
-
}
|
|
27
|
-
const parsed = Number.parseInt(value, 10);
|
|
28
|
-
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
29
|
-
throw new Error(`--threshold must be a positive integer, got ${value}`);
|
|
30
|
-
}
|
|
31
|
-
args.threshold = parsed;
|
|
32
|
-
i += 1;
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
throw new Error(`Unknown compound-readiness flag: ${token}`);
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
return args;
|
|
39
|
-
}
|
|
40
|
-
function stateDir(projectRoot) {
|
|
41
|
-
return path.join(projectRoot, RUNTIME_ROOT, "state");
|
|
42
|
-
}
|
|
43
|
-
function archiveRunsDir(projectRoot) {
|
|
44
|
-
return path.join(projectRoot, RUNTIME_ROOT, "archive");
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Count archived runs as sub-directories under `.cclaw/archive/`. Missing
|
|
48
|
-
* dir / ENOENT is interpreted as zero — callers should NOT conflate
|
|
49
|
-
* that with "unknown" (undefined); we only return undefined on
|
|
50
|
-
* unexpected errors so the caller can choose to skip the relaxation
|
|
51
|
-
* rather than guess a number.
|
|
52
|
-
*/
|
|
53
|
-
export async function countArchivedRunsSafely(projectRoot) {
|
|
54
|
-
const dir = archiveRunsDir(projectRoot);
|
|
55
|
-
try {
|
|
56
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
57
|
-
return entries.filter((entry) => entry.isDirectory()).length;
|
|
58
|
-
}
|
|
59
|
-
catch (error) {
|
|
60
|
-
const code = error?.code;
|
|
61
|
-
if (code === "ENOENT")
|
|
62
|
-
return 0;
|
|
63
|
-
return undefined;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Compact one-liner for bootstrap surfaces.
|
|
68
|
-
*
|
|
69
|
-
* Example: `Compound readiness: clusters=12, ready=2 (critical=1)`.
|
|
70
|
-
* When `ready === 0`, emit `Compound readiness: no candidates`.
|
|
71
|
-
*/
|
|
72
|
-
export function formatCompoundReadinessLine(status) {
|
|
73
|
-
if (status.readyCount === 0) {
|
|
74
|
-
return `Compound readiness: no candidates (clusters=${status.clusterCount}, threshold=${status.threshold})`;
|
|
75
|
-
}
|
|
76
|
-
const critical = status.ready.filter((cluster) => cluster.severity === "critical").length;
|
|
77
|
-
const criticalSuffix = critical > 0 ? ` (critical=${critical})` : "";
|
|
78
|
-
return `Compound readiness: clusters=${status.clusterCount}, ready=${status.readyCount}${criticalSuffix}`;
|
|
79
|
-
}
|
|
80
|
-
export async function runCompoundReadinessCommand(projectRoot, argv, io) {
|
|
81
|
-
const args = parseArgs(argv);
|
|
82
|
-
const threshold = args.threshold;
|
|
83
|
-
const archivedRunsCount = await countArchivedRunsSafely(projectRoot);
|
|
84
|
-
const { entries } = await readKnowledgeSafely(projectRoot, { lockAware: true });
|
|
85
|
-
const status = computeCompoundReadiness(entries, {
|
|
86
|
-
...(typeof threshold === "number" ? { threshold } : {}),
|
|
87
|
-
...(typeof archivedRunsCount === "number" ? { archivedRunsCount } : {})
|
|
88
|
-
});
|
|
89
|
-
if (args.write) {
|
|
90
|
-
const target = path.join(stateDir(projectRoot), "compound-readiness.json");
|
|
91
|
-
await writeFileSafe(target, `${JSON.stringify(status, null, 2)}\n`);
|
|
92
|
-
}
|
|
93
|
-
if (!args.quiet) {
|
|
94
|
-
if (args.json) {
|
|
95
|
-
io.stdout.write(`${JSON.stringify(status, null, 2)}\n`);
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
io.stdout.write(`${formatCompoundReadinessLine(status)}\n`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
return 0;
|
|
102
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import { promisify } from "node:util";
|
|
3
|
-
const execFileAsync = promisify(execFile);
|
|
4
|
-
const PUBLIC_SURFACE_PATH_PATTERNS = [
|
|
5
|
-
/(^|\/)(cli|types?|config)\.[cm]?[jt]s$/iu,
|
|
6
|
-
/(^|\/)(openapi|swagger|schema)(\/|[-_.])/iu,
|
|
7
|
-
/(^|\/)(api|commands?|flags?)(\/|[-_.])/iu,
|
|
8
|
-
/(^|\/)(package|tsconfig)\.json$/iu
|
|
9
|
-
];
|
|
10
|
-
async function resolveDiffBase(projectRoot) {
|
|
11
|
-
try {
|
|
12
|
-
const { stdout } = await execFileAsync("git", ["rev-parse", "HEAD~1"], {
|
|
13
|
-
cwd: projectRoot
|
|
14
|
-
});
|
|
15
|
-
const base = stdout.trim();
|
|
16
|
-
return base.length > 0 ? base : null;
|
|
17
|
-
}
|
|
18
|
-
catch {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
export async function detectPublicApiChanges(projectRoot) {
|
|
23
|
-
const base = await resolveDiffBase(projectRoot);
|
|
24
|
-
if (!base) {
|
|
25
|
-
return { triggered: false, changedFiles: [] };
|
|
26
|
-
}
|
|
27
|
-
try {
|
|
28
|
-
const range = `${base}..HEAD`;
|
|
29
|
-
const { stdout } = await execFileAsync("git", ["diff", "--name-only", range], {
|
|
30
|
-
cwd: projectRoot
|
|
31
|
-
});
|
|
32
|
-
const changedFiles = stdout
|
|
33
|
-
.split(/\r?\n/gu)
|
|
34
|
-
.map((line) => line.trim())
|
|
35
|
-
.filter((line) => line.length > 0)
|
|
36
|
-
.filter((filePath) => PUBLIC_SURFACE_PATH_PATTERNS.some((pattern) => pattern.test(filePath)));
|
|
37
|
-
return {
|
|
38
|
-
triggered: changedFiles.length > 0,
|
|
39
|
-
changedFiles
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
catch {
|
|
43
|
-
return { triggered: false, changedFiles: [] };
|
|
44
|
-
}
|
|
45
|
-
}
|
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
import { execFile } from "node:child_process";
|
|
2
|
-
import { promisify } from "node:util";
|
|
3
|
-
const execFileAsync = promisify(execFile);
|
|
4
|
-
const WORKFLOW_PATH = /(^|\/)\.github\/workflows\//u;
|
|
5
|
-
const CURSOR_CONFIG_PATH = /(^|\/)\.cursor\//u;
|
|
6
|
-
const PACKAGE_JSON_PATH = /(^|\/)package\.json$/u;
|
|
7
|
-
const SUPPLY_CHAIN_DEP_KEYS = [
|
|
8
|
-
"dependencies",
|
|
9
|
-
"devDependencies",
|
|
10
|
-
"peerDependencies",
|
|
11
|
-
"optionalDependencies"
|
|
12
|
-
];
|
|
13
|
-
async function resolveDiffBase(projectRoot) {
|
|
14
|
-
try {
|
|
15
|
-
const { stdout } = await execFileAsync("git", ["rev-parse", "HEAD~1"], {
|
|
16
|
-
cwd: projectRoot
|
|
17
|
-
});
|
|
18
|
-
const base = stdout.trim();
|
|
19
|
-
return base.length > 0 ? base : null;
|
|
20
|
-
}
|
|
21
|
-
catch {
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
async function readFileAtRev(projectRoot, rev, filePath) {
|
|
26
|
-
try {
|
|
27
|
-
const { stdout } = await execFileAsync("git", ["show", `${rev}:${filePath}`], {
|
|
28
|
-
cwd: projectRoot,
|
|
29
|
-
maxBuffer: 32 * 1024 * 1024
|
|
30
|
-
});
|
|
31
|
-
return stdout;
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
function dependencyMapsDiffer(before, after) {
|
|
38
|
-
const beforeKeys = before ? Object.keys(before).sort() : [];
|
|
39
|
-
const afterKeys = after ? Object.keys(after).sort() : [];
|
|
40
|
-
if (beforeKeys.length !== afterKeys.length)
|
|
41
|
-
return true;
|
|
42
|
-
for (let i = 0; i < beforeKeys.length; i += 1) {
|
|
43
|
-
if (beforeKeys[i] !== afterKeys[i])
|
|
44
|
-
return true;
|
|
45
|
-
const k = beforeKeys[i];
|
|
46
|
-
if (before[k] !== after[k]) {
|
|
47
|
-
return true;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return false;
|
|
51
|
-
}
|
|
52
|
-
async function packageJsonHasDependencyDiff(projectRoot, base, filePath) {
|
|
53
|
-
const beforeRaw = await readFileAtRev(projectRoot, base, filePath);
|
|
54
|
-
const afterRaw = await readFileAtRev(projectRoot, "HEAD", filePath);
|
|
55
|
-
// If either side is missing or unparseable, treat as changed (be conservative).
|
|
56
|
-
if (beforeRaw === null || afterRaw === null)
|
|
57
|
-
return true;
|
|
58
|
-
let beforeJson;
|
|
59
|
-
let afterJson;
|
|
60
|
-
try {
|
|
61
|
-
beforeJson = JSON.parse(beforeRaw);
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
try {
|
|
67
|
-
afterJson = JSON.parse(afterRaw);
|
|
68
|
-
}
|
|
69
|
-
catch {
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
const beforeObj = beforeJson !== null && typeof beforeJson === "object"
|
|
73
|
-
? beforeJson
|
|
74
|
-
: {};
|
|
75
|
-
const afterObj = afterJson !== null && typeof afterJson === "object"
|
|
76
|
-
? afterJson
|
|
77
|
-
: {};
|
|
78
|
-
for (const key of SUPPLY_CHAIN_DEP_KEYS) {
|
|
79
|
-
const beforeMap = (beforeObj[key] !== null && typeof beforeObj[key] === "object")
|
|
80
|
-
? beforeObj[key]
|
|
81
|
-
: undefined;
|
|
82
|
-
const afterMap = (afterObj[key] !== null && typeof afterObj[key] === "object")
|
|
83
|
-
? afterObj[key]
|
|
84
|
-
: undefined;
|
|
85
|
-
if (dependencyMapsDiffer(beforeMap, afterMap)) {
|
|
86
|
-
return true;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
export async function detectSupplyChainChanges(projectRoot) {
|
|
92
|
-
const base = await resolveDiffBase(projectRoot);
|
|
93
|
-
if (!base) {
|
|
94
|
-
return { triggered: false, changedFiles: [], reasons: [] };
|
|
95
|
-
}
|
|
96
|
-
let changed = [];
|
|
97
|
-
try {
|
|
98
|
-
const range = `${base}..HEAD`;
|
|
99
|
-
const { stdout } = await execFileAsync("git", ["diff", "--name-only", range], {
|
|
100
|
-
cwd: projectRoot
|
|
101
|
-
});
|
|
102
|
-
changed = stdout
|
|
103
|
-
.split(/\r?\n/gu)
|
|
104
|
-
.map((line) => line.trim())
|
|
105
|
-
.filter((line) => line.length > 0);
|
|
106
|
-
}
|
|
107
|
-
catch {
|
|
108
|
-
return { triggered: false, changedFiles: [], reasons: [] };
|
|
109
|
-
}
|
|
110
|
-
const matchedFiles = [];
|
|
111
|
-
const reasons = [];
|
|
112
|
-
for (const filePath of changed) {
|
|
113
|
-
if (WORKFLOW_PATH.test(filePath)) {
|
|
114
|
-
matchedFiles.push(filePath);
|
|
115
|
-
reasons.push(`.github/workflows changed: ${filePath}`);
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
if (CURSOR_CONFIG_PATH.test(filePath)) {
|
|
119
|
-
matchedFiles.push(filePath);
|
|
120
|
-
reasons.push(`.cursor config changed: ${filePath}`);
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
if (PACKAGE_JSON_PATH.test(filePath)) {
|
|
124
|
-
// Only flag when supply-chain dependency keys differ.
|
|
125
|
-
const depDiffers = await packageJsonHasDependencyDiff(projectRoot, base, filePath);
|
|
126
|
-
if (depDiffers) {
|
|
127
|
-
matchedFiles.push(filePath);
|
|
128
|
-
reasons.push(`${filePath} dependencies/devDependencies/peerDependencies/optionalDependencies changed`);
|
|
129
|
-
}
|
|
130
|
-
continue;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return {
|
|
134
|
-
triggered: matchedFiles.length > 0,
|
|
135
|
-
changedFiles: matchedFiles,
|
|
136
|
-
reasons
|
|
137
|
-
};
|
|
138
|
-
}
|