smart-claude-memory-mcp 2.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/.claude-plugin/plugin.json +38 -0
- package/CHANGELOG.md +52 -0
- package/LICENSE +21 -0
- package/README.md +790 -0
- package/dist/chunker.js +33 -0
- package/dist/chunker.js.map +1 -0
- package/dist/config.js +23 -0
- package/dist/config.js.map +1 -0
- package/dist/curriculum/daemon.js +190 -0
- package/dist/curriculum/daemon.js.map +1 -0
- package/dist/curriculum/scanner.js +237 -0
- package/dist/curriculum/scanner.js.map +1 -0
- package/dist/index.js +429 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/migrations.js +128 -0
- package/dist/lib/migrations.js.map +1 -0
- package/dist/ollama.js +59 -0
- package/dist/ollama.js.map +1 -0
- package/dist/project-detect.js +102 -0
- package/dist/project-detect.js.map +1 -0
- package/dist/project.js +26 -0
- package/dist/project.js.map +1 -0
- package/dist/sleep/daemon.js +215 -0
- package/dist/sleep/daemon.js.map +1 -0
- package/dist/sleep/miner.js +285 -0
- package/dist/sleep/miner.js.map +1 -0
- package/dist/supabase.js +405 -0
- package/dist/supabase.js.map +1 -0
- package/dist/telemetry/emit.js +19 -0
- package/dist/telemetry/emit.js.map +1 -0
- package/dist/telemetry/pruner.js +141 -0
- package/dist/telemetry/pruner.js.map +1 -0
- package/dist/telemetry/types.js +2 -0
- package/dist/telemetry/types.js.map +1 -0
- package/dist/tools/backlog.js +599 -0
- package/dist/tools/backlog.js.map +1 -0
- package/dist/tools/batch-freeze-patterns.js +243 -0
- package/dist/tools/batch-freeze-patterns.js.map +1 -0
- package/dist/tools/bloat-audit.js +101 -0
- package/dist/tools/bloat-audit.js.map +1 -0
- package/dist/tools/checkpoint.js +259 -0
- package/dist/tools/checkpoint.js.map +1 -0
- package/dist/tools/compact.js +60 -0
- package/dist/tools/compact.js.map +1 -0
- package/dist/tools/conflict.js +102 -0
- package/dist/tools/conflict.js.map +1 -0
- package/dist/tools/curriculum.js +225 -0
- package/dist/tools/curriculum.js.map +1 -0
- package/dist/tools/frozen-cache.js +106 -0
- package/dist/tools/frozen-cache.js.map +1 -0
- package/dist/tools/health.js +368 -0
- package/dist/tools/health.js.map +1 -0
- package/dist/tools/hygiene.js +309 -0
- package/dist/tools/hygiene.js.map +1 -0
- package/dist/tools/image.js +107 -0
- package/dist/tools/image.js.map +1 -0
- package/dist/tools/list-global-patterns.js +101 -0
- package/dist/tools/list-global-patterns.js.map +1 -0
- package/dist/tools/orchestrator.js +113 -0
- package/dist/tools/orchestrator.js.map +1 -0
- package/dist/tools/policy.js +90 -0
- package/dist/tools/policy.js.map +1 -0
- package/dist/tools/refactor.js +220 -0
- package/dist/tools/refactor.js.map +1 -0
- package/dist/tools/save.js +42 -0
- package/dist/tools/save.js.map +1 -0
- package/dist/tools/search.js +189 -0
- package/dist/tools/search.js.map +1 -0
- package/dist/tools/setup.js +868 -0
- package/dist/tools/setup.js.map +1 -0
- package/dist/tools/shared-schemas.js +24 -0
- package/dist/tools/shared-schemas.js.map +1 -0
- package/dist/tools/skills.js +174 -0
- package/dist/tools/skills.js.map +1 -0
- package/dist/tools/sleep.js +239 -0
- package/dist/tools/sleep.js.map +1 -0
- package/dist/tools/sovereign-constitution.js +319 -0
- package/dist/tools/sovereign-constitution.js.map +1 -0
- package/dist/tools/summarize.js +55 -0
- package/dist/tools/summarize.js.map +1 -0
- package/dist/tools/sync.js +255 -0
- package/dist/tools/sync.js.map +1 -0
- package/dist/tools/system_dashboard.js +181 -0
- package/dist/tools/system_dashboard.js.map +1 -0
- package/dist/tools/update-rule.js +15 -0
- package/dist/tools/update-rule.js.map +1 -0
- package/dist/tools/verification.js +333 -0
- package/dist/tools/verification.js.map +1 -0
- package/dist/trajectory/daemon.js +270 -0
- package/dist/trajectory/daemon.js.map +1 -0
- package/dist/trajectory/stripper.js +124 -0
- package/dist/trajectory/stripper.js.map +1 -0
- package/dist/trajectory/summarizer.js +77 -0
- package/dist/trajectory/summarizer.js.map +1 -0
- package/dist/transactions/checkpoint.js +272 -0
- package/dist/transactions/checkpoint.js.map +1 -0
- package/dist/verification-gate.js +43 -0
- package/dist/verification-gate.js.map +1 -0
- package/dist/version.js +16 -0
- package/dist/version.js.map +1 -0
- package/hooks/README.md +54 -0
- package/hooks/md-policy.py +497 -0
- package/marketplace.json +13 -0
- package/package.json +66 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// MCP tool handlers for Trajectory Compaction (Agentic OS 2026 / AgentDiet).
|
|
2
|
+
//
|
|
3
|
+
// Two tools:
|
|
4
|
+
// * compact_trajectory — manual entry to the compaction pipeline.
|
|
5
|
+
// With chunk_id: target one row. Without:
|
|
6
|
+
// run one daemon tick over the next batch.
|
|
7
|
+
// * get_trajectory_summary — read-back via the get_trajectory_summary RPC.
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
import { supabase } from "../supabase.js";
|
|
10
|
+
import { compactOneChunk, runCompactionOnce, } from "../trajectory/daemon.js";
|
|
11
|
+
// ─── compact_trajectory ─────────────────────────────────────────────────────
|
|
12
|
+
export const compactTrajectoryInput = z.object({
|
|
13
|
+
chunk_id: z.number().int().positive().optional(),
|
|
14
|
+
dry_run: z.boolean().optional().default(false),
|
|
15
|
+
batch: z.number().int().positive().max(100).optional(),
|
|
16
|
+
});
|
|
17
|
+
export const compactTrajectoryInputShape = {
|
|
18
|
+
chunk_id: z.number().int().positive().optional(),
|
|
19
|
+
dry_run: z.boolean().optional().default(false),
|
|
20
|
+
batch: z.number().int().positive().max(100).optional(),
|
|
21
|
+
};
|
|
22
|
+
export async function compactTrajectoryHandler(args) {
|
|
23
|
+
const dryRun = args.dry_run ?? false;
|
|
24
|
+
if (args.chunk_id !== undefined) {
|
|
25
|
+
const r = await compactOneChunk(args.chunk_id, { dryRun });
|
|
26
|
+
return { mode: "single", chunk_id: args.chunk_id, ...r };
|
|
27
|
+
}
|
|
28
|
+
const limit = args.batch ?? 25;
|
|
29
|
+
const r = await runCompactionOnce({ limit, dryRun });
|
|
30
|
+
return { mode: "batch", ...r };
|
|
31
|
+
}
|
|
32
|
+
// ─── get_trajectory_summary ─────────────────────────────────────────────────
|
|
33
|
+
export const getTrajectorySummaryInput = z.object({
|
|
34
|
+
chunk_id: z.number().int().positive(),
|
|
35
|
+
});
|
|
36
|
+
export const getTrajectorySummaryInputShape = {
|
|
37
|
+
chunk_id: z.number().int().positive(),
|
|
38
|
+
};
|
|
39
|
+
export async function getTrajectorySummaryHandler(args) {
|
|
40
|
+
const { data, error } = await supabase.rpc("get_trajectory_summary", {
|
|
41
|
+
p_chunk_id: args.chunk_id,
|
|
42
|
+
});
|
|
43
|
+
if (error) {
|
|
44
|
+
throw new Error(`get_trajectory_summary RPC failed: ${error.message}`);
|
|
45
|
+
}
|
|
46
|
+
const rows = (data ?? []);
|
|
47
|
+
if (rows.length === 0)
|
|
48
|
+
return { found: false };
|
|
49
|
+
const row = rows[0];
|
|
50
|
+
return {
|
|
51
|
+
found: true,
|
|
52
|
+
summary: row.summary,
|
|
53
|
+
source_tokens: row.source_tokens,
|
|
54
|
+
summary_tokens: row.summary_tokens,
|
|
55
|
+
compression_ratio: row.compression_ratio,
|
|
56
|
+
model: row.model,
|
|
57
|
+
created_at: row.created_at,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=compact.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compact.js","sourceRoot":"","sources":["../../src/tools/compact.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,EAAE;AACF,aAAa;AACb,0EAA0E;AAC1E,yEAAyE;AACzE,0EAA0E;AAC1E,+EAA+E;AAE/E,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EACL,eAAe,EACf,iBAAiB,GAElB,MAAM,yBAAyB,CAAC;AAEjC,+EAA+E;AAE/E,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC7C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAChD,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAC9C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;CACvD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,2BAA2B,GAAG;IACzC,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAChD,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAC9C,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;CACvD,CAAC;AAcF,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,IAA2B;IAE3B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC;IACrC,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,CAAC,GAAG,MAAM,eAAe,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;QAC3D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC;IAC3D,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;IAC/B,MAAM,CAAC,GAAG,MAAM,iBAAiB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IACrD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,CAAC,EAAE,CAAC;AACjC,CAAC;AAED,+EAA+E;AAE/E,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,CAAC,MAAM,CAAC;IAChD,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;CACtC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,8BAA8B,GAAG;IAC5C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;CACtC,CAAC;AAyBF,MAAM,CAAC,KAAK,UAAU,2BAA2B,CAC/C,IAA8B;IAE9B,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,MAAM,QAAQ,CAAC,GAAG,CAAC,wBAAwB,EAAE;QACnE,UAAU,EAAE,IAAI,CAAC,QAAQ;KAC1B,CAAC,CAAC;IACH,IAAI,KAAK,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,sCAAsC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACzE,CAAC;IACD,MAAM,IAAI,GAAG,CAAC,IAAI,IAAI,EAAE,CAAa,CAAC;IACtC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;IAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAE,CAAC;IACrB,OAAO;QACL,KAAK,EAAE,IAAI;QACX,OAAO,EAAE,GAAG,CAAC,OAAO;QACpB,aAAa,EAAE,GAAG,CAAC,aAAa;QAChC,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,iBAAiB,EAAE,GAAG,CAAC,iBAAiB;QACxC,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,UAAU,EAAE,GAAG,CAAC,UAAU;KAC3B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { embed, chat } from "../ollama.js";
|
|
2
|
+
import { searchChunks } from "../supabase.js";
|
|
3
|
+
import { currentProjectId } from "../project.js";
|
|
4
|
+
function parseJsonBlock(text) {
|
|
5
|
+
// Strip common code-fence wrappers and grab the first JSON object.
|
|
6
|
+
const cleaned = text.replace(/```(?:json)?/gi, "").trim();
|
|
7
|
+
const start = cleaned.indexOf("{");
|
|
8
|
+
const end = cleaned.lastIndexOf("}");
|
|
9
|
+
if (start === -1 || end === -1)
|
|
10
|
+
throw new Error("no JSON object in LLM response");
|
|
11
|
+
return JSON.parse(cleaned.slice(start, end + 1));
|
|
12
|
+
}
|
|
13
|
+
async function rerank(query, hits, model) {
|
|
14
|
+
if (hits.length === 0)
|
|
15
|
+
return hits;
|
|
16
|
+
const prompt = [
|
|
17
|
+
{
|
|
18
|
+
role: "system",
|
|
19
|
+
content: "You are a precise retrieval re-ranker. Score each passage from 0.0 to 1.0 for how directly it answers the query. Return ONLY compact JSON: {\"scores\":[<float>,...]} in the same order as input passages. No prose.",
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
role: "user",
|
|
23
|
+
content: `Query: ${query}\n\nPassages:\n` +
|
|
24
|
+
hits
|
|
25
|
+
.map((h, i) => `[${i}] (sim=${h.similarity.toFixed(3)})\n${h.content.slice(0, 500)}`)
|
|
26
|
+
.join("\n\n"),
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
try {
|
|
30
|
+
const out = await chat(prompt, { model, temperature: 0, timeoutMs: 30_000 });
|
|
31
|
+
const parsed = parseJsonBlock(out);
|
|
32
|
+
const scored = hits.map((h, i) => ({
|
|
33
|
+
...h,
|
|
34
|
+
relevance_score: typeof parsed.scores?.[i] === "number" ? parsed.scores[i] : h.similarity,
|
|
35
|
+
}));
|
|
36
|
+
scored.sort((a, b) => (b.relevance_score ?? 0) - (a.relevance_score ?? 0));
|
|
37
|
+
return scored;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Fall back silently to vector order if the LLM misbehaves.
|
|
41
|
+
return hits;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function detectConflict(proposedChange, rule, model) {
|
|
45
|
+
const prompt = [
|
|
46
|
+
{
|
|
47
|
+
role: "system",
|
|
48
|
+
content: "You check whether a proposed code or behavior change violates a governance rule. Respond with ONLY compact JSON: " +
|
|
49
|
+
'{"has_conflict": bool, "severity": "none"|"low"|"medium"|"high", "reason": "<one sentence>"}. ' +
|
|
50
|
+
"Set has_conflict=false and severity=none if the change is neutral or complementary to the rule.",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
role: "user",
|
|
54
|
+
content: `Rule:\n${rule}\n\nProposed change:\n${proposedChange}`,
|
|
55
|
+
},
|
|
56
|
+
];
|
|
57
|
+
try {
|
|
58
|
+
const out = await chat(prompt, { model, temperature: 0, timeoutMs: 30_000 });
|
|
59
|
+
const parsed = parseJsonBlock(out);
|
|
60
|
+
return {
|
|
61
|
+
has_conflict: Boolean(parsed.has_conflict),
|
|
62
|
+
severity: parsed.severity ?? "none",
|
|
63
|
+
reason: parsed.reason ?? "",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
return { has_conflict: false, severity: "none", reason: `(llm-error: ${e.message})` };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
export async function checkRuleConflicts(args) {
|
|
71
|
+
const projectId = args.project_id ?? currentProjectId;
|
|
72
|
+
const topK = Math.min(args.top_k ?? 5, 10);
|
|
73
|
+
const [qVec] = await embed([args.proposed_change]);
|
|
74
|
+
const vectorHits = await searchChunks(projectId, qVec, topK * 2, 0);
|
|
75
|
+
const asHits = vectorHits.map((h) => ({ ...h }));
|
|
76
|
+
const maybeReranked = args.rerank === false ? asHits : await rerank(args.proposed_change, asHits, args.llm_model);
|
|
77
|
+
const top = maybeReranked.slice(0, topK);
|
|
78
|
+
// Analyze the top 3 for intent conflicts (keeps latency bounded).
|
|
79
|
+
const analyze = top.slice(0, 3);
|
|
80
|
+
for (const hit of analyze) {
|
|
81
|
+
hit.conflict = await detectConflict(args.proposed_change, hit.content, args.llm_model);
|
|
82
|
+
}
|
|
83
|
+
const highest = analyze
|
|
84
|
+
.map((h) => h.conflict?.severity ?? "none")
|
|
85
|
+
.reduce((acc, s) => {
|
|
86
|
+
const order = { none: 0, low: 1, medium: 2, high: 3 };
|
|
87
|
+
return order[s] > order[acc] ? s : acc;
|
|
88
|
+
}, "none");
|
|
89
|
+
return {
|
|
90
|
+
project_id: projectId,
|
|
91
|
+
proposed_change: args.proposed_change,
|
|
92
|
+
reranked: args.rerank !== false,
|
|
93
|
+
top,
|
|
94
|
+
highest_severity: highest,
|
|
95
|
+
recommendation: highest === "high"
|
|
96
|
+
? "STOP — a high-severity conflict was detected. Review the flagged rule before proceeding."
|
|
97
|
+
: highest === "medium"
|
|
98
|
+
? "Proceed with caution — at least one medium-severity conflict was flagged."
|
|
99
|
+
: "No significant conflicts detected.",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=conflict.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conflict.js","sourceRoot":"","sources":["../../src/tools/conflict.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
// MCP tool wrappers for M5 Autonomous Curriculum
|
|
2
|
+
// (Agentic OS 2026 / Mission 5 / SCM-S21-D1).
|
|
3
|
+
//
|
|
4
|
+
// Four handlers. The daemon is a deterministic queuer; these tools are the
|
|
5
|
+
// Orchestrator's interface to the queue:
|
|
6
|
+
//
|
|
7
|
+
// * list_curriculum_tasks — inspection / dashboards.
|
|
8
|
+
// * pull_curriculum_task — atomic claim via pull_next_curriculum_task RPC
|
|
9
|
+
// (FOR UPDATE SKIP LOCKED). The Orchestrator's
|
|
10
|
+
// entry point for picking up work.
|
|
11
|
+
// * apply_curriculum_task — verification-gated finalize. Asserts the
|
|
12
|
+
// verification-pending.json gate is CLEAR
|
|
13
|
+
// BEFORE calling the apply_curriculum_task RPC.
|
|
14
|
+
// The RPC itself does the atomic SQL work
|
|
15
|
+
// (status flip + optional auto-promote).
|
|
16
|
+
// * reject_curriculum_task — manual veto (status='rejected').
|
|
17
|
+
//
|
|
18
|
+
// Boundary Invariant #2 (ARCHITECTURE.md §4.7): the auto-promote path lives
|
|
19
|
+
// only inside the SQL RPC — this tool surface does NOT call
|
|
20
|
+
// promote_candidate_to_skill directly. Audit by greping:
|
|
21
|
+
// grep -rn promote_candidate_to_skill src/
|
|
22
|
+
// Should yield exactly ONE TS call site — sleep.ts (manual M3 promotion).
|
|
23
|
+
// Curriculum.ts must NOT appear.
|
|
24
|
+
//
|
|
25
|
+
// Style mirrors src/tools/sleep.ts and src/tools/checkpoint.ts.
|
|
26
|
+
import { z } from "zod";
|
|
27
|
+
import { supabase } from "../supabase.js";
|
|
28
|
+
import { currentProjectId } from "../project.js";
|
|
29
|
+
import { getPending } from "../verification-gate.js";
|
|
30
|
+
import { recordVerified, recordRejected } from "../curriculum/daemon.js";
|
|
31
|
+
// ─── shared project_id helper (mirrors sleep.ts / checkpoint.ts) ──────────
|
|
32
|
+
function resolveProjectId(explicit) {
|
|
33
|
+
if (typeof explicit === "string" && explicit.trim().length > 0) {
|
|
34
|
+
return explicit;
|
|
35
|
+
}
|
|
36
|
+
return currentProjectId;
|
|
37
|
+
}
|
|
38
|
+
// ─── list_curriculum_tasks ────────────────────────────────────────────────
|
|
39
|
+
export const listCurriculumTasksInputShape = {
|
|
40
|
+
project_id: z
|
|
41
|
+
.string()
|
|
42
|
+
.optional()
|
|
43
|
+
.describe(`Project namespace filter. Defaults to the slugified current working directory ('${currentProjectId}').`),
|
|
44
|
+
status: z
|
|
45
|
+
.enum(["queued", "pulled", "attempted", "verified", "rejected", "expired"])
|
|
46
|
+
.optional()
|
|
47
|
+
.describe("Filter by task lifecycle state. Omit to return all states (capped by limit)."),
|
|
48
|
+
kind: z
|
|
49
|
+
.enum(["test_gap", "refactor", "rollback_repro"])
|
|
50
|
+
.optional()
|
|
51
|
+
.describe("Filter by signal source: test_gap (low coverage), refactor (stale skill candidate), rollback_repro (M4 rollback hotspot)."),
|
|
52
|
+
limit: z
|
|
53
|
+
.number()
|
|
54
|
+
.int()
|
|
55
|
+
.positive()
|
|
56
|
+
.max(100)
|
|
57
|
+
.optional()
|
|
58
|
+
.default(20)
|
|
59
|
+
.describe("Maximum rows to return. Default 20, hard cap 100."),
|
|
60
|
+
};
|
|
61
|
+
export async function listCurriculumTasks(args) {
|
|
62
|
+
const projectId = resolveProjectId(args.project_id);
|
|
63
|
+
const limit = args.limit ?? 20;
|
|
64
|
+
let q = supabase
|
|
65
|
+
.from("curriculum_tasks")
|
|
66
|
+
.select("id, project_id, kind, target_path, rationale, signal_source, " +
|
|
67
|
+
"linked_candidate_id, linked_checkpoint_id, status, rejection_reason, " +
|
|
68
|
+
"pulled_by_session_id, pulled_at, verified_at, expires_at, created_at, updated_at")
|
|
69
|
+
.eq("project_id", projectId)
|
|
70
|
+
.order("created_at", { ascending: false })
|
|
71
|
+
.limit(limit);
|
|
72
|
+
if (args.status)
|
|
73
|
+
q = q.eq("status", args.status);
|
|
74
|
+
if (args.kind)
|
|
75
|
+
q = q.eq("kind", args.kind);
|
|
76
|
+
const { data, error } = await q;
|
|
77
|
+
if (error) {
|
|
78
|
+
throw new Error(`list_curriculum_tasks failed: ${error.message}`);
|
|
79
|
+
}
|
|
80
|
+
const rows = (data ?? []);
|
|
81
|
+
return { count: rows.length, tasks: rows };
|
|
82
|
+
}
|
|
83
|
+
// ─── pull_curriculum_task ─────────────────────────────────────────────────
|
|
84
|
+
export const pullCurriculumTaskInputShape = {
|
|
85
|
+
project_id: z
|
|
86
|
+
.string()
|
|
87
|
+
.optional()
|
|
88
|
+
.describe(`Project namespace. Defaults to the slugified current working directory ('${currentProjectId}').`),
|
|
89
|
+
kind: z
|
|
90
|
+
.enum(["test_gap", "refactor", "rollback_repro"])
|
|
91
|
+
.nullable()
|
|
92
|
+
.optional()
|
|
93
|
+
.describe("Optionally restrict the claim to a single kind. NULL/omitted = any kind. Auto-promote-eligible tasks (linked_candidate_id IS NOT NULL) are prioritized regardless."),
|
|
94
|
+
session_id: z
|
|
95
|
+
.string()
|
|
96
|
+
.min(1)
|
|
97
|
+
.describe("Session identifier stamped on the claimed row (audit trail). Use the orchestrator's session id, conversation id, or any stable string for this run."),
|
|
98
|
+
};
|
|
99
|
+
export async function pullCurriculumTask(args) {
|
|
100
|
+
const projectId = resolveProjectId(args.project_id);
|
|
101
|
+
const { data, error } = await supabase.rpc("pull_next_curriculum_task", {
|
|
102
|
+
p_project_id: projectId,
|
|
103
|
+
p_kind: args.kind ?? null,
|
|
104
|
+
p_session_id: args.session_id,
|
|
105
|
+
});
|
|
106
|
+
if (error) {
|
|
107
|
+
throw new Error(`pull_curriculum_task failed: ${error.message}`);
|
|
108
|
+
}
|
|
109
|
+
const rows = (data ?? []);
|
|
110
|
+
if (rows.length === 0) {
|
|
111
|
+
return { claimed: false, task: null };
|
|
112
|
+
}
|
|
113
|
+
return { claimed: true, task: rows[0] };
|
|
114
|
+
}
|
|
115
|
+
// ─── apply_curriculum_task ────────────────────────────────────────────────
|
|
116
|
+
export const applyCurriculumTaskInputShape = {
|
|
117
|
+
task_id: z
|
|
118
|
+
.number()
|
|
119
|
+
.int()
|
|
120
|
+
.positive()
|
|
121
|
+
.describe("curriculum_tasks.id of the row to finalize. Must currently be in 'pulled' or 'attempted' state."),
|
|
122
|
+
success: z
|
|
123
|
+
.boolean()
|
|
124
|
+
.describe("TRUE finalizes as verified (requires checkpoint_id + committed checkpoint). FALSE finalizes as rejected."),
|
|
125
|
+
checkpoint_id: z
|
|
126
|
+
.number()
|
|
127
|
+
.int()
|
|
128
|
+
.positive()
|
|
129
|
+
.nullable()
|
|
130
|
+
.optional()
|
|
131
|
+
.describe("workflow_checkpoints.id wrapping the orchestrator's write. Required when success=true; the RPC asserts status='committed'."),
|
|
132
|
+
description: z
|
|
133
|
+
.string()
|
|
134
|
+
.optional()
|
|
135
|
+
.describe("Skill description passed to promote_candidate_to_skill when linked_candidate_id is set on the task. On success+linked, defaults to 'Auto-promoted via M5 curriculum task #N'. On failure, used as rejection_reason if provided."),
|
|
136
|
+
trigger_keywords: z
|
|
137
|
+
.array(z.string())
|
|
138
|
+
.optional()
|
|
139
|
+
.describe("Skill trigger keywords passed to promote_candidate_to_skill when linked_candidate_id is set. Defaults to []. Ignored on failure."),
|
|
140
|
+
bypass_verification_gate: z
|
|
141
|
+
.boolean()
|
|
142
|
+
.optional()
|
|
143
|
+
.describe("ESCAPE HATCH. Default false. When false (and success=true), the tool refuses to apply if verification-pending.json exists — the Orchestrator must clear it via confirm_verification first. Set true ONLY for smoke tests / tooling."),
|
|
144
|
+
};
|
|
145
|
+
export async function applyCurriculumTask(args) {
|
|
146
|
+
// Boundary Invariant: verification gate clearance is checked BEFORE the RPC.
|
|
147
|
+
// The daemon never reaches this code path; this is Orchestrator-only.
|
|
148
|
+
// Failure path skips the gate check (rejection has no main-touching write).
|
|
149
|
+
let gateClear = true;
|
|
150
|
+
if (args.success && !args.bypass_verification_gate) {
|
|
151
|
+
const pending = await getPending();
|
|
152
|
+
if (pending !== null) {
|
|
153
|
+
gateClear = false;
|
|
154
|
+
return {
|
|
155
|
+
ok: false,
|
|
156
|
+
gate_clear: false,
|
|
157
|
+
reason: `[M5] verification gate not cleared: ${pending.file ?? "pending"}. ` +
|
|
158
|
+
`Call confirm_verification({ success: true|false }) before applying.`,
|
|
159
|
+
result: null,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const { data, error } = await supabase.rpc("apply_curriculum_task", {
|
|
164
|
+
p_task_id: args.task_id,
|
|
165
|
+
p_success: args.success,
|
|
166
|
+
p_checkpoint_id: args.checkpoint_id ?? null,
|
|
167
|
+
p_description: args.description ?? null,
|
|
168
|
+
p_trigger_keywords: args.trigger_keywords ?? null,
|
|
169
|
+
});
|
|
170
|
+
if (error) {
|
|
171
|
+
return {
|
|
172
|
+
ok: false,
|
|
173
|
+
gate_clear: gateClear,
|
|
174
|
+
reason: `[M5] apply_curriculum_task RPC failed: ${error.message}`,
|
|
175
|
+
result: null,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const rows = (data ?? []);
|
|
179
|
+
if (rows.length === 0) {
|
|
180
|
+
return {
|
|
181
|
+
ok: false,
|
|
182
|
+
gate_clear: gateClear,
|
|
183
|
+
reason: "[M5] apply_curriculum_task returned no rows",
|
|
184
|
+
result: null,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
const r = rows[0];
|
|
188
|
+
if (r.applied_status === "verified") {
|
|
189
|
+
recordVerified(r.promoted_candidate_id !== null);
|
|
190
|
+
}
|
|
191
|
+
else if (r.applied_status === "rejected") {
|
|
192
|
+
recordRejected();
|
|
193
|
+
}
|
|
194
|
+
return { ok: true, gate_clear: gateClear, result: r };
|
|
195
|
+
}
|
|
196
|
+
// ─── reject_curriculum_task ───────────────────────────────────────────────
|
|
197
|
+
export const rejectCurriculumTaskInputShape = {
|
|
198
|
+
task_id: z
|
|
199
|
+
.number()
|
|
200
|
+
.int()
|
|
201
|
+
.positive()
|
|
202
|
+
.describe("curriculum_tasks.id to reject."),
|
|
203
|
+
reason: z
|
|
204
|
+
.string()
|
|
205
|
+
.min(1)
|
|
206
|
+
.describe("Free-text rationale stored on the row. Surfaced in list_curriculum_tasks."),
|
|
207
|
+
};
|
|
208
|
+
export async function rejectCurriculumTask(args) {
|
|
209
|
+
const { data, error } = await supabase
|
|
210
|
+
.from("curriculum_tasks")
|
|
211
|
+
.update({
|
|
212
|
+
status: "rejected",
|
|
213
|
+
rejection_reason: args.reason,
|
|
214
|
+
updated_at: new Date().toISOString(),
|
|
215
|
+
})
|
|
216
|
+
.eq("id", args.task_id)
|
|
217
|
+
.select("id, status")
|
|
218
|
+
.single();
|
|
219
|
+
if (error) {
|
|
220
|
+
throw new Error(`reject_curriculum_task failed: ${error.message}`);
|
|
221
|
+
}
|
|
222
|
+
recordRejected();
|
|
223
|
+
return { ok: true, task_id: data.id, status: data.status };
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=curriculum.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"curriculum.js","sourceRoot":"","sources":["../../src/tools/curriculum.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// Shared loader + atomic writer for the per-project frozen-patterns cache
|
|
2
|
+
// snapshot at ~/.claude-memory/frozen-patterns.json.
|
|
3
|
+
//
|
|
4
|
+
// v1.1.3 schema migration: per-project entries are now
|
|
5
|
+
// { pattern: string, source: string, added_at: number /* unix ms */ }
|
|
6
|
+
// instead of bare strings. The loader transparently migrates legacy string
|
|
7
|
+
// entries in-memory so older caches keep working until the next write.
|
|
8
|
+
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
// TODO(v1.2.0): drop the legacy CLAUDE_MEMORY_GATE_DIR fallback after the
|
|
12
|
+
// Smart Claude Memory rebrand has settled.
|
|
13
|
+
const GATE_DIR = process.env.SMART_CLAUDE_MEMORY_GATE_DIR ??
|
|
14
|
+
process.env.CLAUDE_MEMORY_GATE_DIR ??
|
|
15
|
+
join(homedir(), ".claude-memory");
|
|
16
|
+
export const FROZEN_CACHE_PATH = join(GATE_DIR, "frozen-patterns.json");
|
|
17
|
+
function emptyCache() {
|
|
18
|
+
return { updated_at: new Date(0).toISOString(), projects: {} };
|
|
19
|
+
}
|
|
20
|
+
// Coerce one raw JSON value (legacy string OR new object) into a FrozenEntry.
|
|
21
|
+
// Returns null if the value can't be salvaged at all.
|
|
22
|
+
function coerceEntry(raw) {
|
|
23
|
+
if (typeof raw === "string") {
|
|
24
|
+
const trimmed = raw.trim();
|
|
25
|
+
if (!trimmed)
|
|
26
|
+
return null;
|
|
27
|
+
return { pattern: trimmed, source: "legacy", added_at: 0 };
|
|
28
|
+
}
|
|
29
|
+
if (raw && typeof raw === "object") {
|
|
30
|
+
const o = raw;
|
|
31
|
+
const pattern = typeof o.pattern === "string" ? o.pattern.trim() : "";
|
|
32
|
+
if (!pattern)
|
|
33
|
+
return null;
|
|
34
|
+
const source = typeof o.source === "string" && o.source ? o.source : "legacy";
|
|
35
|
+
const added_at = typeof o.added_at === "number" && Number.isFinite(o.added_at)
|
|
36
|
+
? o.added_at
|
|
37
|
+
: 0;
|
|
38
|
+
return { pattern, source, added_at };
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Read the frozen-patterns cache from disk. Missing file → empty cache.
|
|
44
|
+
* Legacy string entries are migrated in-memory (not persisted here — the
|
|
45
|
+
* next write will lay down the new shape).
|
|
46
|
+
*/
|
|
47
|
+
export async function loadFrozenCache() {
|
|
48
|
+
let raw;
|
|
49
|
+
try {
|
|
50
|
+
raw = await readFile(FROZEN_CACHE_PATH, "utf8");
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
const err = e;
|
|
54
|
+
if (err && err.code === "ENOENT")
|
|
55
|
+
return emptyCache();
|
|
56
|
+
throw e;
|
|
57
|
+
}
|
|
58
|
+
let parsed;
|
|
59
|
+
try {
|
|
60
|
+
parsed = JSON.parse(raw);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return emptyCache();
|
|
64
|
+
}
|
|
65
|
+
if (!parsed || typeof parsed !== "object")
|
|
66
|
+
return emptyCache();
|
|
67
|
+
const obj = parsed;
|
|
68
|
+
const updated_at = typeof obj.updated_at === "string" ? obj.updated_at : new Date(0).toISOString();
|
|
69
|
+
const projectsRaw = (obj.projects ?? {});
|
|
70
|
+
const projects = {};
|
|
71
|
+
for (const [pid, list] of Object.entries(projectsRaw)) {
|
|
72
|
+
if (!Array.isArray(list))
|
|
73
|
+
continue;
|
|
74
|
+
const out = [];
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
for (const item of list) {
|
|
77
|
+
const entry = coerceEntry(item);
|
|
78
|
+
if (!entry)
|
|
79
|
+
continue;
|
|
80
|
+
if (seen.has(entry.pattern))
|
|
81
|
+
continue;
|
|
82
|
+
seen.add(entry.pattern);
|
|
83
|
+
out.push(entry);
|
|
84
|
+
}
|
|
85
|
+
projects[pid] = out;
|
|
86
|
+
}
|
|
87
|
+
return { updated_at, projects };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Atomically write the cache: serialize → write to `.tmp` sibling → rename.
|
|
91
|
+
* `rename` is atomic on POSIX and on NTFS (Node's fs.rename uses MoveFileEx
|
|
92
|
+
* with replace) so a concurrent reader never sees a half-written file.
|
|
93
|
+
*/
|
|
94
|
+
export async function writeFrozenCache(cache) {
|
|
95
|
+
const dir = dirname(FROZEN_CACHE_PATH);
|
|
96
|
+
await mkdir(dir, { recursive: true });
|
|
97
|
+
const tmp = `${FROZEN_CACHE_PATH}.tmp`;
|
|
98
|
+
const payload = {
|
|
99
|
+
updated_at: new Date().toISOString(),
|
|
100
|
+
projects: cache.projects,
|
|
101
|
+
};
|
|
102
|
+
await writeFile(tmp, JSON.stringify(payload, null, 2));
|
|
103
|
+
await rename(tmp, FROZEN_CACHE_PATH);
|
|
104
|
+
return FROZEN_CACHE_PATH;
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=frozen-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"frozen-cache.js","sourceRoot":"","sources":["../../src/tools/frozen-cache.ts"],"names":[],"mappings":"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"}
|