@reposkein/mcp 0.2.6 → 0.3.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/binary-digests.json +4 -4
- package/dist/SKILL.md +44 -1
- package/dist/cli/adr.d.ts +37 -0
- package/dist/cli/adr.js +231 -0
- package/dist/cli/adr.js.map +1 -0
- package/dist/cli/doctor.js +3 -0
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/doctorDecisions.d.ts +5 -0
- package/dist/cli/doctorDecisions.js +83 -0
- package/dist/cli/doctorDecisions.js.map +1 -0
- package/dist/cli/init.d.ts +3 -3
- package/dist/cli/init.js +11 -7
- package/dist/cli/init.js.map +1 -1
- package/dist/guard/decisionValidation.d.ts +29 -0
- package/dist/guard/decisionValidation.js +51 -0
- package/dist/guard/decisionValidation.js.map +1 -0
- package/dist/guard/summaryValidation.d.ts +3 -0
- package/dist/guard/summaryValidation.js +5 -3
- package/dist/guard/summaryValidation.js.map +1 -1
- package/dist/index.d.ts +40 -0
- package/dist/index.js +127 -5
- package/dist/index.js.map +1 -1
- package/dist/indexer/decisionsAffected.d.ts +42 -0
- package/dist/indexer/decisionsAffected.js +149 -0
- package/dist/indexer/decisionsAffected.js.map +1 -0
- package/dist/indexer/ensureGraph.d.ts +36 -0
- package/dist/indexer/ensureGraph.js +60 -0
- package/dist/indexer/ensureGraph.js.map +1 -0
- package/dist/indexer/runIndexer.d.ts +3 -0
- package/dist/indexer/runIndexer.js.map +1 -1
- package/dist/profile/decisions.d.ts +37 -0
- package/dist/profile/decisions.js +112 -0
- package/dist/profile/decisions.js.map +1 -0
- package/dist/profile/types.d.ts +8 -0
- package/dist/store/GraphStore.d.ts +5 -0
- package/dist/store/JsonlGraphStore.d.ts +1 -0
- package/dist/store/JsonlGraphStore.js +11 -2
- package/dist/store/JsonlGraphStore.js.map +1 -1
- package/dist/store/UnconfiguredStore.js +9 -1
- package/dist/store/UnconfiguredStore.js.map +1 -1
- package/dist/store/decisions.d.ts +113 -0
- package/dist/store/decisions.js +288 -0
- package/dist/store/decisions.js.map +1 -0
- package/dist/tools/getContextProfile.d.ts +1 -1
- package/dist/tools/getContextProfile.js +9 -1
- package/dist/tools/getContextProfile.js.map +1 -1
- package/dist/tools/getDecision.d.ts +6 -0
- package/dist/tools/getDecision.js +59 -0
- package/dist/tools/getDecision.js.map +1 -0
- package/dist/tools/impact.d.ts +1 -1
- package/dist/tools/impact.js +13 -1
- package/dist/tools/impact.js.map +1 -1
- package/dist/tools/indexerTools.d.ts +2 -0
- package/dist/tools/indexerTools.js +27 -0
- package/dist/tools/indexerTools.js.map +1 -1
- package/dist/tools/listDecisions.d.ts +19 -0
- package/dist/tools/listDecisions.js +93 -0
- package/dist/tools/listDecisions.js.map +1 -0
- package/dist/tools/reaffirmDecision.d.ts +11 -0
- package/dist/tools/reaffirmDecision.js +55 -0
- package/dist/tools/reaffirmDecision.js.map +1 -0
- package/dist/tools/recordDecision.d.ts +24 -0
- package/dist/tools/recordDecision.js +130 -0
- package/dist/tools/recordDecision.js.map +1 -0
- package/dist/tools/semanticFind.d.ts +9 -2
- package/dist/tools/semanticFind.js +44 -2
- package/dist/tools/semanticFind.js.map +1 -1
- package/dist/tools/setDecisionStatus.d.ts +8 -0
- package/dist/tools/setDecisionStatus.js +34 -0
- package/dist/tools/setDecisionStatus.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { neutralizeSummary } from "../guard/summaryValidation.js";
|
|
2
|
+
import { anchorRepoIds, loadDecisions, nodeIdSuffix, resolveAnchorStates, } from "../store/decisions.js";
|
|
3
|
+
const err = (text) => ({ content: [{ type: "text", text }], isError: true });
|
|
4
|
+
function neutralize(s) {
|
|
5
|
+
return neutralizeSummary(s) ?? "";
|
|
6
|
+
}
|
|
7
|
+
/** True when the decision governs the given file path — an anchor records it
|
|
8
|
+
* exactly, or a decision path prefix (dir ending "/") contains it. */
|
|
9
|
+
export function governsPath(rec, filePath) {
|
|
10
|
+
for (const a of rec.anchors) {
|
|
11
|
+
if (a.path === filePath)
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
for (const p of rec.paths) {
|
|
15
|
+
if (p === filePath)
|
|
16
|
+
return true;
|
|
17
|
+
if (p.endsWith("/") && filePath.startsWith(p))
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
function matchesAnchor(rec, anchor) {
|
|
23
|
+
if (anchor.startsWith("rs1:")) {
|
|
24
|
+
// Suffix-tolerant, like every other decision surface — anchors recorded
|
|
25
|
+
// in a fork/clone carry a different repo_id for the same node.
|
|
26
|
+
const suffix = nodeIdSuffix(anchor);
|
|
27
|
+
return rec.anchors.some((a) => a.node_id === anchor || (suffix !== null && nodeIdSuffix(a.node_id) === suffix));
|
|
28
|
+
}
|
|
29
|
+
return governsPath(rec, anchor);
|
|
30
|
+
}
|
|
31
|
+
function matchesText(rec, q) {
|
|
32
|
+
const needle = q.toLowerCase();
|
|
33
|
+
return (rec.title.toLowerCase().includes(needle) ||
|
|
34
|
+
rec.context.toLowerCase().includes(needle) ||
|
|
35
|
+
rec.decision.toLowerCase().includes(needle));
|
|
36
|
+
}
|
|
37
|
+
/** Deterministic order: decided_at desc, id asc within a day. */
|
|
38
|
+
export function sortDecisions(decisions) {
|
|
39
|
+
return [...decisions].sort((a, b) => {
|
|
40
|
+
if (a.decided_at !== b.decided_at)
|
|
41
|
+
return a.decided_at < b.decided_at ? 1 : -1;
|
|
42
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
export function makeListDecisions(store, repoId, repoPath) {
|
|
46
|
+
return async (args) => {
|
|
47
|
+
try {
|
|
48
|
+
const limit = Math.min(Math.max(args.limit ?? 25, 1), 50);
|
|
49
|
+
const { decisions, warnings } = loadDecisions(repoPath);
|
|
50
|
+
let filtered = decisions;
|
|
51
|
+
if (args.status)
|
|
52
|
+
filtered = filtered.filter((d) => d.status === args.status);
|
|
53
|
+
if (args.anchor)
|
|
54
|
+
filtered = filtered.filter((d) => matchesAnchor(d, args.anchor));
|
|
55
|
+
if (args.q)
|
|
56
|
+
filtered = filtered.filter((d) => matchesText(d, args.q));
|
|
57
|
+
const ordered = sortDecisions(filtered);
|
|
58
|
+
const page = ordered.slice(0, limit);
|
|
59
|
+
const repoIds = await anchorRepoIds(store, repoId);
|
|
60
|
+
const rows = [];
|
|
61
|
+
for (const d of page) {
|
|
62
|
+
const states = await resolveAnchorStates(store, repoIds, d.anchors);
|
|
63
|
+
const counts = { current: 0, stale: 0, moved: 0, orphaned: 0 };
|
|
64
|
+
for (const s of states)
|
|
65
|
+
counts[s.state]++;
|
|
66
|
+
rows.push({
|
|
67
|
+
id: d.id,
|
|
68
|
+
title: neutralize(d.title),
|
|
69
|
+
status: d.status,
|
|
70
|
+
decided_at: d.decided_at,
|
|
71
|
+
anchor_counts: counts,
|
|
72
|
+
...(d.superseded_by ? { superseded_by: d.superseded_by } : {}),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
content: [
|
|
77
|
+
{
|
|
78
|
+
type: "text",
|
|
79
|
+
text: JSON.stringify({
|
|
80
|
+
decisions: rows,
|
|
81
|
+
truncated: ordered.length > page.length,
|
|
82
|
+
...(warnings.length ? { warnings } : {}),
|
|
83
|
+
}),
|
|
84
|
+
},
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
catch (e) {
|
|
89
|
+
return err(e.message);
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=listDecisions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listDecisions.js","sourceRoot":"","sources":["../../src/tools/listDecisions.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EACL,aAAa,EACb,aAAa,EACb,YAAY,EACZ,mBAAmB,GAGpB,MAAM,uBAAuB,CAAC;AAa/B,MAAM,GAAG,GAAG,CAAC,IAAY,EAAc,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;AAEjG,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,iBAAiB,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;AACpC,CAAC;AAED;uEACuE;AACvE,MAAM,UAAU,WAAW,CAAC,GAAmB,EAAE,QAAgB;IAC/D,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QAC5B,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;IACvC,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;QAC1B,IAAI,CAAC,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAChC,IAAI,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC;YAAE,OAAO,IAAI,CAAC;IAC7D,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,aAAa,CAAC,GAAmB,EAAE,MAAc;IACxD,IAAI,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QAC9B,wEAAwE;QACxE,+DAA+D;QAC/D,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;QACpC,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CACrB,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,KAAK,MAAM,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC,CACvF,CAAC;IACJ,CAAC;IACD,OAAO,WAAW,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AAClC,CAAC;AAED,SAAS,WAAW,CAAC,GAAmB,EAAE,CAAS;IACjD,MAAM,MAAM,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAC/B,OAAO,CACL,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;QACxC,GAAG,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC;QAC1C,GAAG,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAC5C,CAAC;AACJ,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,aAAa,CAAC,SAA2B;IACvD,OAAO,CAAC,GAAG,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAClC,IAAI,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,UAAU;YAAE,OAAO,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/E,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,KAAiB,EAAE,MAAc,EAAE,QAAgB;IACnF,OAAO,KAAK,EAAE,IAAuB,EAAuB,EAAE;QAC5D,IAAI,CAAC;YACH,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC1D,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;YACxD,IAAI,QAAQ,GAAG,SAAS,CAAC;YACzB,IAAI,IAAI,CAAC,MAAM;gBAAE,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,IAAI,CAAC,MAAM,CAAC,CAAC;YAC7E,IAAI,IAAI,CAAC,MAAM;gBAAE,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC,CAAC,EAAE,IAAI,CAAC,MAAO,CAAC,CAAC,CAAC;YACnF,IAAI,IAAI,CAAC,CAAC;gBAAE,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,IAAI,CAAC,CAAE,CAAC,CAAC,CAAC;YACvE,MAAM,OAAO,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;YACxC,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;YACrC,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YACnD,MAAM,IAAI,GAAG,EAAE,CAAC;YAChB,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE,CAAC;gBACrB,MAAM,MAAM,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;gBACpE,MAAM,MAAM,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;gBAC/D,KAAK,MAAM,CAAC,IAAI,MAAM;oBAAE,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC1C,IAAI,CAAC,IAAI,CAAC;oBACR,EAAE,EAAE,CAAC,CAAC,EAAE;oBACR,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC;oBAC1B,MAAM,EAAE,CAAC,CAAC,MAAM;oBAChB,UAAU,EAAE,CAAC,CAAC,UAAU;oBACxB,aAAa,EAAE,MAAM;oBACrB,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC/D,CAAC,CAAC;YACL,CAAC;YACD,OAAO;gBACL,OAAO,EAAE;oBACP;wBACE,IAAI,EAAE,MAAM;wBACZ,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;4BACnB,SAAS,EAAE,IAAI;4BACf,SAAS,EAAE,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM;4BACvC,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;yBACzC,CAAC;qBACH;iBACF;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,GAAG,CAAE,CAAW,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { GraphStore } from "../store/GraphStore.js";
|
|
2
|
+
import type { ToolResult } from "./readCypher.js";
|
|
3
|
+
export interface ReaffirmDecisionArgs {
|
|
4
|
+
decision_id: string;
|
|
5
|
+
}
|
|
6
|
+
/** "This decision is still correct": re-stamps every anchor from the live
|
|
7
|
+
* graph — stale anchors get the current hash, moved anchors are rebound to
|
|
8
|
+
* their recovered node — clearing review flags without supersede spam. The
|
|
9
|
+
* prose body never changes; body_hash is re-signed because rebinding an
|
|
10
|
+
* anchor updates the governed node list it covers. */
|
|
11
|
+
export declare function makeReaffirmDecision(store: GraphStore, repoId: string, repoPath: string): (args: ReaffirmDecisionArgs) => Promise<ToolResult>;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { anchorRepoIds, computeBodyHash, loadDecisions, resolveAnchorStates, writeDecision, } from "../store/decisions.js";
|
|
2
|
+
const err = (text) => ({ content: [{ type: "text", text }], isError: true });
|
|
3
|
+
/** "This decision is still correct": re-stamps every anchor from the live
|
|
4
|
+
* graph — stale anchors get the current hash, moved anchors are rebound to
|
|
5
|
+
* their recovered node — clearing review flags without supersede spam. The
|
|
6
|
+
* prose body never changes; body_hash is re-signed because rebinding an
|
|
7
|
+
* anchor updates the governed node list it covers. */
|
|
8
|
+
export function makeReaffirmDecision(store, repoId, repoPath) {
|
|
9
|
+
return async (args) => {
|
|
10
|
+
try {
|
|
11
|
+
const { decisions } = loadDecisions(repoPath);
|
|
12
|
+
const rec = decisions.find((d) => d.id === args.decision_id);
|
|
13
|
+
if (!rec)
|
|
14
|
+
return err(`unknown decision: ${args.decision_id}`);
|
|
15
|
+
const repoIds = await anchorRepoIds(store, repoId);
|
|
16
|
+
const resolved = await resolveAnchorStates(store, repoIds, rec.anchors);
|
|
17
|
+
const anchors = [];
|
|
18
|
+
const results = [];
|
|
19
|
+
for (const r of resolved) {
|
|
20
|
+
results.push({ node_id: r.node_id, state: r.state });
|
|
21
|
+
if (r.state === "orphaned" || !r.resolved_node_id) {
|
|
22
|
+
anchors.push({ node_id: r.node_id, path: r.path, name: r.name, kind: r.kind, hash: r.hash });
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const live = await store.getNode(repoIds, r.resolved_node_id);
|
|
26
|
+
if (!live) {
|
|
27
|
+
anchors.push({ node_id: r.node_id, path: r.path, name: r.name, kind: r.kind, hash: r.hash });
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
anchors.push({
|
|
31
|
+
node_id: live.id,
|
|
32
|
+
path: live.file_path,
|
|
33
|
+
name: live.qualified_name || live.name,
|
|
34
|
+
kind: live.labels[0] ?? r.kind,
|
|
35
|
+
hash: live.content_hash,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
const updated = { ...rec, anchors, body_hash: "" };
|
|
39
|
+
updated.body_hash = computeBodyHash(updated);
|
|
40
|
+
writeDecision(repoPath, updated);
|
|
41
|
+
return {
|
|
42
|
+
content: [
|
|
43
|
+
{
|
|
44
|
+
type: "text",
|
|
45
|
+
text: JSON.stringify({ ok: true, decision_id: rec.id, anchors: results }),
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
catch (e) {
|
|
51
|
+
return err(e.message);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=reaffirmDecision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reaffirmDecision.js","sourceRoot":"","sources":["../../src/tools/reaffirmDecision.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { GraphStore } from "../store/GraphStore.js";
|
|
2
|
+
import type { ToolResult } from "./readCypher.js";
|
|
3
|
+
export interface RecordDecisionArgs {
|
|
4
|
+
title: string;
|
|
5
|
+
context: string;
|
|
6
|
+
decision: string;
|
|
7
|
+
consequences?: string;
|
|
8
|
+
alternatives?: string;
|
|
9
|
+
/** Default "proposed" — agents record, humans (or an explicit instruction)
|
|
10
|
+
* ratify. Honest provenance: the committing party is often the agent. */
|
|
11
|
+
status?: "proposed" | "accepted";
|
|
12
|
+
anchor_node_ids?: string[];
|
|
13
|
+
/** Literal file paths or directory prefixes ending "/". No globs. */
|
|
14
|
+
anchor_paths?: string[];
|
|
15
|
+
supersedes?: string[];
|
|
16
|
+
}
|
|
17
|
+
export interface DecisionToolDeps {
|
|
18
|
+
/** Best-effort graph refresh before stamping anchor hashes, so a decision
|
|
19
|
+
* recorded right after an edit isn't born stale (the store reads the last
|
|
20
|
+
* index snapshot). Failures are swallowed — recording must not break. */
|
|
21
|
+
refresh?: () => Promise<void>;
|
|
22
|
+
today?: () => string;
|
|
23
|
+
}
|
|
24
|
+
export declare function makeRecordDecision(store: GraphStore, repoId: string, repoPath: string, deps?: DecisionToolDeps): (args: RecordDecisionArgs) => Promise<ToolResult>;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { sanitizeDecisionFields } from "../guard/decisionValidation.js";
|
|
2
|
+
import { anchorRepoIds, computeBodyHash, loadDecisions, mintDecisionId, takenDecisionIds, writeDecision, } from "../store/decisions.js";
|
|
3
|
+
import { removeFromPendingDelta } from "../indexer/decisionsAffected.js";
|
|
4
|
+
function isoToday() {
|
|
5
|
+
// Day-precision ISO date (PRD §6.2.5: no wall-clock timestamps in output).
|
|
6
|
+
return new Date().toISOString().slice(0, 10);
|
|
7
|
+
}
|
|
8
|
+
const err = (text) => ({ content: [{ type: "text", text }], isError: true });
|
|
9
|
+
const ok = (obj) => ({ content: [{ type: "text", text: JSON.stringify(obj) }] });
|
|
10
|
+
/** A path anchor is a literal file path or a dir prefix ending "/" — never a
|
|
11
|
+
* glob (deterministic matching, no glob engine dependency). */
|
|
12
|
+
function invalidPath(p) {
|
|
13
|
+
return p.includes("*") || p.includes("?") || p.includes("[") || p.trim() === "";
|
|
14
|
+
}
|
|
15
|
+
export function makeRecordDecision(store, repoId, repoPath, deps = {}) {
|
|
16
|
+
const today = deps.today ?? isoToday;
|
|
17
|
+
return async (args) => {
|
|
18
|
+
const prose = sanitizeDecisionFields({
|
|
19
|
+
title: args.title,
|
|
20
|
+
context: args.context,
|
|
21
|
+
decision: args.decision,
|
|
22
|
+
consequences: args.consequences,
|
|
23
|
+
alternatives: args.alternatives,
|
|
24
|
+
});
|
|
25
|
+
if (!prose.ok)
|
|
26
|
+
return err(prose.error);
|
|
27
|
+
const anchorIds = args.anchor_node_ids ?? [];
|
|
28
|
+
const paths = args.anchor_paths ?? [];
|
|
29
|
+
const supersedes = args.supersedes ?? [];
|
|
30
|
+
if (anchorIds.length > 20)
|
|
31
|
+
return err("at most 20 anchor_node_ids");
|
|
32
|
+
if (paths.length > 20)
|
|
33
|
+
return err("at most 20 anchor_paths");
|
|
34
|
+
if (supersedes.length > 5)
|
|
35
|
+
return err("at most 5 supersedes");
|
|
36
|
+
const badPath = paths.find(invalidPath);
|
|
37
|
+
if (badPath !== undefined) {
|
|
38
|
+
return err(`anchor_paths must be literal paths or dir prefixes ending "/" (no globs): ${badPath}`);
|
|
39
|
+
}
|
|
40
|
+
try {
|
|
41
|
+
const { decisions } = loadDecisions(repoPath);
|
|
42
|
+
const byId = new Map(decisions.map((d) => [d.id, d]));
|
|
43
|
+
// Validate supersedes before writing anything (atomic refusal on typo).
|
|
44
|
+
for (const sid of supersedes) {
|
|
45
|
+
const target = byId.get(sid);
|
|
46
|
+
if (!target)
|
|
47
|
+
return err(`supersedes references unknown decision: ${sid}`);
|
|
48
|
+
if (target.status === "superseded") {
|
|
49
|
+
return err(`decision ${sid} is already superseded by ${target.superseded_by ?? "another decision"}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Refresh the graph before stamping so anchors reflect the working tree.
|
|
53
|
+
if (deps.refresh) {
|
|
54
|
+
try {
|
|
55
|
+
await deps.refresh();
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// best-effort
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const repoIds = await anchorRepoIds(store, repoId);
|
|
62
|
+
const anchors = [];
|
|
63
|
+
const unresolved = [];
|
|
64
|
+
for (const nodeId of anchorIds) {
|
|
65
|
+
const live = await store.getNode(repoIds, nodeId);
|
|
66
|
+
if (!live) {
|
|
67
|
+
unresolved.push(nodeId);
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
anchors.push({
|
|
71
|
+
node_id: live.id,
|
|
72
|
+
path: live.file_path,
|
|
73
|
+
name: live.qualified_name || live.name,
|
|
74
|
+
kind: live.labels[0] ?? "",
|
|
75
|
+
hash: live.content_hash,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const date = today();
|
|
79
|
+
// Filename-aware taken set: a damaged file's id must not be re-minted,
|
|
80
|
+
// or writeDecision would rename over the hand-recoverable record.
|
|
81
|
+
const id = mintDecisionId(date, prose.value.title, takenDecisionIds(repoPath));
|
|
82
|
+
const trigger = { kind: "manual" };
|
|
83
|
+
const rec = {
|
|
84
|
+
id,
|
|
85
|
+
title: prose.value.title,
|
|
86
|
+
status: args.status ?? "proposed",
|
|
87
|
+
context: prose.value.context,
|
|
88
|
+
decision: prose.value.decision,
|
|
89
|
+
anchors,
|
|
90
|
+
paths,
|
|
91
|
+
supersedes,
|
|
92
|
+
decided_at: date,
|
|
93
|
+
decided_by: process.env.REPOSKEIN_AGENT ?? "agent",
|
|
94
|
+
trigger,
|
|
95
|
+
body_hash: "",
|
|
96
|
+
};
|
|
97
|
+
if (prose.value.consequences !== undefined)
|
|
98
|
+
rec.consequences = prose.value.consequences;
|
|
99
|
+
if (prose.value.alternatives !== undefined)
|
|
100
|
+
rec.alternatives = prose.value.alternatives;
|
|
101
|
+
rec.body_hash = computeBodyHash(rec);
|
|
102
|
+
writeDecision(repoPath, rec);
|
|
103
|
+
// Flip superseded records after the new record exists; superseded_by
|
|
104
|
+
// makes the chain walkable in both directions.
|
|
105
|
+
for (const sid of supersedes) {
|
|
106
|
+
const target = byId.get(sid);
|
|
107
|
+
const flipped = { ...target, status: "superseded", superseded_by: id };
|
|
108
|
+
writeDecision(repoPath, flipped);
|
|
109
|
+
}
|
|
110
|
+
// The refresh above (or the agent's own pre-record edit + hook index)
|
|
111
|
+
// parked a drift delta that includes the very nodes this decision was
|
|
112
|
+
// just stamped against. Subtract them so the next reindex doesn't flag
|
|
113
|
+
// the freshly recorded decision as drifted; unrelated pending drift is
|
|
114
|
+
// preserved.
|
|
115
|
+
removeFromPendingDelta(repoPath, anchors.map((a) => a.node_id));
|
|
116
|
+
return ok({
|
|
117
|
+
ok: true,
|
|
118
|
+
decision_id: id,
|
|
119
|
+
status: rec.status,
|
|
120
|
+
anchors: anchors.map((a) => ({ node_id: a.node_id, path: a.path, kind: a.kind })),
|
|
121
|
+
unresolved,
|
|
122
|
+
superseded: supersedes,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
catch (e) {
|
|
126
|
+
return err(e.message);
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=recordDecision.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recordDecision.js","sourceRoot":"","sources":["../../src/tools/recordDecision.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,7 +22,7 @@ import type { EmbeddingProvider } from "../embed/provider.js";
|
|
|
22
22
|
export interface SemanticFindArgs {
|
|
23
23
|
query: string;
|
|
24
24
|
limit?: number;
|
|
25
|
-
kind?: "Function" | "Class" | "Interface" | "Enum";
|
|
25
|
+
kind?: "Function" | "Class" | "Interface" | "Enum" | "Decision";
|
|
26
26
|
federated?: boolean;
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
@@ -33,5 +33,12 @@ export interface SemanticFindArgs {
|
|
|
33
33
|
* @param repoPath File system path to the repo root (for the embedding cache).
|
|
34
34
|
* @param providerOverride Inject a provider directly (for tests — avoids env/network).
|
|
35
35
|
* Pass null to force pure-lexical. Pass undefined to use env config.
|
|
36
|
+
* @param opts.decisions Include the decision log in the corpus (default true).
|
|
37
|
+
* Callers without a REAL configured repoPath must pass
|
|
38
|
+
* false — repoPath falls back to "." for the embed
|
|
39
|
+
* cache, and reading <cwd>/.reposkein/decisions/ would
|
|
40
|
+
* inject another project's decision log.
|
|
36
41
|
*/
|
|
37
|
-
export declare function makeSemanticFind(store: GraphStore, repoId: string, repoPath?: string, providerOverride?: EmbeddingProvider | null
|
|
42
|
+
export declare function makeSemanticFind(store: GraphStore, repoId: string, repoPath?: string, providerOverride?: EmbeddingProvider | null, opts?: {
|
|
43
|
+
decisions?: boolean;
|
|
44
|
+
}): (args: SemanticFindArgs) => Promise<ToolResult>;
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
* and Neo4j backends. No Neo4j vector index used.
|
|
18
18
|
*/
|
|
19
19
|
import { federationIds } from "../store/federation.js";
|
|
20
|
+
import { loadDecisions } from "../store/decisions.js";
|
|
20
21
|
import { rankCorpus } from "../search/bm25f.js";
|
|
21
22
|
import { providerFromEnv } from "../embed/provider.js";
|
|
22
23
|
import { embedCorpus } from "../embed/cache.js";
|
|
@@ -24,6 +25,26 @@ import { cosineRank, rrf } from "../embed/hybrid.js";
|
|
|
24
25
|
import { neutralizeSummary } from "../guard/summaryValidation.js";
|
|
25
26
|
const DEFAULT_LIMIT = 10;
|
|
26
27
|
const MAX_LIMIT = 25;
|
|
28
|
+
/** Cap for the displayed rationale on Decision rows (the full record is one
|
|
29
|
+
* get_decision away; the search text itself is unbounded). */
|
|
30
|
+
const DECISION_SUMMARY_DISPLAY = 240;
|
|
31
|
+
/** Decision records as corpus pseudo-nodes: title is the name, and the
|
|
32
|
+
* searchable/embeddable text is title + context + decision — the schema was
|
|
33
|
+
* shaped for exactly this. All statuses are included (history is the point);
|
|
34
|
+
* rows carry `status` so superseded rationale is recognizable. Bodies are
|
|
35
|
+
* immutable, so the embedding cache never re-embeds a decision. */
|
|
36
|
+
function decisionCorpus(decisions, repoId) {
|
|
37
|
+
return decisions.map((d) => ({
|
|
38
|
+
id: d.id,
|
|
39
|
+
kind: "Decision",
|
|
40
|
+
name: d.title,
|
|
41
|
+
qualified_name: d.title,
|
|
42
|
+
signature: "",
|
|
43
|
+
summary: `${d.context} ${d.decision}`,
|
|
44
|
+
file_path: d.paths[0] ?? d.anchors[0]?.path ?? "",
|
|
45
|
+
repo_id: repoId,
|
|
46
|
+
}));
|
|
47
|
+
}
|
|
27
48
|
/**
|
|
28
49
|
* Factory for the semantic_find handler.
|
|
29
50
|
*
|
|
@@ -32,8 +53,14 @@ const MAX_LIMIT = 25;
|
|
|
32
53
|
* @param repoPath File system path to the repo root (for the embedding cache).
|
|
33
54
|
* @param providerOverride Inject a provider directly (for tests — avoids env/network).
|
|
34
55
|
* Pass null to force pure-lexical. Pass undefined to use env config.
|
|
56
|
+
* @param opts.decisions Include the decision log in the corpus (default true).
|
|
57
|
+
* Callers without a REAL configured repoPath must pass
|
|
58
|
+
* false — repoPath falls back to "." for the embed
|
|
59
|
+
* cache, and reading <cwd>/.reposkein/decisions/ would
|
|
60
|
+
* inject another project's decision log.
|
|
35
61
|
*/
|
|
36
|
-
export function makeSemanticFind(store, repoId, repoPath = ".", providerOverride) {
|
|
62
|
+
export function makeSemanticFind(store, repoId, repoPath = ".", providerOverride, opts = {}) {
|
|
63
|
+
const includeDecisions = opts.decisions ?? true;
|
|
37
64
|
return async (args) => {
|
|
38
65
|
const { query, kind, federated } = args;
|
|
39
66
|
const limit = Math.min(Math.max(1, args.limit ?? DEFAULT_LIMIT), MAX_LIMIT);
|
|
@@ -47,6 +74,15 @@ export function makeSemanticFind(store, repoId, repoPath = ".", providerOverride
|
|
|
47
74
|
const repoIds = federated ? await federationIds(store, repoId) : [repoId];
|
|
48
75
|
// Fetch the corpus from the store (both backends return nodes sorted by id)
|
|
49
76
|
let corpus = await store.searchCorpus(repoIds);
|
|
77
|
+
// Decision records join the corpus as pseudo-nodes (gated like every
|
|
78
|
+
// decision surface — see opts.decisions).
|
|
79
|
+
const decisionStatusById = new Map();
|
|
80
|
+
if (includeDecisions) {
|
|
81
|
+
const { decisions } = loadDecisions(repoPath);
|
|
82
|
+
corpus = [...corpus, ...decisionCorpus(decisions, repoId)];
|
|
83
|
+
for (const d of decisions)
|
|
84
|
+
decisionStatusById.set(d.id, d.status);
|
|
85
|
+
}
|
|
50
86
|
// Apply optional kind filter before ranking (design §4)
|
|
51
87
|
if (kind) {
|
|
52
88
|
corpus = corpus.filter((n) => n.kind === kind);
|
|
@@ -129,10 +165,16 @@ export function makeSemanticFind(store, repoId, repoPath = ".", providerOverride
|
|
|
129
165
|
if (via !== undefined) {
|
|
130
166
|
result["via"] = via;
|
|
131
167
|
}
|
|
168
|
+
if (node.kind === "Decision") {
|
|
169
|
+
result["status"] = decisionStatusById.get(node.id);
|
|
170
|
+
}
|
|
132
171
|
// Include summary only when present (keeps payload lean; absence is informative).
|
|
133
172
|
// Neutralize on the READ path to close the injection bypass: summaries from
|
|
134
173
|
// git-pulled nodes.jsonl or hand-edited files never passed the write guard.
|
|
135
|
-
|
|
174
|
+
let neutralized = neutralizeSummary(node.summary || null);
|
|
175
|
+
if (neutralized && node.kind === "Decision" && neutralized.length > DECISION_SUMMARY_DISPLAY) {
|
|
176
|
+
neutralized = neutralized.slice(0, DECISION_SUMMARY_DISPLAY) + "…";
|
|
177
|
+
}
|
|
136
178
|
if (neutralized) {
|
|
137
179
|
result["summary"] = neutralized;
|
|
138
180
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"semanticFind.js","sourceRoot":"","sources":["../../src/tools/semanticFind.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGhD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAE,UAAU,EAAE,GAAG,EAAgB,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AASlE,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB,MAAM,SAAS,GAAG,EAAE,CAAC;AAErB
|
|
1
|
+
{"version":3,"file":"semanticFind.js","sourceRoot":"","sources":["../../src/tools/semanticFind.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ToolResult } from "./readCypher.js";
|
|
2
|
+
export interface SetDecisionStatusArgs {
|
|
3
|
+
decision_id: string;
|
|
4
|
+
/** "superseded" is deliberately absent — it only happens via
|
|
5
|
+
* record_decision's supersedes, which links both directions. */
|
|
6
|
+
status: "accepted" | "rejected" | "deprecated";
|
|
7
|
+
}
|
|
8
|
+
export declare function makeSetDecisionStatus(repoPath: string): (args: SetDecisionStatusArgs) => Promise<ToolResult>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { loadDecisions, writeDecision, } from "../store/decisions.js";
|
|
2
|
+
/** Legal lifecycle transitions. rejected / deprecated / superseded are
|
|
3
|
+
* terminal; deprecation is for accepted decisions that aged out without a
|
|
4
|
+
* successor. */
|
|
5
|
+
const LEGAL = {
|
|
6
|
+
proposed: ["accepted", "rejected"],
|
|
7
|
+
accepted: ["deprecated"],
|
|
8
|
+
};
|
|
9
|
+
const err = (text) => ({ content: [{ type: "text", text }], isError: true });
|
|
10
|
+
export function makeSetDecisionStatus(repoPath) {
|
|
11
|
+
return async (args) => {
|
|
12
|
+
try {
|
|
13
|
+
const { decisions } = loadDecisions(repoPath);
|
|
14
|
+
const rec = decisions.find((d) => d.id === args.decision_id);
|
|
15
|
+
if (!rec)
|
|
16
|
+
return err(`unknown decision: ${args.decision_id}`);
|
|
17
|
+
const allowed = LEGAL[rec.status] ?? [];
|
|
18
|
+
if (!allowed.includes(args.status)) {
|
|
19
|
+
return err(`illegal transition ${rec.status} → ${args.status} (allowed from ${rec.status}: ${allowed.length ? allowed.join(", ") : "none — terminal status"})`);
|
|
20
|
+
}
|
|
21
|
+
const updated = { ...rec, status: args.status };
|
|
22
|
+
writeDecision(repoPath, updated);
|
|
23
|
+
return {
|
|
24
|
+
content: [
|
|
25
|
+
{ type: "text", text: JSON.stringify({ ok: true, decision_id: rec.id, status: args.status }) },
|
|
26
|
+
],
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
return err(e.message);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=setDecisionStatus.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"setDecisionStatus.js","sourceRoot":"","sources":["../../src/tools/setDecisionStatus.ts"],"names":[],"mappings":"AACA,OAAO,EACL,aAAa,EACb,aAAa,GAGd,MAAM,uBAAuB,CAAC;AAS/B;;iBAEiB;AACjB,MAAM,KAAK,GAAqC;IAC9C,QAAQ,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC;IAClC,QAAQ,EAAE,CAAC,YAAY,CAAC;CACzB,CAAC;AAEF,MAAM,GAAG,GAAG,CAAC,IAAY,EAAc,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;AAEjG,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,OAAO,KAAK,EAAE,IAA2B,EAAuB,EAAE;QAChE,IAAI,CAAC;YACH,MAAM,EAAE,SAAS,EAAE,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;YAC9C,MAAM,GAAG,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,WAAW,CAAC,CAAC;YAC7D,IAAI,CAAC,GAAG;gBAAE,OAAO,GAAG,CAAC,qBAAqB,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC;YAC9D,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YACxC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;gBACnC,OAAO,GAAG,CACR,sBAAsB,GAAG,CAAC,MAAM,MAAM,IAAI,CAAC,MAAM,kBAAkB,GAAG,CAAC,MAAM,KAC3E,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,wBACxC,GAAG,CACJ,CAAC;YACJ,CAAC;YACD,MAAM,OAAO,GAAmB,EAAE,GAAG,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;YAChE,aAAa,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YACjC,OAAO;gBACL,OAAO,EAAE;oBACP,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE;iBAC/F;aACF,CAAC;QACJ,CAAC;QAAC,OAAO,CAAC,EAAE,CAAC;YACX,OAAO,GAAG,CAAE,CAAW,CAAC,OAAO,CAAC,CAAC;QACnC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reposkein/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Deterministic code-graph (GraphRAG) MCP server — give your AI agent callers/callees, change-impact, and semantic search over your repo instead of grep. 7 languages, zero-infra, git-native.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|