cclaw-cli 0.49.0 → 0.51.1
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 +57 -84
- package/dist/artifact-linter.d.ts +4 -0
- package/dist/artifact-linter.js +24 -3
- package/dist/cli.d.ts +1 -19
- package/dist/cli.js +49 -491
- package/dist/constants.d.ts +2 -13
- package/dist/constants.js +1 -43
- package/dist/content/closeout-guidance.d.ts +14 -0
- package/dist/content/closeout-guidance.js +42 -0
- package/dist/content/core-agents.js +55 -17
- package/dist/content/decision-protocol.d.ts +12 -0
- package/dist/content/decision-protocol.js +20 -0
- package/dist/content/diff-command.d.ts +1 -2
- package/dist/content/diff-command.js +8 -94
- package/dist/content/examples.d.ts +4 -10
- package/dist/content/examples.js +10 -20
- package/dist/content/hook-events.js +2 -2
- package/dist/content/hook-inline-snippets.d.ts +5 -2
- package/dist/content/hook-inline-snippets.js +33 -1
- package/dist/content/hook-manifest.d.ts +3 -4
- package/dist/content/hook-manifest.js +11 -12
- package/dist/content/hooks.js +44 -21
- package/dist/content/ideate-command.d.ts +2 -0
- package/dist/content/ideate-command.js +34 -25
- package/dist/content/iron-laws.d.ts +5 -5
- package/dist/content/iron-laws.js +5 -5
- package/dist/content/language-policy.d.ts +2 -0
- package/dist/content/language-policy.js +13 -0
- package/dist/content/learnings.d.ts +3 -4
- package/dist/content/learnings.js +26 -50
- package/dist/content/meta-skill.js +33 -22
- package/dist/content/next-command.js +41 -38
- package/dist/content/node-hooks.js +17 -345
- package/dist/content/opencode-plugin.js +5 -103
- package/dist/content/research-playbooks.js +14 -14
- package/dist/content/review-loop.d.ts +2 -0
- package/dist/content/review-loop.js +8 -0
- package/dist/content/session-hooks.js +15 -47
- package/dist/content/skills.d.ts +0 -5
- package/dist/content/skills.js +55 -128
- package/dist/content/stage-common-guidance.d.ts +0 -1
- package/dist/content/stage-common-guidance.js +17 -14
- package/dist/content/stage-schema.d.ts +26 -1
- package/dist/content/stage-schema.js +121 -40
- package/dist/content/stages/_lint-metadata/index.js +9 -15
- package/dist/content/stages/brainstorm.js +22 -43
- package/dist/content/stages/design.js +37 -57
- package/dist/content/stages/plan.js +22 -13
- package/dist/content/stages/review.js +24 -27
- package/dist/content/stages/scope.js +34 -46
- package/dist/content/stages/ship.js +7 -4
- package/dist/content/stages/spec.js +20 -9
- package/dist/content/stages/tdd.js +64 -44
- package/dist/content/start-command.js +13 -12
- package/dist/content/status-command.d.ts +2 -7
- package/dist/content/status-command.js +19 -146
- package/dist/content/subagents.d.ts +0 -5
- package/dist/content/subagents.js +51 -28
- package/dist/content/templates.d.ts +1 -1
- package/dist/content/templates.js +126 -135
- package/dist/content/track-render-context.d.ts +17 -0
- package/dist/content/track-render-context.js +44 -0
- package/dist/content/tree-command.d.ts +1 -2
- package/dist/content/tree-command.js +4 -87
- package/dist/content/utility-skills.d.ts +2 -29
- package/dist/content/utility-skills.js +2 -1534
- package/dist/content/view-command.js +31 -11
- package/dist/delegation.d.ts +1 -1
- package/dist/delegation.js +5 -15
- package/dist/doctor-registry.js +20 -21
- package/dist/doctor.js +88 -344
- package/dist/flow-state.d.ts +3 -0
- package/dist/flow-state.js +2 -0
- package/dist/harness-adapters.d.ts +1 -1
- package/dist/harness-adapters.js +51 -58
- package/dist/install.js +128 -358
- package/dist/internal/advance-stage.js +3 -9
- package/dist/internal/compound-readiness.d.ts +1 -1
- package/dist/internal/compound-readiness.js +1 -1
- package/dist/internal/tdd-loop-status.d.ts +1 -1
- package/dist/internal/tdd-loop-status.js +1 -1
- package/dist/knowledge-store.d.ts +16 -10
- package/dist/knowledge-store.js +51 -15
- package/dist/policy.js +16 -105
- package/dist/run-archive.d.ts +4 -6
- package/dist/run-archive.js +15 -20
- package/dist/run-persistence.d.ts +2 -2
- package/dist/run-persistence.js +3 -9
- package/package.json +1 -2
- package/dist/content/archive-command.d.ts +0 -2
- package/dist/content/archive-command.js +0 -124
- package/dist/content/compound-command.d.ts +0 -5
- package/dist/content/compound-command.js +0 -193
- package/dist/content/contexts.d.ts +0 -18
- package/dist/content/contexts.js +0 -24
- package/dist/content/contracts.d.ts +0 -2
- package/dist/content/contracts.js +0 -51
- package/dist/content/doctor-references.d.ts +0 -2
- package/dist/content/doctor-references.js +0 -150
- package/dist/content/eval-scaffold.d.ts +0 -15
- package/dist/content/eval-scaffold.js +0 -370
- package/dist/content/feature-command.d.ts +0 -2
- package/dist/content/feature-command.js +0 -123
- package/dist/content/flow-map.d.ts +0 -23
- package/dist/content/flow-map.js +0 -134
- package/dist/content/harness-doc.d.ts +0 -2
- package/dist/content/harness-doc.js +0 -202
- package/dist/content/harness-playbooks.d.ts +0 -24
- package/dist/content/harness-playbooks.js +0 -393
- package/dist/content/harness-tool-refs.d.ts +0 -20
- package/dist/content/harness-tool-refs.js +0 -268
- package/dist/content/ops-command.d.ts +0 -2
- package/dist/content/ops-command.js +0 -71
- package/dist/content/protocols.d.ts +0 -7
- package/dist/content/protocols.js +0 -215
- package/dist/content/retro-command.d.ts +0 -2
- package/dist/content/retro-command.js +0 -165
- package/dist/content/rewind-command.d.ts +0 -2
- package/dist/content/rewind-command.js +0 -106
- package/dist/content/tdd-log-command.d.ts +0 -2
- package/dist/content/tdd-log-command.js +0 -85
- package/dist/eval/agents/single-shot.d.ts +0 -27
- package/dist/eval/agents/single-shot.js +0 -79
- package/dist/eval/agents/with-tools.d.ts +0 -44
- package/dist/eval/agents/with-tools.js +0 -261
- package/dist/eval/agents/workflow.d.ts +0 -31
- package/dist/eval/agents/workflow.js +0 -155
- package/dist/eval/baseline.d.ts +0 -38
- package/dist/eval/baseline.js +0 -282
- package/dist/eval/config-loader.d.ts +0 -14
- package/dist/eval/config-loader.js +0 -395
- package/dist/eval/corpus.d.ts +0 -30
- package/dist/eval/corpus.js +0 -330
- package/dist/eval/cost-guard.d.ts +0 -102
- package/dist/eval/cost-guard.js +0 -190
- package/dist/eval/diff.d.ts +0 -64
- package/dist/eval/diff.js +0 -323
- package/dist/eval/llm-client.d.ts +0 -176
- package/dist/eval/llm-client.js +0 -267
- package/dist/eval/mode.d.ts +0 -28
- package/dist/eval/mode.js +0 -61
- package/dist/eval/progress.d.ts +0 -83
- package/dist/eval/progress.js +0 -59
- package/dist/eval/report.d.ts +0 -11
- package/dist/eval/report.js +0 -181
- package/dist/eval/rubric-loader.d.ts +0 -20
- package/dist/eval/rubric-loader.js +0 -143
- package/dist/eval/runner.d.ts +0 -81
- package/dist/eval/runner.js +0 -746
- package/dist/eval/runs.d.ts +0 -41
- package/dist/eval/runs.js +0 -114
- package/dist/eval/sandbox.d.ts +0 -38
- package/dist/eval/sandbox.js +0 -137
- package/dist/eval/tools/glob.d.ts +0 -2
- package/dist/eval/tools/glob.js +0 -163
- package/dist/eval/tools/grep.d.ts +0 -2
- package/dist/eval/tools/grep.js +0 -152
- package/dist/eval/tools/index.d.ts +0 -7
- package/dist/eval/tools/index.js +0 -35
- package/dist/eval/tools/read.d.ts +0 -2
- package/dist/eval/tools/read.js +0 -122
- package/dist/eval/tools/types.d.ts +0 -49
- package/dist/eval/tools/types.js +0 -41
- package/dist/eval/tools/write.d.ts +0 -2
- package/dist/eval/tools/write.js +0 -92
- package/dist/eval/types.d.ts +0 -561
- package/dist/eval/types.js +0 -47
- package/dist/eval/verifiers/judge.d.ts +0 -40
- package/dist/eval/verifiers/judge.js +0 -256
- package/dist/eval/verifiers/rules.d.ts +0 -24
- package/dist/eval/verifiers/rules.js +0 -218
- package/dist/eval/verifiers/structural.d.ts +0 -14
- package/dist/eval/verifiers/structural.js +0 -171
- package/dist/eval/verifiers/traceability.d.ts +0 -23
- package/dist/eval/verifiers/traceability.js +0 -84
- package/dist/eval/verifiers/workflow-consistency.d.ts +0 -21
- package/dist/eval/verifiers/workflow-consistency.js +0 -225
- package/dist/eval/workflow-corpus.d.ts +0 -7
- package/dist/eval/workflow-corpus.js +0 -207
- package/dist/feature-system.d.ts +0 -42
- package/dist/feature-system.js +0 -432
- package/dist/internal/knowledge-digest.d.ts +0 -7
- package/dist/internal/knowledge-digest.js +0 -93
|
@@ -290,12 +290,6 @@ async function appendJsonLine(filePath, value) {
|
|
|
290
290
|
});
|
|
291
291
|
}
|
|
292
292
|
|
|
293
|
-
async function writeTextFileAtomic(filePath, content) {
|
|
294
|
-
await withDirectoryLockInline(lockPathFor(filePath), async () => {
|
|
295
|
-
await writeFileAtomic(filePath, content);
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
|
|
299
293
|
async function readStdin() {
|
|
300
294
|
return await new Promise((resolve) => {
|
|
301
295
|
let data = "";
|
|
@@ -400,7 +394,7 @@ function hookEventNameForOutput(hookName) {
|
|
|
400
394
|
if (hookName === "prompt-guard") return "PreToolUse";
|
|
401
395
|
if (hookName === "workflow-guard") return "PreToolUse";
|
|
402
396
|
if (hookName === "context-monitor") return "PostToolUse";
|
|
403
|
-
if (hookName === "stop-
|
|
397
|
+
if (hookName === "stop-handoff") return "Stop";
|
|
404
398
|
if (hookName === "pre-compact") return "PreCompact";
|
|
405
399
|
if (hookName === "verify-current-state") return "UserPromptSubmit";
|
|
406
400
|
return "SessionStart";
|
|
@@ -811,33 +805,9 @@ async function readFlowState(root) {
|
|
|
811
805
|
};
|
|
812
806
|
}
|
|
813
807
|
|
|
814
|
-
function formatCheckpointSummary(checkpointObj) {
|
|
815
|
-
const stage = typeof checkpointObj.stage === "string" ? checkpointObj.stage : "none";
|
|
816
|
-
const status = typeof checkpointObj.status === "string" ? checkpointObj.status : "unknown";
|
|
817
|
-
const runId = typeof checkpointObj.runId === "string" ? checkpointObj.runId : "none";
|
|
818
|
-
const timestamp = typeof checkpointObj.timestamp === "string" ? checkpointObj.timestamp : "unknown";
|
|
819
|
-
return "Checkpoint: stage=" + stage + ", status=" + status + ", run=" + runId + ", at=" + timestamp;
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
function stageSuggestion(stage) {
|
|
823
|
-
const map = {
|
|
824
|
-
brainstorm:
|
|
825
|
-
"Suggestion: list 2-3 alternatives and ask a single focused clarifying question before direction lock.",
|
|
826
|
-
scope: "Suggestion: lock explicit in-scope/out-of-scope boundaries and choose one scope mode.",
|
|
827
|
-
design:
|
|
828
|
-
"Suggestion: map failure modes per new codepath and confirm architecture boundaries before moving forward.",
|
|
829
|
-
spec: "Suggestion: ensure every acceptance criterion is measurable and mapped to a concrete test.",
|
|
830
|
-
plan: "Suggestion: group tasks into dependency batches and keep WAIT_FOR_CONFIRM pending until approval.",
|
|
831
|
-
tdd: "Suggestion: execute RED -> GREEN -> REFACTOR for each selected slice and capture evidence per cycle.",
|
|
832
|
-
review: "Suggestion: run Layer 1 before Layer 2 and reconcile findings into 07-review-army.json.",
|
|
833
|
-
ship: "Suggestion: verify preflight + rollback plan before selecting exactly one finalization mode."
|
|
834
|
-
};
|
|
835
|
-
return map[stage] || "";
|
|
836
|
-
}
|
|
837
808
|
|
|
838
809
|
async function buildKnowledgeDigest(root, currentStage, prereadRaw) {
|
|
839
810
|
const knowledgeFile = path.join(root, RUNTIME_ROOT, "knowledge.jsonl");
|
|
840
|
-
const digestFile = path.join(root, RUNTIME_ROOT, "state", "knowledge-digest.md");
|
|
841
811
|
// Caller may supply pre-read raw bytes to avoid re-reading knowledge.jsonl.
|
|
842
812
|
// Falls back to a local read if nothing is passed in.
|
|
843
813
|
const raw = typeof prereadRaw === "string"
|
|
@@ -873,105 +843,19 @@ async function buildKnowledgeDigest(root, currentStage, prereadRaw) {
|
|
|
873
843
|
});
|
|
874
844
|
const body =
|
|
875
845
|
relevant.length > 0 ? relevant.join("\\n") : "(no matching entries for current stage)";
|
|
876
|
-
await writeTextFileAtomic(
|
|
877
|
-
digestFile,
|
|
878
|
-
"# Knowledge digest (auto-generated)\\n\\n" + body + "\\n"
|
|
879
|
-
);
|
|
880
846
|
return {
|
|
881
847
|
digestLines: relevant,
|
|
882
848
|
learningsCount
|
|
883
849
|
};
|
|
884
850
|
}
|
|
885
851
|
|
|
886
|
-
async function readRecentActivityLines(activityFile) {
|
|
887
|
-
const raw = await readTextFile(activityFile, "");
|
|
888
|
-
const lines = raw.split(/\\r?\\n/gu).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
889
|
-
const tail = lines.slice(-5);
|
|
890
|
-
const out = [];
|
|
891
|
-
for (const line of tail) {
|
|
892
|
-
try {
|
|
893
|
-
const parsed = JSON.parse(line);
|
|
894
|
-
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) continue;
|
|
895
|
-
out.push(
|
|
896
|
-
"- " +
|
|
897
|
-
(typeof parsed.ts === "string" ? parsed.ts : "unknown") +
|
|
898
|
-
" [" +
|
|
899
|
-
(typeof parsed.phase === "string" ? parsed.phase : "unknown") +
|
|
900
|
-
"] " +
|
|
901
|
-
(typeof parsed.tool === "string" ? parsed.tool : "unknown") +
|
|
902
|
-
" (stage=" +
|
|
903
|
-
(typeof parsed.stage === "string" ? parsed.stage : "unknown") +
|
|
904
|
-
", run=" +
|
|
905
|
-
(typeof parsed.runId === "string" ? parsed.runId : "none") +
|
|
906
|
-
")"
|
|
907
|
-
);
|
|
908
|
-
} catch {
|
|
909
|
-
// ignore malformed activity lines
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
return out;
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
async function readLatestContextWarningLine(filePath) {
|
|
916
|
-
const raw = await readTextFile(filePath, "");
|
|
917
|
-
const lines = raw.split(/\\r?\\n/gu).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
918
|
-
const line = lines[lines.length - 1] || "";
|
|
919
|
-
if (line.length === 0) return "";
|
|
920
|
-
try {
|
|
921
|
-
const parsed = JSON.parse(line);
|
|
922
|
-
if (parsed && typeof parsed === "object" && typeof parsed.note === "string") {
|
|
923
|
-
return parsed.note;
|
|
924
|
-
}
|
|
925
|
-
} catch {
|
|
926
|
-
// fallback
|
|
927
|
-
}
|
|
928
|
-
return line;
|
|
929
|
-
}
|
|
930
|
-
|
|
931
852
|
async function handleSessionStart(runtime) {
|
|
932
853
|
const state = await readFlowState(runtime.root);
|
|
933
854
|
const stateDir = path.join(runtime.root, RUNTIME_ROOT, "state");
|
|
934
|
-
const contextsDir = path.join(runtime.root, RUNTIME_ROOT, "contexts");
|
|
935
|
-
const activeFeatureFile = path.join(stateDir, "active-feature.json");
|
|
936
|
-
const checkpointFile = path.join(stateDir, "checkpoint.json");
|
|
937
|
-
const activityFile = path.join(stateDir, "stage-activity.jsonl");
|
|
938
|
-
const contextWarningsFile = path.join(stateDir, "context-warnings.jsonl");
|
|
939
|
-
const contextModeFile = path.join(stateDir, "context-mode.json");
|
|
940
|
-
const suggestionMemoryFile = path.join(stateDir, "suggestion-memory.json");
|
|
941
855
|
const ironLawsFile = path.join(stateDir, "iron-laws.json");
|
|
942
|
-
const sessionDigestFile = path.join(stateDir, "session-digest.md");
|
|
943
856
|
const metaSkillFile = path.join(runtime.root, RUNTIME_ROOT, "skills", "using-cclaw", "SKILL.md");
|
|
944
857
|
|
|
945
|
-
|
|
946
|
-
const activeFeature =
|
|
947
|
-
typeof activeFeatureObj.activeFeature === "string" && activeFeatureObj.activeFeature.length > 0
|
|
948
|
-
? activeFeatureObj.activeFeature
|
|
949
|
-
: "default";
|
|
950
|
-
|
|
951
|
-
const contextModeObj = toObject(await readJsonFile(contextModeFile, {})) || {};
|
|
952
|
-
const activeContextMode =
|
|
953
|
-
typeof contextModeObj.activeMode === "string" && contextModeObj.activeMode.length > 0
|
|
954
|
-
? contextModeObj.activeMode
|
|
955
|
-
: "default";
|
|
956
|
-
const contextGuidePath = path.join(contextsDir, activeContextMode + ".md");
|
|
957
|
-
const contextModeNote = (await fileExists(contextGuidePath))
|
|
958
|
-
? "Context mode: " +
|
|
959
|
-
activeContextMode +
|
|
960
|
-
" (guide: " +
|
|
961
|
-
RUNTIME_ROOT +
|
|
962
|
-
"/contexts/" +
|
|
963
|
-
activeContextMode +
|
|
964
|
-
".md)"
|
|
965
|
-
: "Context mode: " + activeContextMode;
|
|
966
|
-
|
|
967
|
-
const checkpointObj = toObject(await readJsonFile(checkpointFile, {})) || {};
|
|
968
|
-
const checkpointSummary = Object.keys(checkpointObj).length > 0
|
|
969
|
-
? formatCheckpointSummary(checkpointObj)
|
|
970
|
-
: "";
|
|
971
|
-
|
|
972
|
-
const sessionDigest = (await readTextFile(sessionDigestFile, "")).trim();
|
|
973
|
-
const activitySummary = await readRecentActivityLines(activityFile);
|
|
974
|
-
const contextWarning = await readLatestContextWarningLine(contextWarningsFile);
|
|
858
|
+
|
|
975
859
|
// Read knowledge.jsonl exactly once per session-start while holding the
|
|
976
860
|
// SAME lock CLI writers acquire in \`appendKnowledge\`. Guarantees we never
|
|
977
861
|
// see a partial (mid-write) snapshot. Both the digest and
|
|
@@ -1041,32 +925,8 @@ async function handleSessionStart(runtime) {
|
|
|
1041
925
|
});
|
|
1042
926
|
await writeJsonFile(path.join(stateDir, "compound-readiness.json"), readiness);
|
|
1043
927
|
}
|
|
1044
|
-
const readinessObj = toObject(readiness) || {};
|
|
1045
|
-
const ready = Array.isArray(readinessObj.ready) ? readinessObj.ready : [];
|
|
1046
|
-
const readyCount =
|
|
1047
|
-
typeof readinessObj.readyCount === "number" && Number.isFinite(readinessObj.readyCount)
|
|
1048
|
-
? Math.trunc(readinessObj.readyCount)
|
|
1049
|
-
: ready.length;
|
|
1050
|
-
const clusterCount =
|
|
1051
|
-
typeof readinessObj.clusterCount === "number" && Number.isFinite(readinessObj.clusterCount)
|
|
1052
|
-
? Math.trunc(readinessObj.clusterCount)
|
|
1053
|
-
: 0;
|
|
1054
|
-
const threshold =
|
|
1055
|
-
typeof readinessObj.threshold === "number" && Number.isFinite(readinessObj.threshold)
|
|
1056
|
-
? Math.trunc(readinessObj.threshold)
|
|
1057
|
-
: COMPOUND_RECURRENCE_THRESHOLD;
|
|
1058
928
|
if (state.currentStage === "review" || state.currentStage === "ship") {
|
|
1059
|
-
|
|
1060
|
-
compoundReadinessLine = "Compound readiness: no candidates (clusters=" +
|
|
1061
|
-
String(clusterCount) + ", threshold=" + String(threshold) + ")";
|
|
1062
|
-
} else {
|
|
1063
|
-
const critical = ready.filter(
|
|
1064
|
-
(entry) => entry && typeof entry === "object" && entry.severity === "critical"
|
|
1065
|
-
).length;
|
|
1066
|
-
const criticalSuffix = critical > 0 ? " (critical=" + String(critical) + ")" : "";
|
|
1067
|
-
compoundReadinessLine = "Compound readiness: clusters=" + String(clusterCount) +
|
|
1068
|
-
", ready=" + String(readyCount) + criticalSuffix;
|
|
1069
|
-
}
|
|
929
|
+
compoundReadinessLine = formatCompoundReadinessLineInline(toObject(readiness) || {});
|
|
1070
930
|
}
|
|
1071
931
|
} catch (err) {
|
|
1072
932
|
// Best-effort — a malformed knowledge.jsonl must never break
|
|
@@ -1080,26 +940,6 @@ async function handleSessionStart(runtime) {
|
|
|
1080
940
|
);
|
|
1081
941
|
}
|
|
1082
942
|
|
|
1083
|
-
const suggestionMemory = toObject(await readJsonFile(suggestionMemoryFile, {})) || {};
|
|
1084
|
-
const suggestionsEnabled = suggestionMemory.enabled !== false;
|
|
1085
|
-
const mutedStages = Array.isArray(suggestionMemory.mutedStages)
|
|
1086
|
-
? suggestionMemory.mutedStages.filter((value) => typeof value === "string")
|
|
1087
|
-
: [];
|
|
1088
|
-
const stageMuted = mutedStages.includes(state.currentStage);
|
|
1089
|
-
let stageHint = "";
|
|
1090
|
-
if (suggestionsEnabled && !stageMuted) {
|
|
1091
|
-
stageHint = stageSuggestion(state.currentStage);
|
|
1092
|
-
if (stageHint.length > 0) {
|
|
1093
|
-
const nextSuggestionMemory = {
|
|
1094
|
-
enabled: suggestionsEnabled,
|
|
1095
|
-
mutedStages,
|
|
1096
|
-
lastSuggestedStage: state.currentStage,
|
|
1097
|
-
lastSuggestedAt: new Date().toISOString()
|
|
1098
|
-
};
|
|
1099
|
-
await writeJsonFile(suggestionMemoryFile, nextSuggestionMemory);
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
|
|
1103
943
|
const ironLawsObj = toObject(await readJsonFile(ironLawsFile, {})) || {};
|
|
1104
944
|
const laws = Array.isArray(ironLawsObj.laws) ? ironLawsObj.laws : [];
|
|
1105
945
|
const ironLawLines = laws
|
|
@@ -1122,50 +962,23 @@ async function handleSessionStart(runtime) {
|
|
|
1122
962
|
String(state.completedCount) +
|
|
1123
963
|
"/8 completed, run=" +
|
|
1124
964
|
state.activeRunId +
|
|
1125
|
-
", feature=" +
|
|
1126
|
-
activeFeature +
|
|
1127
965
|
"). Active artifacts: " +
|
|
1128
966
|
RUNTIME_ROOT +
|
|
1129
|
-
"/artifacts/.
|
|
1130
|
-
RUNTIME_ROOT +
|
|
1131
|
-
"/state/worktrees.json (managed roots: " +
|
|
1132
|
-
RUNTIME_ROOT +
|
|
1133
|
-
"/worktrees/). Learnings: " +
|
|
967
|
+
"/artifacts/. Learnings: " +
|
|
1134
968
|
String(knowledge.learningsCount) +
|
|
1135
969
|
" entries."
|
|
1136
970
|
];
|
|
1137
|
-
parts.push(contextModeNote);
|
|
1138
|
-
if (checkpointSummary.length > 0) {
|
|
1139
|
-
parts.push(checkpointSummary);
|
|
1140
|
-
}
|
|
1141
|
-
if (sessionDigest.length > 0) {
|
|
1142
|
-
parts.push("Last session:\\n" + sessionDigest);
|
|
1143
|
-
}
|
|
1144
|
-
if (activitySummary.length > 0) {
|
|
1145
|
-
parts.push("Recent stage activity:\\n" + activitySummary.join("\\n"));
|
|
1146
|
-
}
|
|
1147
971
|
if (ralphLoopLine.length > 0) {
|
|
1148
972
|
parts.push(ralphLoopLine);
|
|
1149
973
|
}
|
|
1150
974
|
if (compoundReadinessLine.length > 0) {
|
|
1151
975
|
parts.push(compoundReadinessLine);
|
|
1152
976
|
}
|
|
1153
|
-
if (contextWarning.length > 0) {
|
|
1154
|
-
parts.push("Latest context warning:\\n" + contextWarning);
|
|
1155
|
-
}
|
|
1156
|
-
if (stageHint.length > 0) {
|
|
1157
|
-
parts.push(
|
|
1158
|
-
stageHint +
|
|
1159
|
-
"\\nTo disable suggestions persistently set " +
|
|
1160
|
-
RUNTIME_ROOT +
|
|
1161
|
-
"/state/suggestion-memory.json -> enabled=false."
|
|
1162
|
-
);
|
|
1163
|
-
}
|
|
1164
977
|
if (staleStageNames.length > 0) {
|
|
1165
978
|
parts.push(
|
|
1166
979
|
"Stale stages pending acknowledgement: " +
|
|
1167
980
|
staleStageNames.join(", ") +
|
|
1168
|
-
" (use
|
|
981
|
+
" (use cclaw internal rewind --ack <stage> after redo)."
|
|
1169
982
|
);
|
|
1170
983
|
}
|
|
1171
984
|
if (knowledge.digestLines.length > 0) {
|
|
@@ -1222,15 +1035,14 @@ function stopLawIsStrict(ironLawsObj) {
|
|
|
1222
1035
|
(row) =>
|
|
1223
1036
|
row &&
|
|
1224
1037
|
typeof row === "object" &&
|
|
1225
|
-
row.id === "stop-clean-or-checkpointed" &&
|
|
1038
|
+
(row.id === "stop-clean-or-handoff" || row.id === "stop-clean-or-checkpointed") &&
|
|
1226
1039
|
row.strict === true
|
|
1227
1040
|
);
|
|
1228
1041
|
}
|
|
1229
1042
|
|
|
1230
|
-
async function
|
|
1043
|
+
async function handleStopHandoff(runtime) {
|
|
1231
1044
|
const state = await readFlowState(runtime.root);
|
|
1232
1045
|
const stateDir = path.join(runtime.root, RUNTIME_ROOT, "state");
|
|
1233
|
-
const checkpointFile = path.join(stateDir, "checkpoint.json");
|
|
1234
1046
|
const ironLawsFile = path.join(stateDir, "iron-laws.json");
|
|
1235
1047
|
const input = toObject(runtime.inputData) || {};
|
|
1236
1048
|
const loopCount =
|
|
@@ -1238,31 +1050,11 @@ async function handleStopCheckpoint(runtime) {
|
|
|
1238
1050
|
? Math.trunc(input.loop_count)
|
|
1239
1051
|
: 0;
|
|
1240
1052
|
|
|
1241
|
-
const existing = toObject(await readJsonFile(checkpointFile, {})) || {};
|
|
1242
|
-
const timestamp = new Date().toISOString();
|
|
1243
1053
|
const dirtyState = await isGitDirty(runtime.root);
|
|
1244
|
-
const nextCheckpoint = {
|
|
1245
|
-
...existing,
|
|
1246
|
-
stage: state.currentStage,
|
|
1247
|
-
runId: state.activeRunId,
|
|
1248
|
-
status:
|
|
1249
|
-
typeof existing.status === "string" && existing.status.trim().length > 0
|
|
1250
|
-
? existing.status
|
|
1251
|
-
: "in_progress",
|
|
1252
|
-
dirtyState,
|
|
1253
|
-
lastCompletedStep:
|
|
1254
|
-
typeof existing.lastCompletedStep === "string" ? existing.lastCompletedStep : "",
|
|
1255
|
-
remainingSteps: Array.isArray(existing.remainingSteps) ? existing.remainingSteps : [],
|
|
1256
|
-
blockers: Array.isArray(existing.blockers) ? existing.blockers : [],
|
|
1257
|
-
harness: runtime.harness,
|
|
1258
|
-
timestamp
|
|
1259
|
-
};
|
|
1260
|
-
await writeJsonFile(checkpointFile, nextCheckpoint);
|
|
1261
|
-
|
|
1262
1054
|
const strictStop = stopLawIsStrict(toObject(await readJsonFile(ironLawsFile, {})) || {});
|
|
1263
1055
|
if (dirtyState === "dirty" && strictStop) {
|
|
1264
1056
|
process.stderr.write(
|
|
1265
|
-
'[cclaw] Stop blocked by iron law "stop-clean-or-
|
|
1057
|
+
'[cclaw] Stop blocked by iron law "stop-clean-or-handoff": working tree is dirty. Commit/revert changes or record blockers in the current artifact before ending the session.\\n'
|
|
1266
1058
|
);
|
|
1267
1059
|
return 1;
|
|
1268
1060
|
}
|
|
@@ -1272,13 +1064,9 @@ async function handleStopCheckpoint(runtime) {
|
|
|
1272
1064
|
state.currentStage +
|
|
1273
1065
|
", run=" +
|
|
1274
1066
|
state.activeRunId +
|
|
1275
|
-
").
|
|
1276
|
-
RUNTIME_ROOT +
|
|
1277
|
-
"/state/checkpoint.json. Run metadata sync removed; active artifacts stay in " +
|
|
1067
|
+
"). Active artifacts stay in " +
|
|
1278
1068
|
RUNTIME_ROOT +
|
|
1279
|
-
"/artifacts until
|
|
1280
|
-
RUNTIME_ROOT +
|
|
1281
|
-
"/knowledge.jsonl, (4) commit or revert pending changes.";
|
|
1069
|
+
"/artifacts until archive. Before stopping: (1) confirm flow-state reflects reality, (2) ensure artifact changes match current intent, (3) if you discovered a non-obvious rule/pattern during stage work, add it to the current artifact ## Learnings section so stage-complete can harvest it, (4) commit or revert pending changes.";
|
|
1282
1070
|
|
|
1283
1071
|
if (runtime.harness === "cursor") {
|
|
1284
1072
|
if (loopCount === 0) {
|
|
@@ -1293,116 +1081,7 @@ async function handleStopCheckpoint(runtime) {
|
|
|
1293
1081
|
return 0;
|
|
1294
1082
|
}
|
|
1295
1083
|
|
|
1296
|
-
async function handlePreCompact(
|
|
1297
|
-
const state = await readFlowState(runtime.root);
|
|
1298
|
-
const stateDir = path.join(runtime.root, RUNTIME_ROOT, "state");
|
|
1299
|
-
const flow = state.raw;
|
|
1300
|
-
const stage = state.currentStage;
|
|
1301
|
-
const track = typeof flow.track === "string" ? flow.track : "standard";
|
|
1302
|
-
const skipped = Array.isArray(flow.skippedStages)
|
|
1303
|
-
? flow.skippedStages.filter((value) => typeof value === "string").join(",")
|
|
1304
|
-
: "";
|
|
1305
|
-
|
|
1306
|
-
const stageGateCatalog = toObject(flow.stageGateCatalog) || {};
|
|
1307
|
-
const stageGate = toObject(stageGateCatalog[stage]) || {};
|
|
1308
|
-
const passed = Array.isArray(stageGate.passed)
|
|
1309
|
-
? stageGate.passed.filter((value) => typeof value === "string").join(",")
|
|
1310
|
-
: "";
|
|
1311
|
-
const blocked = Array.isArray(stageGate.blocked)
|
|
1312
|
-
? stageGate.blocked.filter((value) => typeof value === "string").join(",")
|
|
1313
|
-
: "";
|
|
1314
|
-
|
|
1315
|
-
let delegationPending = "";
|
|
1316
|
-
const delegationLog = await readJsonFile(path.join(stateDir, "delegation-log.json"), {});
|
|
1317
|
-
const delegationObj = toObject(delegationLog) || {};
|
|
1318
|
-
const entries = Array.isArray(delegationObj.entries) ? delegationObj.entries : [];
|
|
1319
|
-
const pendingAgents = entries
|
|
1320
|
-
.filter((row) => row && typeof row === "object")
|
|
1321
|
-
.filter(
|
|
1322
|
-
(row) =>
|
|
1323
|
-
row.stage === stage &&
|
|
1324
|
-
row.status !== "completed" &&
|
|
1325
|
-
row.status !== "waived" &&
|
|
1326
|
-
typeof row.agent === "string"
|
|
1327
|
-
)
|
|
1328
|
-
.map((row) => row.agent);
|
|
1329
|
-
if (pendingAgents.length > 0) {
|
|
1330
|
-
delegationPending = [...new Set(pendingAgents)].join(",");
|
|
1331
|
-
}
|
|
1332
|
-
|
|
1333
|
-
const knowledgeRaw = await readTextFile(path.join(runtime.root, RUNTIME_ROOT, "knowledge.jsonl"), "");
|
|
1334
|
-
const knowledgeTail = knowledgeRaw
|
|
1335
|
-
.split(/\\r?\\n/gu)
|
|
1336
|
-
.filter((line) => line.trim().length > 0)
|
|
1337
|
-
.slice(-12)
|
|
1338
|
-
.join("\\n");
|
|
1339
|
-
|
|
1340
|
-
let gitBranch = "unknown";
|
|
1341
|
-
let gitHead = "unknown";
|
|
1342
|
-
let gitDirty = "unknown";
|
|
1343
|
-
await new Promise((resolve) => {
|
|
1344
|
-
const child = spawn("git", ["-C", runtime.root, "rev-parse", "--abbrev-ref", "HEAD"], {
|
|
1345
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
1346
|
-
});
|
|
1347
|
-
let output = "";
|
|
1348
|
-
child.stdout.on("data", (chunk) => {
|
|
1349
|
-
output += String(chunk);
|
|
1350
|
-
});
|
|
1351
|
-
child.on("close", (code) => {
|
|
1352
|
-
if (code === 0 && output.trim().length > 0) {
|
|
1353
|
-
gitBranch = output.trim();
|
|
1354
|
-
}
|
|
1355
|
-
resolve(undefined);
|
|
1356
|
-
});
|
|
1357
|
-
child.on("error", () => resolve(undefined));
|
|
1358
|
-
});
|
|
1359
|
-
await new Promise((resolve) => {
|
|
1360
|
-
const child = spawn("git", ["-C", runtime.root, "rev-parse", "--short", "HEAD"], {
|
|
1361
|
-
stdio: ["ignore", "pipe", "ignore"]
|
|
1362
|
-
});
|
|
1363
|
-
let output = "";
|
|
1364
|
-
child.stdout.on("data", (chunk) => {
|
|
1365
|
-
output += String(chunk);
|
|
1366
|
-
});
|
|
1367
|
-
child.on("close", (code) => {
|
|
1368
|
-
if (code === 0 && output.trim().length > 0) {
|
|
1369
|
-
gitHead = output.trim();
|
|
1370
|
-
}
|
|
1371
|
-
resolve(undefined);
|
|
1372
|
-
});
|
|
1373
|
-
child.on("error", () => resolve(undefined));
|
|
1374
|
-
});
|
|
1375
|
-
gitDirty = await isGitDirty(runtime.root);
|
|
1376
|
-
|
|
1377
|
-
const timestamp = new Date().toISOString();
|
|
1378
|
-
const digest = [
|
|
1379
|
-
"# Session Digest",
|
|
1380
|
-
"_Generated by pre-compact hook at " + timestamp + "_",
|
|
1381
|
-
"",
|
|
1382
|
-
"## Flow snapshot",
|
|
1383
|
-
"- track: " + track,
|
|
1384
|
-
"- current stage: " + stage,
|
|
1385
|
-
"- completed: " + String(state.completedCount) + " stages",
|
|
1386
|
-
"- skipped: " + (skipped.length > 0 ? skipped : "(none)"),
|
|
1387
|
-
"- run: " + state.activeRunId,
|
|
1388
|
-
"",
|
|
1389
|
-
"## Gates (current stage)",
|
|
1390
|
-
"- passed: " + (passed.length > 0 ? passed : "(none)"),
|
|
1391
|
-
"- blocked: " + (blocked.length > 0 ? blocked : "(none)"),
|
|
1392
|
-
"",
|
|
1393
|
-
"## Outstanding delegations",
|
|
1394
|
-
"- pending: " + (delegationPending.length > 0 ? delegationPending : "(none)"),
|
|
1395
|
-
"",
|
|
1396
|
-
"## Git",
|
|
1397
|
-
"- branch: " + gitBranch,
|
|
1398
|
-
"- head: " + gitHead,
|
|
1399
|
-
"- worktree: " + gitDirty
|
|
1400
|
-
];
|
|
1401
|
-
if (knowledgeTail.length > 0) {
|
|
1402
|
-
digest.push("", "## Knowledge tail", knowledgeTail);
|
|
1403
|
-
}
|
|
1404
|
-
const digestFile = path.join(stateDir, "session-digest.md");
|
|
1405
|
-
await writeTextFileAtomic(digestFile, digest.join("\\n") + "\\n");
|
|
1084
|
+
async function handlePreCompact(_runtime) {
|
|
1406
1085
|
return 0;
|
|
1407
1086
|
}
|
|
1408
1087
|
|
|
@@ -1781,7 +1460,6 @@ async function handleWorkflowGuard(runtime) {
|
|
|
1781
1460
|
async function handleContextMonitor(runtime) {
|
|
1782
1461
|
const stateDir = path.join(runtime.root, RUNTIME_ROOT, "state");
|
|
1783
1462
|
const monitorStateFile = path.join(stateDir, "context-monitor.json");
|
|
1784
|
-
const warningsFile = path.join(stateDir, "context-warnings.jsonl");
|
|
1785
1463
|
const autoEvidenceFile = path.join(stateDir, "tdd-red-evidence.jsonl");
|
|
1786
1464
|
const flowState = await readFlowState(runtime.root);
|
|
1787
1465
|
|
|
@@ -1862,14 +1540,7 @@ async function handleContextMonitor(runtime) {
|
|
|
1862
1540
|
String(remainingPercent.toFixed(2)) +
|
|
1863
1541
|
"% (" +
|
|
1864
1542
|
band +
|
|
1865
|
-
"). Consider
|
|
1866
|
-
await appendJsonLine(warningsFile, {
|
|
1867
|
-
ts: now.toISOString(),
|
|
1868
|
-
harness: runtime.harness,
|
|
1869
|
-
band,
|
|
1870
|
-
remainingPercent,
|
|
1871
|
-
note
|
|
1872
|
-
});
|
|
1543
|
+
"). Consider leaving a handoff note or compacting soon.";
|
|
1873
1544
|
emitAdvisoryContext(runtime, "context-monitor", note);
|
|
1874
1545
|
process.stderr.write("[cclaw] " + note + "\\n");
|
|
1875
1546
|
nextAdvisoryBand = band;
|
|
@@ -1920,7 +1591,8 @@ async function handleVerifyCurrentState(runtime) {
|
|
|
1920
1591
|
function normalizeHookName(rawName) {
|
|
1921
1592
|
const value = normalizeText(rawName).toLowerCase();
|
|
1922
1593
|
if (value === "session-start") return "session-start";
|
|
1923
|
-
if (value === "stop-
|
|
1594
|
+
if (value === "stop-handoff") return "stop-handoff";
|
|
1595
|
+
if (value === "stop-checkpoint") return "stop-handoff";
|
|
1924
1596
|
if (value === "pre-compact") return "pre-compact";
|
|
1925
1597
|
if (value === "prompt-guard") return "prompt-guard";
|
|
1926
1598
|
if (value === "workflow-guard") return "workflow-guard";
|
|
@@ -1935,7 +1607,7 @@ async function main() {
|
|
|
1935
1607
|
process.stderr.write(
|
|
1936
1608
|
"[cclaw] run-hook: usage: node " +
|
|
1937
1609
|
RUNTIME_ROOT +
|
|
1938
|
-
"/hooks/run-hook.mjs <session-start|stop-
|
|
1610
|
+
"/hooks/run-hook.mjs <session-start|stop-handoff|pre-compact|prompt-guard|workflow-guard|context-monitor|verify-current-state>\\n"
|
|
1939
1611
|
);
|
|
1940
1612
|
process.exitCode = 1;
|
|
1941
1613
|
return;
|
|
@@ -1967,8 +1639,8 @@ async function main() {
|
|
|
1967
1639
|
process.exitCode = await handleSessionStart(runtime);
|
|
1968
1640
|
return;
|
|
1969
1641
|
}
|
|
1970
|
-
if (hookName === "stop-
|
|
1971
|
-
process.exitCode = await
|
|
1642
|
+
if (hookName === "stop-handoff") {
|
|
1643
|
+
process.exitCode = await handleStopHandoff(runtime);
|
|
1972
1644
|
return;
|
|
1973
1645
|
}
|
|
1974
1646
|
if (hookName === "pre-compact") {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
2
|
import { META_SKILL_NAME } from "./meta-skill.js";
|
|
3
3
|
export function opencodePluginJs(_options = {}) {
|
|
4
|
-
return `// cclaw OpenCode plugin — generated by cclaw sync
|
|
4
|
+
return `// cclaw OpenCode plugin — generated by npx cclaw-cli sync
|
|
5
5
|
import { appendFileSync, existsSync, mkdirSync } from "node:fs";
|
|
6
6
|
import { readFile, stat } from "node:fs/promises";
|
|
7
7
|
import { join } from "node:path";
|
|
@@ -14,14 +14,7 @@ export default function cclawPlugin(ctx) {
|
|
|
14
14
|
const pluginLogPath = join(logsDir, "opencode-plugin.log");
|
|
15
15
|
const configPath = join(runtimeDir, "config.yaml");
|
|
16
16
|
const flowStatePath = join(stateDir, "flow-state.json");
|
|
17
|
-
const checkpointPath = join(stateDir, "checkpoint.json");
|
|
18
|
-
const activityPath = join(stateDir, "stage-activity.jsonl");
|
|
19
|
-
const contextWarningsPath = join(stateDir, "context-warnings.jsonl");
|
|
20
|
-
const contextModePath = join(stateDir, "context-mode.json");
|
|
21
|
-
const contextsDir = join(runtimeDir, "contexts");
|
|
22
|
-
const sessionDigestPath = join(stateDir, "session-digest.md");
|
|
23
17
|
const knowledgePath = join(runtimeDir, "knowledge.jsonl");
|
|
24
|
-
const knowledgeDigestPath = join(stateDir, "knowledge-digest.md");
|
|
25
18
|
const metaSkillPath = join(runtimeDir, "skills/${META_SKILL_NAME}/SKILL.md");
|
|
26
19
|
|
|
27
20
|
function ensureRuntimeDirs() {
|
|
@@ -86,77 +79,8 @@ export default function cclawPlugin(ctx) {
|
|
|
86
79
|
return text.split(/\\r?\\n/).slice(-maxLines);
|
|
87
80
|
}
|
|
88
81
|
|
|
89
|
-
async function readCheckpointSummary() {
|
|
90
|
-
try {
|
|
91
|
-
const raw = await readFileText(checkpointPath);
|
|
92
|
-
if (!raw) return "";
|
|
93
|
-
const cp = JSON.parse(raw);
|
|
94
|
-
return \`Checkpoint: stage=\${cp.stage || "none"}, status=\${cp.status || "unknown"}, run=\${cp.runId || "none"}, at=\${cp.timestamp || "unknown"}\`;
|
|
95
|
-
} catch {
|
|
96
|
-
return "";
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
async function readContextMode() {
|
|
101
|
-
let mode = "default";
|
|
102
|
-
try {
|
|
103
|
-
const parsed = JSON.parse(await readFileText(contextModePath));
|
|
104
|
-
if (parsed && typeof parsed.activeMode === "string" && parsed.activeMode.trim().length > 0) {
|
|
105
|
-
mode = parsed.activeMode.trim();
|
|
106
|
-
}
|
|
107
|
-
} catch {
|
|
108
|
-
// keep default
|
|
109
|
-
}
|
|
110
|
-
const guidePath = join(contextsDir, mode + ".md");
|
|
111
|
-
const guide = existsSync(guidePath) ? "${RUNTIME_ROOT}/contexts/" + mode + ".md" : "";
|
|
112
|
-
return { mode, guide };
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
async function readRecentActivity() {
|
|
116
|
-
try {
|
|
117
|
-
const lines = await readTailLines(activityPath, 5);
|
|
118
|
-
if (lines.length === 0) return [];
|
|
119
|
-
return lines
|
|
120
|
-
.map((line) => {
|
|
121
|
-
try {
|
|
122
|
-
return JSON.parse(line);
|
|
123
|
-
} catch {
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
})
|
|
127
|
-
.filter(Boolean)
|
|
128
|
-
.map((entry) => \`- \${entry.ts || "unknown"} [\${entry.phase || "unknown"}] \${entry.tool || "unknown"} (stage=\${entry.stage || "unknown"}, run=\${entry.runId || "none"})\`);
|
|
129
|
-
} catch {
|
|
130
|
-
return [];
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
async function readLatestContextWarning() {
|
|
135
|
-
try {
|
|
136
|
-
const line = (await readTailLines(contextWarningsPath, 1))[0];
|
|
137
|
-
if (!line) return "";
|
|
138
|
-
try {
|
|
139
|
-
const parsed = JSON.parse(line);
|
|
140
|
-
if (parsed && typeof parsed.note === "string") return parsed.note;
|
|
141
|
-
} catch {
|
|
142
|
-
// non-json fallback
|
|
143
|
-
}
|
|
144
|
-
return line;
|
|
145
|
-
} catch {
|
|
146
|
-
return "";
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
82
|
async function readKnowledgeDigest() {
|
|
151
|
-
|
|
152
|
-
if (!digest) {
|
|
153
|
-
return readTailLines(knowledgePath, 12);
|
|
154
|
-
}
|
|
155
|
-
return digest
|
|
156
|
-
.split(/\\r?\\n/)
|
|
157
|
-
.map((line) => line.trim())
|
|
158
|
-
.filter((line) => line.length > 0)
|
|
159
|
-
.filter((line) => !line.startsWith("#"));
|
|
83
|
+
return readTailLines(knowledgePath, 12);
|
|
160
84
|
}
|
|
161
85
|
|
|
162
86
|
const BOOTSTRAP_MARKER = "<!-- cclaw-bootstrap-v1 -->";
|
|
@@ -167,30 +91,14 @@ export default function cclawPlugin(ctx) {
|
|
|
167
91
|
BOOTSTRAP_MARKER,
|
|
168
92
|
\`cclaw loaded. Flow: stage=\${flow.stage} (\${flow.completed}/8 completed, run=\${flow.activeRunId}). Active artifacts: ${RUNTIME_ROOT}/artifacts/\`
|
|
169
93
|
];
|
|
170
|
-
const contextMode = await readContextMode();
|
|
171
|
-
parts.push(
|
|
172
|
-
contextMode.guide
|
|
173
|
-
? \`Context mode: \${contextMode.mode} (guide: \${contextMode.guide})\`
|
|
174
|
-
: \`Context mode: \${contextMode.mode}\`
|
|
175
|
-
);
|
|
176
|
-
|
|
177
|
-
const checkpoint = await readCheckpointSummary();
|
|
178
|
-
if (checkpoint) parts.push(checkpoint);
|
|
179
|
-
|
|
180
|
-
const digest = (await readFileText(sessionDigestPath)).trim();
|
|
181
|
-
if (digest) parts.push("Last session:", digest);
|
|
182
94
|
|
|
183
|
-
const activity = await readRecentActivity();
|
|
184
|
-
if (activity.length > 0) parts.push("Recent stage activity:", ...activity);
|
|
185
95
|
|
|
186
|
-
const warning = await readLatestContextWarning();
|
|
187
|
-
if (warning) parts.push("Latest context warning:", warning);
|
|
188
96
|
|
|
189
97
|
const knowledge = await readKnowledgeDigest();
|
|
190
98
|
if (knowledge.length > 0) parts.push("Knowledge digest (top relevant entries):", ...knowledge);
|
|
191
99
|
|
|
192
100
|
parts.push(
|
|
193
|
-
"If you discover a non-obvious rule or pattern,
|
|
101
|
+
"If you discover a non-obvious rule or pattern during stage work, add it to the current artifact ## Learnings section; stage-complete harvests it into .cclaw/knowledge.jsonl. Direct JSONL append is only for explicit manual learnings operations."
|
|
194
102
|
);
|
|
195
103
|
|
|
196
104
|
const meta = (await readFileText(metaSkillPath)).trim();
|
|
@@ -203,13 +111,7 @@ export default function cclawPlugin(ctx) {
|
|
|
203
111
|
let bootstrapRefreshPromise = null;
|
|
204
112
|
const BOOTSTRAP_SOURCE_PATHS = [
|
|
205
113
|
flowStatePath,
|
|
206
|
-
checkpointPath,
|
|
207
|
-
activityPath,
|
|
208
|
-
contextWarningsPath,
|
|
209
|
-
contextModePath,
|
|
210
|
-
sessionDigestPath,
|
|
211
114
|
knowledgePath,
|
|
212
|
-
knowledgeDigestPath,
|
|
213
115
|
metaSkillPath
|
|
214
116
|
];
|
|
215
117
|
|
|
@@ -684,7 +586,7 @@ export default function cclawPlugin(ctx) {
|
|
|
684
586
|
await refreshBootstrapCache(true);
|
|
685
587
|
}
|
|
686
588
|
if (eventType === "session.idle") {
|
|
687
|
-
await runHookScript("stop-
|
|
589
|
+
await runHookScript("stop-handoff", { loop_count: 0 });
|
|
688
590
|
}
|
|
689
591
|
},
|
|
690
592
|
"tool.execute.before": async (input, output) => {
|
|
@@ -744,7 +646,7 @@ export default function cclawPlugin(ctx) {
|
|
|
744
646
|
throw new Error(
|
|
745
647
|
"cclaw " + failed + " blocked tool.execute.before.\\n" +
|
|
746
648
|
"Reason: " + detail + "\\n" +
|
|
747
|
-
"Diagnose: run \`cclaw doctor\` in project root.\\n" +
|
|
649
|
+
"Diagnose: run \`npx cclaw-cli doctor\` in project root.\\n" +
|
|
748
650
|
"Bypass (temporary): export CCLAW_DISABLE=1 before starting OpenCode,\\n" +
|
|
749
651
|
"or set \`strictness: advisory\` in .cclaw/config.yaml."
|
|
750
652
|
);
|