cclaw-cli 0.48.35 → 0.51.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 +54 -82
- 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 -495
- package/dist/constants.d.ts +2 -13
- package/dist/constants.js +1 -46
- package/dist/content/closeout-guidance.d.ts +14 -0
- package/dist/content/closeout-guidance.js +42 -0
- package/dist/content/core-agents.js +51 -9
- 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 +2 -0
- package/dist/content/ideate-command.d.ts +2 -0
- package/dist/content/ideate-command.js +31 -25
- package/dist/content/iron-laws.d.ts +5 -5
- package/dist/content/iron-laws.js +5 -5
- package/dist/content/learnings.d.ts +3 -4
- package/dist/content/learnings.js +24 -50
- package/dist/content/meta-skill.js +31 -24
- package/dist/content/next-command.js +38 -38
- package/dist/content/node-hooks.js +17 -343
- package/dist/content/opencode-plugin.js +2 -100
- 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 +14 -46
- package/dist/content/skills.d.ts +0 -5
- package/dist/content/skills.js +53 -128
- package/dist/content/stage-common-guidance.d.ts +0 -1
- package/dist/content/stage-common-guidance.js +15 -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 +10 -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 +47 -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 -1533
- package/dist/content/view-command.js +29 -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 -408
- 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 +48 -57
- package/dist/install.js +128 -520
- 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 -109
- 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 -9
- package/dist/content/contexts.js +0 -65
- 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,11 +1064,9 @@ async function handleStopCheckpoint(runtime) {
|
|
|
1272
1064
|
state.currentStage +
|
|
1273
1065
|
", run=" +
|
|
1274
1066
|
state.activeRunId +
|
|
1275
|
-
").
|
|
1067
|
+
"). Active artifacts stay in " +
|
|
1276
1068
|
RUNTIME_ROOT +
|
|
1277
|
-
"/
|
|
1278
|
-
RUNTIME_ROOT +
|
|
1279
|
-
"/artifacts until /cc-ops archive (or cclaw archive runtime). Before stopping: (1) confirm flow-state reflects reality, (2) ensure artifact changes match current feature intent, (3) if you discovered a non-obvious rule/pattern, append one strict-schema JSON line to " +
|
|
1069
|
+
"/artifacts until cclaw 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, append one strict-schema JSON line to " +
|
|
1280
1070
|
RUNTIME_ROOT +
|
|
1281
1071
|
"/knowledge.jsonl, (4) commit or revert pending changes.";
|
|
1282
1072
|
|
|
@@ -1293,116 +1083,7 @@ async function handleStopCheckpoint(runtime) {
|
|
|
1293
1083
|
return 0;
|
|
1294
1084
|
}
|
|
1295
1085
|
|
|
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");
|
|
1086
|
+
async function handlePreCompact(_runtime) {
|
|
1406
1087
|
return 0;
|
|
1407
1088
|
}
|
|
1408
1089
|
|
|
@@ -1781,7 +1462,6 @@ async function handleWorkflowGuard(runtime) {
|
|
|
1781
1462
|
async function handleContextMonitor(runtime) {
|
|
1782
1463
|
const stateDir = path.join(runtime.root, RUNTIME_ROOT, "state");
|
|
1783
1464
|
const monitorStateFile = path.join(stateDir, "context-monitor.json");
|
|
1784
|
-
const warningsFile = path.join(stateDir, "context-warnings.jsonl");
|
|
1785
1465
|
const autoEvidenceFile = path.join(stateDir, "tdd-red-evidence.jsonl");
|
|
1786
1466
|
const flowState = await readFlowState(runtime.root);
|
|
1787
1467
|
|
|
@@ -1862,14 +1542,7 @@ async function handleContextMonitor(runtime) {
|
|
|
1862
1542
|
String(remainingPercent.toFixed(2)) +
|
|
1863
1543
|
"% (" +
|
|
1864
1544
|
band +
|
|
1865
|
-
"). Consider
|
|
1866
|
-
await appendJsonLine(warningsFile, {
|
|
1867
|
-
ts: now.toISOString(),
|
|
1868
|
-
harness: runtime.harness,
|
|
1869
|
-
band,
|
|
1870
|
-
remainingPercent,
|
|
1871
|
-
note
|
|
1872
|
-
});
|
|
1545
|
+
"). Consider leaving a handoff note or compacting soon.";
|
|
1873
1546
|
emitAdvisoryContext(runtime, "context-monitor", note);
|
|
1874
1547
|
process.stderr.write("[cclaw] " + note + "\\n");
|
|
1875
1548
|
nextAdvisoryBand = band;
|
|
@@ -1920,7 +1593,8 @@ async function handleVerifyCurrentState(runtime) {
|
|
|
1920
1593
|
function normalizeHookName(rawName) {
|
|
1921
1594
|
const value = normalizeText(rawName).toLowerCase();
|
|
1922
1595
|
if (value === "session-start") return "session-start";
|
|
1923
|
-
if (value === "stop-
|
|
1596
|
+
if (value === "stop-handoff") return "stop-handoff";
|
|
1597
|
+
if (value === "stop-checkpoint") return "stop-handoff";
|
|
1924
1598
|
if (value === "pre-compact") return "pre-compact";
|
|
1925
1599
|
if (value === "prompt-guard") return "prompt-guard";
|
|
1926
1600
|
if (value === "workflow-guard") return "workflow-guard";
|
|
@@ -1935,7 +1609,7 @@ async function main() {
|
|
|
1935
1609
|
process.stderr.write(
|
|
1936
1610
|
"[cclaw] run-hook: usage: node " +
|
|
1937
1611
|
RUNTIME_ROOT +
|
|
1938
|
-
"/hooks/run-hook.mjs <session-start|stop-
|
|
1612
|
+
"/hooks/run-hook.mjs <session-start|stop-handoff|pre-compact|prompt-guard|workflow-guard|context-monitor|verify-current-state>\\n"
|
|
1939
1613
|
);
|
|
1940
1614
|
process.exitCode = 1;
|
|
1941
1615
|
return;
|
|
@@ -1967,8 +1641,8 @@ async function main() {
|
|
|
1967
1641
|
process.exitCode = await handleSessionStart(runtime);
|
|
1968
1642
|
return;
|
|
1969
1643
|
}
|
|
1970
|
-
if (hookName === "stop-
|
|
1971
|
-
process.exitCode = await
|
|
1644
|
+
if (hookName === "stop-handoff") {
|
|
1645
|
+
process.exitCode = await handleStopHandoff(runtime);
|
|
1972
1646
|
return;
|
|
1973
1647
|
}
|
|
1974
1648
|
if (hookName === "pre-compact") {
|
|
@@ -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,24 +91,8 @@ 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);
|
|
@@ -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) => {
|