@kage-core/kage-graph-mcp 1.1.13 → 1.1.14
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 +63 -11
- package/dist/cli.js +102 -0
- package/dist/daemon.js +11 -3
- package/dist/graph-registry.js +167 -0
- package/dist/index.js +73 -0
- package/dist/kernel.js +672 -25
- package/dist/registry/index.js +1 -1
- package/package.json +1 -1
- package/viewer/app.js +124 -8
- package/viewer/index.html +3 -3
- package/viewer/styles.css +1 -0
package/README.md
CHANGED
|
@@ -9,6 +9,17 @@ This package exposes two surfaces:
|
|
|
9
9
|
|
|
10
10
|
## Latest Release
|
|
11
11
|
|
|
12
|
+
`1.1.14` publishes the memory/code graph trust and retrieval pass:
|
|
13
|
+
|
|
14
|
+
- recall now uses vectorless BM25 lexical ranking with graph, path/type/tag,
|
|
15
|
+
intent, freshness, quality, and feedback boosts.
|
|
16
|
+
- `kage audit`, `kage inbox`, `kage code-index`, and `kage graph-registry`
|
|
17
|
+
are documented as first-class CLI and MCP surfaces.
|
|
18
|
+
- the viewer coalesces memory graph code entities with code graph nodes and
|
|
19
|
+
highlights memory-code links.
|
|
20
|
+
- README and the website now report the current 79-test proof state and BM25
|
|
21
|
+
retrieval behavior.
|
|
22
|
+
|
|
12
23
|
`1.1.13` switches future Kage releases to GPL-3.0-only:
|
|
13
24
|
|
|
14
25
|
- package metadata now advertises `GPL-3.0-only`.
|
|
@@ -21,8 +32,8 @@ This package exposes two surfaces:
|
|
|
21
32
|
- npm README now includes this explicit release note.
|
|
22
33
|
- Root README leads with the animated Kage demo GIF.
|
|
23
34
|
- README links clearly to the website and live viewer.
|
|
24
|
-
- Hosted viewer publishes Kage repo graph, code graph, and
|
|
25
|
-
Pages while local repos still use `kage viewer --project .`.
|
|
35
|
+
- Hosted viewer publishes Kage repo graph, code graph, metrics, and inbox from
|
|
36
|
+
GitHub Pages while local repos still use `kage viewer --project .`.
|
|
26
37
|
|
|
27
38
|
## Build
|
|
28
39
|
|
|
@@ -46,14 +57,18 @@ kage doctor --project /path/to/repo
|
|
|
46
57
|
kage index --project /path/to/repo
|
|
47
58
|
kage refresh --project /path/to/repo
|
|
48
59
|
kage branch --project /path/to/repo
|
|
60
|
+
kage code-index --project /path/to/repo
|
|
49
61
|
kage code-graph --project /path/to/repo
|
|
50
62
|
kage code-graph "createApp routes tests" --project /path/to/repo
|
|
51
63
|
kage graph --project /path/to/repo
|
|
52
64
|
kage graph --project /path/to/repo --mermaid
|
|
53
65
|
kage graph "test command" --project /path/to/repo
|
|
66
|
+
kage graph-registry --project /path/to/repo
|
|
54
67
|
kage recall "how do I run tests" --project /path/to/repo
|
|
55
68
|
kage recall "how do I run tests" --project /path/to/repo --explain --json
|
|
56
69
|
kage quality --project /path/to/repo
|
|
70
|
+
kage audit --project /path/to/repo
|
|
71
|
+
kage inbox --project /path/to/repo
|
|
57
72
|
kage benchmark --project /path/to/repo
|
|
58
73
|
kage benchmark --project /path/to/repo --compare --task "how do I run tests"
|
|
59
74
|
kage viewer --project /path/to/repo
|
|
@@ -131,6 +146,12 @@ generic extraction in metrics and file parser coverage. This keeps installation
|
|
|
131
146
|
light while allowing teams to plug in the strongest indexer available for their
|
|
132
147
|
language stack.
|
|
133
148
|
|
|
149
|
+
`kage code-index --project <repo>` writes `.agent_memory/code_index/lsp-symbols.json`
|
|
150
|
+
in an LSP document-symbol-compatible shape using Kage's local parser. The CI,
|
|
151
|
+
PR, and sync workflows run it before refresh so the code graph has a committed
|
|
152
|
+
precise-index slot while teams can still replace or augment it with SCIP/LSIF
|
|
153
|
+
artifacts from their preferred toolchain.
|
|
154
|
+
|
|
134
155
|
The memory graph follows the same product direction as temporal context graph
|
|
135
156
|
systems such as Graphiti: immutable ingestion episodes, derived entities and
|
|
136
157
|
facts, evidence/provenance on every edge, confidence, branch/commit context, and
|
|
@@ -143,12 +164,36 @@ and parser coverage, code graph counts, evidence coverage, approved vs pending
|
|
|
143
164
|
memory, validation status, estimated tokens saved per recall, duplicate
|
|
144
165
|
candidates, average memory quality, and a readiness score.
|
|
145
166
|
|
|
167
|
+
Use `kage audit --project <repo>` or the `kage_audit` MCP tool before relying
|
|
168
|
+
on repo memory for agent work. Audit reports validation, pending memory inbox
|
|
169
|
+
size, structured context coverage, stale/duplicate risk, memory-to-code graph
|
|
170
|
+
links, precise code index coverage, and concrete recommendations such as
|
|
171
|
+
extending SCIP/LSIF/LSP coverage or adding structured
|
|
172
|
+
`why`/`verification`/`risk_if_forgotten` fields to high-value packets.
|
|
173
|
+
|
|
174
|
+
Use `kage inbox --project <repo>` or the `kage_inbox` MCP tool for the
|
|
175
|
+
actionable review queue. It consolidates pending packets, stale packets,
|
|
176
|
+
duplicates, missing structured context, validation warnings/errors, and concrete
|
|
177
|
+
actions into one report that the viewer also loads.
|
|
178
|
+
|
|
146
179
|
Use `kage benchmark --compare --task "<task>" --project <repo>` or
|
|
147
180
|
`kage_benchmark_compare` to compare the same task on the same repo with and
|
|
148
181
|
without Kage. It estimates manual full-file rediscovery tokens/steps, compares
|
|
149
182
|
them to compact Kage recall plus code graph context, and returns evidence plus
|
|
150
183
|
caveats for honest marketing proof.
|
|
151
184
|
|
|
185
|
+
Plain `kage benchmark --project <repo>` now includes explicit gates and an
|
|
186
|
+
overall score for recall hit rate, evidence coverage, useful memory ratio, and
|
|
187
|
+
code-flow coverage, so benchmark output has pass/fail criteria instead of loose
|
|
188
|
+
claims.
|
|
189
|
+
|
|
190
|
+
Use `kage graph-registry --project <repo>` or `kage_graph_registry` to write
|
|
191
|
+
`.agent_memory/graph_registry/manifest.json`. The manifest is signed with the
|
|
192
|
+
same canonical JSON scheme as registry bundles and records memory/code graph
|
|
193
|
+
artifact hashes, generated index/report paths, source packet IDs and packet
|
|
194
|
+
hashes, git state, audit trust, inbox counts, and metrics readiness. CI, PR, and
|
|
195
|
+
sync workflows build it after refresh.
|
|
196
|
+
|
|
152
197
|
Use `kage refresh --project <repo>` or the `kage_refresh` MCP tool after
|
|
153
198
|
meaningful file changes. Refresh rebuilds indexes, code graph, memory graph,
|
|
154
199
|
metrics, and stale-memory metadata. Memory is marked stale when status or
|
|
@@ -172,13 +217,15 @@ review.
|
|
|
172
217
|
clients. It accepts session, prompt, tool, file-change, command, test, and
|
|
173
218
|
session-end events; deduplicates them; scans for secrets and PII; and stores raw
|
|
174
219
|
observations locally only. `kage distill` turns useful observations into
|
|
175
|
-
repo-local packets with observation session source refs.
|
|
176
|
-
|
|
220
|
+
repo-local packets with observation session source refs. Distillation is not
|
|
221
|
+
limited to action-changing instructions: durable rationale, bug causes, issue
|
|
222
|
+
state, decisions, and code explanations are valid memory when source-backed. It
|
|
223
|
+
never publishes memory.
|
|
177
224
|
|
|
178
225
|
`kage recall --explain --json` exposes the hybrid scoring explanation used for
|
|
179
|
-
ranking:
|
|
180
|
-
placeholder for future local or external embedding providers.
|
|
181
|
-
is
|
|
226
|
+
ranking: BM25 lexical score, graph, path/type/tag, intent, freshness, quality,
|
|
227
|
+
feedback, and a vector placeholder for future local or external embedding providers.
|
|
228
|
+
Current fallback is vectorless BM25 plus graph retrieval.
|
|
182
229
|
|
|
183
230
|
`kage_context` is the primary MCP entrypoint for agents. It validates repo
|
|
184
231
|
memory, recalls relevant packets, and returns code/knowledge graph context in
|
|
@@ -192,6 +239,7 @@ one call. Agents should use it at task start instead of loading separate
|
|
|
192
239
|
- `GET /kage/status`
|
|
193
240
|
- `GET /kage/metrics`
|
|
194
241
|
- `GET /kage/quality`
|
|
242
|
+
- `GET /kage/inbox`
|
|
195
243
|
- `GET /kage/benchmark`
|
|
196
244
|
- `POST /kage/recall`
|
|
197
245
|
- `POST /kage/observe`
|
|
@@ -207,13 +255,13 @@ and refreshes generated graph/index artifacts after a short debounce.
|
|
|
207
255
|
Run `kage viewer --project <repo>` to start the local terminal console. It
|
|
208
256
|
serves the viewer and the selected repo's `.agent_memory/` files from the same
|
|
209
257
|
localhost server, then prints a URL that auto-loads memory graph, code graph,
|
|
210
|
-
metrics, review artifact, and pending packets when present.
|
|
211
|
-
a fallback, not the main workflow.
|
|
258
|
+
metrics, memory inbox, review artifact, and pending packets when present.
|
|
259
|
+
Manual JSON selection remains as a fallback, not the main workflow.
|
|
212
260
|
|
|
213
261
|
The viewer renders nodes and relations in SVG, supports memory/code/combined
|
|
214
262
|
modes, filters by type and relation, displays metrics, shows packets and pending
|
|
215
|
-
quarantine items when present, and marks risks such
|
|
216
|
-
missing-evidence edges.
|
|
263
|
+
quarantine items when present, surfaces the memory inbox, and marks risks such
|
|
264
|
+
as low-confidence or missing-evidence edges.
|
|
217
265
|
|
|
218
266
|
For demos or local docs, the viewer also accepts URL params:
|
|
219
267
|
|
|
@@ -232,8 +280,12 @@ Local repo tools:
|
|
|
232
280
|
|
|
233
281
|
- `kage_context`
|
|
234
282
|
- `kage_recall`
|
|
283
|
+
- `kage_graph_registry`
|
|
235
284
|
- `kage_code_graph`
|
|
285
|
+
- `kage_code_index`
|
|
236
286
|
- `kage_metrics`
|
|
287
|
+
- `kage_audit`
|
|
288
|
+
- `kage_inbox`
|
|
237
289
|
- `kage_refresh`
|
|
238
290
|
- `kage_pr_summarize`
|
|
239
291
|
- `kage_pr_check`
|
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,7 @@ const promises_1 = require("node:readline/promises");
|
|
|
6
6
|
const node_process_1 = require("node:process");
|
|
7
7
|
const daemon_js_1 = require("./daemon.js");
|
|
8
8
|
const kernel_js_1 = require("./kernel.js");
|
|
9
|
+
const graph_registry_js_1 = require("./graph-registry.js");
|
|
9
10
|
function usage() {
|
|
10
11
|
console.log(`Kage repo memory and code graph
|
|
11
12
|
|
|
@@ -30,14 +31,18 @@ Usage:
|
|
|
30
31
|
kage upgrade [--dry-run]
|
|
31
32
|
kage branch --project <dir> [--json]
|
|
32
33
|
kage metrics --project <dir> [--json]
|
|
34
|
+
kage audit --project <dir> [--json]
|
|
35
|
+
kage inbox --project <dir> [--json]
|
|
33
36
|
kage quality --project <dir> [--json]
|
|
34
37
|
kage benchmark --project <dir> [--json]
|
|
35
38
|
kage benchmark --project <dir> --compare --task <task> [--json]
|
|
36
39
|
kage code-graph --project <dir> [--json]
|
|
37
40
|
kage code-graph "<query>" --project <dir> [--json]
|
|
41
|
+
kage code-index --project <dir> [--json]
|
|
38
42
|
kage graph --project <dir> [--json]
|
|
39
43
|
kage graph --project <dir> --mermaid
|
|
40
44
|
kage graph "<query>" --project <dir> [--json]
|
|
45
|
+
kage graph-registry --project <dir> [--json]
|
|
41
46
|
kage recall "<query>" --project <dir> [--json] [--explain]
|
|
42
47
|
kage observe --project <dir> --event <json>
|
|
43
48
|
kage distill --project <dir> --session <id>
|
|
@@ -452,6 +457,25 @@ async function main() {
|
|
|
452
457
|
console.log(`- ${edge.fact}`);
|
|
453
458
|
return;
|
|
454
459
|
}
|
|
460
|
+
if (command === "graph-registry") {
|
|
461
|
+
const result = (0, graph_registry_js_1.buildGraphRegistryManifest)(projectArg(args));
|
|
462
|
+
if (args.includes("--json")) {
|
|
463
|
+
console.log(JSON.stringify(result, null, 2));
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
console.log(`Kage Graph Registry: ${result.project_dir}`);
|
|
467
|
+
console.log(`Manifest: ${result.path}`);
|
|
468
|
+
console.log(`Artifacts: ${result.artifacts.length}`);
|
|
469
|
+
console.log(`Packets: ${result.manifest.payload.sources.packet_count}`);
|
|
470
|
+
console.log(`Signature: ${result.manifest.signature.payload_sha256}`);
|
|
471
|
+
if (result.errors.length) {
|
|
472
|
+
console.log("\nErrors:");
|
|
473
|
+
for (const error of result.errors)
|
|
474
|
+
console.log(` - ${error}`);
|
|
475
|
+
process.exitCode = 2;
|
|
476
|
+
}
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
455
479
|
if (command === "code-graph") {
|
|
456
480
|
const query = firstPositional(args);
|
|
457
481
|
if (query) {
|
|
@@ -481,6 +505,25 @@ async function main() {
|
|
|
481
505
|
console.log(`- ${symbol.kind} ${symbol.name} (${symbol.path}:${symbol.line})`);
|
|
482
506
|
return;
|
|
483
507
|
}
|
|
508
|
+
if (command === "code-index") {
|
|
509
|
+
const result = (0, kernel_js_1.writeLspSymbolIndex)(projectArg(args));
|
|
510
|
+
if (args.includes("--json")) {
|
|
511
|
+
console.log(JSON.stringify(result, null, 2));
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
console.log(`Kage Code Index: ${result.project_dir}`);
|
|
515
|
+
console.log(`Parser: ${result.parser}`);
|
|
516
|
+
console.log(`Path: ${result.path}`);
|
|
517
|
+
console.log(`Documents: ${result.documents}`);
|
|
518
|
+
console.log(`Symbols: ${result.symbols}`);
|
|
519
|
+
if (result.errors.length) {
|
|
520
|
+
console.log("\nErrors:");
|
|
521
|
+
for (const error of result.errors)
|
|
522
|
+
console.log(` - ${error}`);
|
|
523
|
+
process.exitCode = 2;
|
|
524
|
+
}
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
484
527
|
if (command === "branch") {
|
|
485
528
|
const result = (0, kernel_js_1.buildBranchOverlay)(projectArg(args));
|
|
486
529
|
if (args.includes("--json"))
|
|
@@ -544,6 +587,59 @@ async function main() {
|
|
|
544
587
|
}
|
|
545
588
|
return;
|
|
546
589
|
}
|
|
590
|
+
if (command === "audit") {
|
|
591
|
+
const result = (0, kernel_js_1.auditProject)(projectArg(args));
|
|
592
|
+
if (args.includes("--json")) {
|
|
593
|
+
console.log(JSON.stringify(result, null, 2));
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
console.log(`Kage Audit: ${result.project_dir}`);
|
|
597
|
+
console.log(`Trust score: ${result.trust_score}/100`);
|
|
598
|
+
console.log(`Validation: ${result.checks.validation.ok ? "passed" : "failed"}`);
|
|
599
|
+
console.log(`Memory inbox: ${result.checks.memory_inbox.approved_packets} approved, ${result.checks.memory_inbox.pending_packets} pending, ${result.checks.memory_inbox.stale_packets} stale`);
|
|
600
|
+
console.log(`Structured memory: ${result.checks.structured_memory.structured_packets}/${result.checks.structured_memory.total_packets} (${result.checks.structured_memory.coverage_percent}%)`);
|
|
601
|
+
console.log(`Code graph precision: ${result.checks.code_graph.precise_files}/${result.checks.code_graph.files} precise (${result.checks.code_graph.precise_coverage_percent}%), ${result.checks.code_graph.ast_files} AST, ${result.checks.code_graph.fallback_files} fallback`);
|
|
602
|
+
console.log(`Memory-code graph edges: ${result.checks.graph_links.memory_code_edges}`);
|
|
603
|
+
if (result.recommendations.length) {
|
|
604
|
+
console.log("\nRecommendations:");
|
|
605
|
+
for (const recommendation of result.recommendations)
|
|
606
|
+
console.log(` - ${recommendation}`);
|
|
607
|
+
}
|
|
608
|
+
if (!result.ok)
|
|
609
|
+
process.exitCode = 2;
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
if (command === "inbox") {
|
|
613
|
+
const result = (0, kernel_js_1.memoryInbox)(projectArg(args));
|
|
614
|
+
if (args.includes("--json")) {
|
|
615
|
+
console.log(JSON.stringify(result, null, 2));
|
|
616
|
+
return;
|
|
617
|
+
}
|
|
618
|
+
console.log(`Kage Memory Inbox: ${result.project_dir}`);
|
|
619
|
+
console.log(`Approved: ${result.counts.approved}`);
|
|
620
|
+
console.log(`Pending: ${result.counts.pending}`);
|
|
621
|
+
console.log(`Stale: ${result.counts.stale}`);
|
|
622
|
+
console.log(`Duplicates: ${result.counts.duplicates}`);
|
|
623
|
+
console.log(`Missing structured context: ${result.counts.missing_context}`);
|
|
624
|
+
console.log(`Validation: ${result.counts.validation_errors} errors, ${result.counts.validation_warnings} warnings`);
|
|
625
|
+
if (result.items.length) {
|
|
626
|
+
console.log("\nInbox items:");
|
|
627
|
+
for (const item of result.items.slice(0, 30)) {
|
|
628
|
+
console.log(` - [${item.severity}] ${item.kind}: ${item.title ?? item.summary}`);
|
|
629
|
+
console.log(` Action: ${item.action}`);
|
|
630
|
+
}
|
|
631
|
+
if (result.items.length > 30)
|
|
632
|
+
console.log(` ... ${result.items.length - 30} more item(s)`);
|
|
633
|
+
}
|
|
634
|
+
if (result.recommendations.length) {
|
|
635
|
+
console.log("\nRecommendations:");
|
|
636
|
+
for (const recommendation of result.recommendations)
|
|
637
|
+
console.log(` - ${recommendation}`);
|
|
638
|
+
}
|
|
639
|
+
if (!result.ok)
|
|
640
|
+
process.exitCode = 2;
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
547
643
|
if (command === "quality") {
|
|
548
644
|
const result = (0, kernel_js_1.qualityReport)(projectArg(args));
|
|
549
645
|
if (args.includes("--json")) {
|
|
@@ -607,6 +703,12 @@ async function main() {
|
|
|
607
703
|
return;
|
|
608
704
|
}
|
|
609
705
|
console.log(`Kage Benchmark: ${result.project_dir}`);
|
|
706
|
+
console.log(`OK: ${result.ok ? "yes" : "no"}`);
|
|
707
|
+
console.log(`Overall score: ${result.overall_score}/100`);
|
|
708
|
+
console.log("Gates:");
|
|
709
|
+
for (const gate of result.gates)
|
|
710
|
+
console.log(` - ${gate.name}: ${gate.actual}${gate.unit === "percent" ? "%" : ""} / target ${gate.target}${gate.unit === "percent" ? "%" : ""} (${gate.pass ? "pass" : "fail"})`);
|
|
711
|
+
console.log("Pain metrics:");
|
|
610
712
|
for (const [name, value] of Object.entries(result.pain_metrics))
|
|
611
713
|
console.log(`${name}: ${value}`);
|
|
612
714
|
return;
|
package/dist/daemon.js
CHANGED
|
@@ -98,6 +98,7 @@ function daemonDoctor(projectDir) {
|
|
|
98
98
|
`POST http://${DEFAULT_HOST}:${restPort}/kage/distill`,
|
|
99
99
|
`GET http://${DEFAULT_HOST}:${restPort}/kage/metrics`,
|
|
100
100
|
`GET http://${DEFAULT_HOST}:${restPort}/kage/quality`,
|
|
101
|
+
`GET http://${DEFAULT_HOST}:${restPort}/kage/inbox`,
|
|
101
102
|
`GET http://${DEFAULT_HOST}:${restPort}/kage/benchmark`,
|
|
102
103
|
],
|
|
103
104
|
warnings,
|
|
@@ -186,6 +187,10 @@ async function startDaemon(projectDir, options = {}) {
|
|
|
186
187
|
json(res, 200, (0, kernel_js_1.qualityReport)(projectDir));
|
|
187
188
|
return;
|
|
188
189
|
}
|
|
190
|
+
if (req.method === "GET" && url.pathname === "/kage/inbox") {
|
|
191
|
+
json(res, 200, (0, kernel_js_1.memoryInbox)(projectDir));
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
189
194
|
if (req.method === "GET" && url.pathname === "/kage/benchmark") {
|
|
190
195
|
json(res, 200, (0, kernel_js_1.benchmarkProject)(projectDir));
|
|
191
196
|
return;
|
|
@@ -231,17 +236,20 @@ async function startViewer(projectDir, options = {}) {
|
|
|
231
236
|
const graphPath = (0, node_path_1.join)(projectRoot, ".agent_memory", "graph", "graph.json");
|
|
232
237
|
const codePath = (0, node_path_1.join)(projectRoot, ".agent_memory", "code_graph", "graph.json");
|
|
233
238
|
const metricsPath = (0, node_path_1.join)(projectRoot, ".agent_memory", "metrics.json");
|
|
239
|
+
const inboxPath = (0, node_path_1.join)(projectRoot, ".agent_memory", "inbox.json");
|
|
234
240
|
const reviewPath = (0, node_path_1.join)(projectRoot, ".agent_memory", "review", "memory-review.md");
|
|
235
241
|
const pendingDir = (0, node_path_1.join)(projectRoot, ".agent_memory", "pending");
|
|
236
|
-
// Pre-generate
|
|
242
|
+
// Pre-generate lightweight JSON reports so the viewer can load them directly.
|
|
237
243
|
try {
|
|
238
244
|
const metrics = (0, kernel_js_1.kageMetrics)(projectDir);
|
|
239
245
|
(0, node_fs_1.writeFileSync)(metricsPath, JSON.stringify(metrics, null, 2));
|
|
246
|
+
const inbox = (0, kernel_js_1.memoryInbox)(projectDir);
|
|
247
|
+
(0, node_fs_1.writeFileSync)(inboxPath, JSON.stringify(inbox, null, 2));
|
|
240
248
|
}
|
|
241
249
|
catch {
|
|
242
|
-
// non-fatal: viewer will show 404 for
|
|
250
|
+
// non-fatal: viewer will show 404 for reports if generation fails
|
|
243
251
|
}
|
|
244
|
-
const url = `http://${host}:${port}/viewer/index.html?graph=${encodeURIComponent(graphPath)}&code=${encodeURIComponent(codePath)}&metrics=${encodeURIComponent(metricsPath)}&review=${encodeURIComponent(reviewPath)}&pending=${encodeURIComponent(pendingDir)}`;
|
|
252
|
+
const url = `http://${host}:${port}/viewer/index.html?graph=${encodeURIComponent(graphPath)}&code=${encodeURIComponent(codePath)}&metrics=${encodeURIComponent(metricsPath)}&inbox=${encodeURIComponent(inboxPath)}&review=${encodeURIComponent(reviewPath)}&pending=${encodeURIComponent(pendingDir)}`;
|
|
245
253
|
const server = (0, node_http_1.createServer)((req, res) => {
|
|
246
254
|
const requestUrl = new URL(req.url ?? "/", `http://${host}:${port}`);
|
|
247
255
|
let filePath = null;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildGraphRegistryManifest = buildGraphRegistryManifest;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
const node_child_process_1 = require("node:child_process");
|
|
6
|
+
const node_fs_1 = require("node:fs");
|
|
7
|
+
const node_path_1 = require("node:path");
|
|
8
|
+
const index_js_1 = require("./registry/index.js");
|
|
9
|
+
const kernel_js_1 = require("./kernel.js");
|
|
10
|
+
function nowIso() {
|
|
11
|
+
return new Date().toISOString();
|
|
12
|
+
}
|
|
13
|
+
function ensureDir(path) {
|
|
14
|
+
if (!(0, node_fs_1.existsSync)(path)) {
|
|
15
|
+
(0, node_fs_1.mkdirSync)(path, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function writeJson(path, value) {
|
|
19
|
+
const dir = (0, node_path_1.dirname)(path);
|
|
20
|
+
if (!(0, node_fs_1.existsSync)(dir)) {
|
|
21
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
(0, node_fs_1.writeFileSync)(path, `${JSON.stringify(value, null, 2)}\n`, "utf8");
|
|
24
|
+
}
|
|
25
|
+
function readGit(projectDir, args) {
|
|
26
|
+
try {
|
|
27
|
+
return (0, node_child_process_1.execFileSync)("git", args, { cwd: projectDir, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim() || null;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function gitBranch(projectDir) {
|
|
34
|
+
return readGit(projectDir, ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
35
|
+
}
|
|
36
|
+
function gitHead(projectDir) {
|
|
37
|
+
return readGit(projectDir, ["rev-parse", "HEAD"]);
|
|
38
|
+
}
|
|
39
|
+
function gitMergeBase(projectDir) {
|
|
40
|
+
return readGit(projectDir, ["merge-base", "HEAD", "origin/HEAD"]) ?? gitHead(projectDir);
|
|
41
|
+
}
|
|
42
|
+
function repoKey(projectDir) {
|
|
43
|
+
return (readGit(projectDir, ["config", "--get", "remote.origin.url"]) ?? projectDir)
|
|
44
|
+
.toLowerCase()
|
|
45
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
46
|
+
.replace(/^-+|-+$/g, "")
|
|
47
|
+
.slice(0, 120) || "local-repo";
|
|
48
|
+
}
|
|
49
|
+
function canonicalPacketText(packet) {
|
|
50
|
+
return JSON.stringify({
|
|
51
|
+
title: packet.title,
|
|
52
|
+
summary: packet.summary,
|
|
53
|
+
body: packet.body,
|
|
54
|
+
type: packet.type,
|
|
55
|
+
tags: packet.tags,
|
|
56
|
+
paths: packet.paths,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function graphRegistryArtifact(projectDir, path, name, kind) {
|
|
60
|
+
const absolute = (0, node_path_1.join)(projectDir, path);
|
|
61
|
+
if (!(0, node_fs_1.existsSync)(absolute))
|
|
62
|
+
return null;
|
|
63
|
+
const bytes = (0, node_fs_1.readFileSync)(absolute);
|
|
64
|
+
let schemaVersion = null;
|
|
65
|
+
try {
|
|
66
|
+
const parsed = JSON.parse(bytes.toString("utf8"));
|
|
67
|
+
schemaVersion = typeof parsed.schema_version === "number" ? parsed.schema_version : null;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
schemaVersion = null;
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
name,
|
|
74
|
+
kind,
|
|
75
|
+
path,
|
|
76
|
+
schema_version: schemaVersion,
|
|
77
|
+
sha256: (0, node_crypto_1.createHash)("sha256").update(bytes).digest("hex"),
|
|
78
|
+
bytes: (0, node_fs_1.statSync)(absolute).size,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function buildGraphRegistryManifest(projectDir) {
|
|
82
|
+
(0, kernel_js_1.ensureMemoryDirs)(projectDir);
|
|
83
|
+
ensureDir((0, kernel_js_1.graphRegistryDir)(projectDir));
|
|
84
|
+
(0, kernel_js_1.buildIndexes)(projectDir);
|
|
85
|
+
(0, kernel_js_1.buildCodeGraph)(projectDir);
|
|
86
|
+
(0, kernel_js_1.buildKnowledgeGraph)(projectDir);
|
|
87
|
+
const metrics = (0, kernel_js_1.kageMetrics)(projectDir);
|
|
88
|
+
const audit = (0, kernel_js_1.auditProject)(projectDir);
|
|
89
|
+
const inbox = (0, kernel_js_1.memoryInbox)(projectDir);
|
|
90
|
+
const metricsPath = ".agent_memory/metrics.json";
|
|
91
|
+
const auditPath = ".agent_memory/audit.json";
|
|
92
|
+
const inboxPath = ".agent_memory/inbox.json";
|
|
93
|
+
writeJson((0, node_path_1.join)(projectDir, metricsPath), metrics);
|
|
94
|
+
writeJson((0, node_path_1.join)(projectDir, auditPath), audit);
|
|
95
|
+
writeJson((0, node_path_1.join)(projectDir, inboxPath), inbox);
|
|
96
|
+
const artifacts = [
|
|
97
|
+
graphRegistryArtifact(projectDir, ".agent_memory/graph/graph.json", "memory graph", "memory_graph"),
|
|
98
|
+
graphRegistryArtifact(projectDir, ".agent_memory/code_graph/graph.json", "code graph", "code_graph"),
|
|
99
|
+
graphRegistryArtifact(projectDir, ".agent_memory/indexes/catalog.json", "packet catalog", "indexes"),
|
|
100
|
+
graphRegistryArtifact(projectDir, ".agent_memory/indexes/graph.json", "memory graph index", "indexes"),
|
|
101
|
+
graphRegistryArtifact(projectDir, ".agent_memory/indexes/code-graph.json", "code graph index", "indexes"),
|
|
102
|
+
graphRegistryArtifact(projectDir, metricsPath, "metrics report", "metrics"),
|
|
103
|
+
graphRegistryArtifact(projectDir, auditPath, "audit report", "audit"),
|
|
104
|
+
graphRegistryArtifact(projectDir, inboxPath, "memory inbox report", "inbox"),
|
|
105
|
+
].filter((artifact) => Boolean(artifact));
|
|
106
|
+
const packets = (0, kernel_js_1.loadApprovedPackets)(projectDir).sort((a, b) => a.id.localeCompare(b.id));
|
|
107
|
+
const payload = {
|
|
108
|
+
schema_version: 1,
|
|
109
|
+
repo_key: repoKey(projectDir),
|
|
110
|
+
project_dir: projectDir,
|
|
111
|
+
generated_at: nowIso(),
|
|
112
|
+
repo_state: {
|
|
113
|
+
branch: gitBranch(projectDir),
|
|
114
|
+
head: gitHead(projectDir),
|
|
115
|
+
merge_base: gitMergeBase(projectDir),
|
|
116
|
+
},
|
|
117
|
+
artifacts,
|
|
118
|
+
sources: {
|
|
119
|
+
packet_schema_version: kernel_js_1.PACKET_SCHEMA_VERSION,
|
|
120
|
+
packet_count: packets.length,
|
|
121
|
+
packets: packets.map((packet) => ({
|
|
122
|
+
id: packet.id,
|
|
123
|
+
title: packet.title,
|
|
124
|
+
type: packet.type,
|
|
125
|
+
status: packet.status,
|
|
126
|
+
updated_at: packet.updated_at,
|
|
127
|
+
content_sha256: (0, node_crypto_1.createHash)("sha256").update(canonicalPacketText(packet)).digest("hex"),
|
|
128
|
+
})),
|
|
129
|
+
},
|
|
130
|
+
reports: {
|
|
131
|
+
metrics: {
|
|
132
|
+
path: metricsPath,
|
|
133
|
+
readiness_score: metrics.harness.readiness_score,
|
|
134
|
+
evidence_coverage_percent: metrics.memory_graph.evidence_coverage_percent,
|
|
135
|
+
},
|
|
136
|
+
audit: {
|
|
137
|
+
path: auditPath,
|
|
138
|
+
ok: audit.ok,
|
|
139
|
+
trust_score: audit.trust_score,
|
|
140
|
+
structured_coverage_percent: audit.checks.structured_memory.coverage_percent,
|
|
141
|
+
precise_coverage_percent: audit.checks.code_graph.precise_coverage_percent,
|
|
142
|
+
},
|
|
143
|
+
inbox: {
|
|
144
|
+
path: inboxPath,
|
|
145
|
+
ok: inbox.ok,
|
|
146
|
+
pending: inbox.counts.pending,
|
|
147
|
+
stale: inbox.counts.stale,
|
|
148
|
+
duplicates: inbox.counts.duplicates,
|
|
149
|
+
missing_context: inbox.counts.missing_context,
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
const manifest = (0, index_js_1.createSignedManifest)({
|
|
154
|
+
kind: "graph_registry",
|
|
155
|
+
name: `${repoKey(projectDir)} graph registry`,
|
|
156
|
+
version: payload.repo_state.head?.slice(0, 12) ?? payload.generated_at.slice(0, 10),
|
|
157
|
+
keyId: `${repoKey(projectDir)}-local`,
|
|
158
|
+
payload,
|
|
159
|
+
});
|
|
160
|
+
const path = (0, node_path_1.join)((0, kernel_js_1.graphRegistryDir)(projectDir), "manifest.json");
|
|
161
|
+
writeJson(path, manifest);
|
|
162
|
+
const errors = [
|
|
163
|
+
...(!artifacts.some((artifact) => artifact.kind === "memory_graph") ? ["memory graph artifact missing"] : []),
|
|
164
|
+
...(!artifacts.some((artifact) => artifact.kind === "code_graph") ? ["code graph artifact missing"] : []),
|
|
165
|
+
];
|
|
166
|
+
return { ok: errors.length === 0, project_dir: projectDir, path, manifest, artifacts, errors };
|
|
167
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,7 @@ const index_js_1 = require("@modelcontextprotocol/sdk/server/index.js");
|
|
|
7
7
|
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
8
8
|
const types_js_1 = require("@modelcontextprotocol/sdk/types.js");
|
|
9
9
|
const kernel_js_1 = require("./kernel.js");
|
|
10
|
+
const graph_registry_js_1 = require("./graph-registry.js");
|
|
10
11
|
const BASE_URL = "https://raw.githubusercontent.com/kage-core/kage-graph/master";
|
|
11
12
|
async function fetchText(url) {
|
|
12
13
|
const res = await fetch(url);
|
|
@@ -145,6 +146,17 @@ function listTools() {
|
|
|
145
146
|
required: ["project_dir", "query"],
|
|
146
147
|
},
|
|
147
148
|
},
|
|
149
|
+
{
|
|
150
|
+
name: "kage_graph_registry",
|
|
151
|
+
description: "Build a signed graph-registry manifest for generated memory graph, code graph, indexes, metrics, audit, inbox, source packet IDs, packet hashes, and repo git state.",
|
|
152
|
+
inputSchema: {
|
|
153
|
+
type: "object",
|
|
154
|
+
properties: {
|
|
155
|
+
project_dir: { type: "string" },
|
|
156
|
+
},
|
|
157
|
+
required: ["project_dir"],
|
|
158
|
+
},
|
|
159
|
+
},
|
|
148
160
|
{
|
|
149
161
|
name: "kage_code_graph",
|
|
150
162
|
description: "Query the source-derived codebase graph: files, symbols, imports, calls, routes, tests, package scripts. This is generated from code, not learned memory.",
|
|
@@ -159,6 +171,17 @@ function listTools() {
|
|
|
159
171
|
required: ["project_dir"],
|
|
160
172
|
},
|
|
161
173
|
},
|
|
174
|
+
{
|
|
175
|
+
name: "kage_code_index",
|
|
176
|
+
description: "Write .agent_memory/code_index/lsp-symbols.json, an LSP-compatible symbol artifact consumed by the code graph for higher parser coverage.",
|
|
177
|
+
inputSchema: {
|
|
178
|
+
type: "object",
|
|
179
|
+
properties: {
|
|
180
|
+
project_dir: { type: "string" },
|
|
181
|
+
},
|
|
182
|
+
required: ["project_dir"],
|
|
183
|
+
},
|
|
184
|
+
},
|
|
162
185
|
{
|
|
163
186
|
name: "kage_metrics",
|
|
164
187
|
description: "Return concise Kage adoption and quality metrics: code graph counts, language/parser coverage, memory graph evidence coverage, pending/approved packets, validation state, and readiness score.",
|
|
@@ -170,6 +193,28 @@ function listTools() {
|
|
|
170
193
|
required: ["project_dir"],
|
|
171
194
|
},
|
|
172
195
|
},
|
|
196
|
+
{
|
|
197
|
+
name: "kage_audit",
|
|
198
|
+
description: "Audit whether repo memory and code intelligence are trustworthy: validation, memory inbox, structured context coverage, code graph precision, graph links, and concrete recommendations.",
|
|
199
|
+
inputSchema: {
|
|
200
|
+
type: "object",
|
|
201
|
+
properties: {
|
|
202
|
+
project_dir: { type: "string" },
|
|
203
|
+
},
|
|
204
|
+
required: ["project_dir"],
|
|
205
|
+
},
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "kage_inbox",
|
|
209
|
+
description: "Return an actionable memory review inbox: pending packets, stale packets, duplicates, missing structured context, validation issues, and recommended actions.",
|
|
210
|
+
inputSchema: {
|
|
211
|
+
type: "object",
|
|
212
|
+
properties: {
|
|
213
|
+
project_dir: { type: "string" },
|
|
214
|
+
},
|
|
215
|
+
required: ["project_dir"],
|
|
216
|
+
},
|
|
217
|
+
},
|
|
173
218
|
{
|
|
174
219
|
name: "kage_refresh",
|
|
175
220
|
description: "Rebuild repo indexes, code graph, memory graph, metrics, and stale-memory metadata. Agents should run this after meaningful file changes and before PR checks.",
|
|
@@ -658,6 +703,13 @@ async function callTool(name, args) {
|
|
|
658
703
|
content: [{ type: "text", text: result.context_block }],
|
|
659
704
|
};
|
|
660
705
|
}
|
|
706
|
+
if (name === "kage_graph_registry") {
|
|
707
|
+
const result = (0, graph_registry_js_1.buildGraphRegistryManifest)(String(args?.project_dir ?? ""));
|
|
708
|
+
return {
|
|
709
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
710
|
+
isError: !result.ok,
|
|
711
|
+
};
|
|
712
|
+
}
|
|
661
713
|
if (name === "kage_code_graph") {
|
|
662
714
|
const projectDir = String(args?.project_dir ?? "");
|
|
663
715
|
const query = typeof args?.query === "string" ? args.query : "";
|
|
@@ -672,12 +724,33 @@ async function callTool(name, args) {
|
|
|
672
724
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
673
725
|
};
|
|
674
726
|
}
|
|
727
|
+
if (name === "kage_code_index") {
|
|
728
|
+
const result = (0, kernel_js_1.writeLspSymbolIndex)(String(args?.project_dir ?? ""));
|
|
729
|
+
return {
|
|
730
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
731
|
+
isError: !result.ok,
|
|
732
|
+
};
|
|
733
|
+
}
|
|
675
734
|
if (name === "kage_metrics") {
|
|
676
735
|
const result = (0, kernel_js_1.kageMetrics)(String(args?.project_dir ?? ""));
|
|
677
736
|
return {
|
|
678
737
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
679
738
|
};
|
|
680
739
|
}
|
|
740
|
+
if (name === "kage_audit") {
|
|
741
|
+
const result = (0, kernel_js_1.auditProject)(String(args?.project_dir ?? ""));
|
|
742
|
+
return {
|
|
743
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
744
|
+
isError: !result.ok,
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
if (name === "kage_inbox") {
|
|
748
|
+
const result = (0, kernel_js_1.memoryInbox)(String(args?.project_dir ?? ""));
|
|
749
|
+
return {
|
|
750
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
751
|
+
isError: !result.ok,
|
|
752
|
+
};
|
|
753
|
+
}
|
|
681
754
|
if (name === "kage_refresh") {
|
|
682
755
|
const result = (0, kernel_js_1.refreshProject)(String(args?.project_dir ?? ""));
|
|
683
756
|
return {
|