@kage-core/kage-graph-mcp 1.1.13 → 1.1.15
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 +89 -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 +697 -26
- package/dist/registry/index.js +1 -1
- package/dist/release.js +121 -0
- package/package.json +4 -2
- 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,31 @@ This package exposes two surfaces:
|
|
|
9
9
|
|
|
10
10
|
## Latest Release
|
|
11
11
|
|
|
12
|
+
`1.1.15` hardens the npm release path and memory-only review flow:
|
|
13
|
+
|
|
14
|
+
- `npm run release:npm:dry-run` runs the guarded release checks without
|
|
15
|
+
publishing.
|
|
16
|
+
- `npm run release:npm` builds the release helper, requires a clean worktree,
|
|
17
|
+
fetches the remote branch, verifies local `HEAD` contains `origin/<branch>`,
|
|
18
|
+
runs tests and `npm pack --dry-run`, pushes the branch before publishing,
|
|
19
|
+
publishes with `--access public`, verifies npm registry metadata, and performs
|
|
20
|
+
a smoke install.
|
|
21
|
+
- all git steps run with `GIT_EDITOR=true` so agent sessions cannot get stuck in
|
|
22
|
+
an interactive commit or rebase editor.
|
|
23
|
+
- `kage propose --from-diff` now includes repo memory packet-only changes from
|
|
24
|
+
`.agent_memory/packets/*.json` and `.agent_memory/pending/*.json`.
|
|
25
|
+
|
|
26
|
+
`1.1.14` publishes the memory/code graph trust and retrieval pass:
|
|
27
|
+
|
|
28
|
+
- recall now uses vectorless BM25 lexical ranking with graph, path/type/tag,
|
|
29
|
+
intent, freshness, quality, and feedback boosts.
|
|
30
|
+
- `kage audit`, `kage inbox`, `kage code-index`, and `kage graph-registry`
|
|
31
|
+
are documented as first-class CLI and MCP surfaces.
|
|
32
|
+
- the viewer coalesces memory graph code entities with code graph nodes and
|
|
33
|
+
highlights memory-code links.
|
|
34
|
+
- README and the website now report the current 79-test proof state and BM25
|
|
35
|
+
retrieval behavior.
|
|
36
|
+
|
|
12
37
|
`1.1.13` switches future Kage releases to GPL-3.0-only:
|
|
13
38
|
|
|
14
39
|
- package metadata now advertises `GPL-3.0-only`.
|
|
@@ -21,16 +46,28 @@ This package exposes two surfaces:
|
|
|
21
46
|
- npm README now includes this explicit release note.
|
|
22
47
|
- Root README leads with the animated Kage demo GIF.
|
|
23
48
|
- 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 .`.
|
|
49
|
+
- Hosted viewer publishes Kage repo graph, code graph, metrics, and inbox from
|
|
50
|
+
GitHub Pages while local repos still use `kage viewer --project .`.
|
|
26
51
|
|
|
27
52
|
## Build
|
|
28
53
|
|
|
29
54
|
```bash
|
|
30
55
|
npm install
|
|
31
56
|
npm run build
|
|
57
|
+
npm run release:npm:dry-run
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Publishing from the repo should use the guarded release script after the release
|
|
61
|
+
commit is ready:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
npm run release:npm
|
|
32
65
|
```
|
|
33
66
|
|
|
67
|
+
The script fetches the current branch and blocks if the remote branch is not an
|
|
68
|
+
ancestor of local `HEAD`, which prevents publishing an npm version from a branch
|
|
69
|
+
that cannot be pushed cleanly.
|
|
70
|
+
|
|
34
71
|
## CLI
|
|
35
72
|
|
|
36
73
|
```bash
|
|
@@ -46,14 +83,18 @@ kage doctor --project /path/to/repo
|
|
|
46
83
|
kage index --project /path/to/repo
|
|
47
84
|
kage refresh --project /path/to/repo
|
|
48
85
|
kage branch --project /path/to/repo
|
|
86
|
+
kage code-index --project /path/to/repo
|
|
49
87
|
kage code-graph --project /path/to/repo
|
|
50
88
|
kage code-graph "createApp routes tests" --project /path/to/repo
|
|
51
89
|
kage graph --project /path/to/repo
|
|
52
90
|
kage graph --project /path/to/repo --mermaid
|
|
53
91
|
kage graph "test command" --project /path/to/repo
|
|
92
|
+
kage graph-registry --project /path/to/repo
|
|
54
93
|
kage recall "how do I run tests" --project /path/to/repo
|
|
55
94
|
kage recall "how do I run tests" --project /path/to/repo --explain --json
|
|
56
95
|
kage quality --project /path/to/repo
|
|
96
|
+
kage audit --project /path/to/repo
|
|
97
|
+
kage inbox --project /path/to/repo
|
|
57
98
|
kage benchmark --project /path/to/repo
|
|
58
99
|
kage benchmark --project /path/to/repo --compare --task "how do I run tests"
|
|
59
100
|
kage viewer --project /path/to/repo
|
|
@@ -131,6 +172,12 @@ generic extraction in metrics and file parser coverage. This keeps installation
|
|
|
131
172
|
light while allowing teams to plug in the strongest indexer available for their
|
|
132
173
|
language stack.
|
|
133
174
|
|
|
175
|
+
`kage code-index --project <repo>` writes `.agent_memory/code_index/lsp-symbols.json`
|
|
176
|
+
in an LSP document-symbol-compatible shape using Kage's local parser. The CI,
|
|
177
|
+
PR, and sync workflows run it before refresh so the code graph has a committed
|
|
178
|
+
precise-index slot while teams can still replace or augment it with SCIP/LSIF
|
|
179
|
+
artifacts from their preferred toolchain.
|
|
180
|
+
|
|
134
181
|
The memory graph follows the same product direction as temporal context graph
|
|
135
182
|
systems such as Graphiti: immutable ingestion episodes, derived entities and
|
|
136
183
|
facts, evidence/provenance on every edge, confidence, branch/commit context, and
|
|
@@ -143,12 +190,36 @@ and parser coverage, code graph counts, evidence coverage, approved vs pending
|
|
|
143
190
|
memory, validation status, estimated tokens saved per recall, duplicate
|
|
144
191
|
candidates, average memory quality, and a readiness score.
|
|
145
192
|
|
|
193
|
+
Use `kage audit --project <repo>` or the `kage_audit` MCP tool before relying
|
|
194
|
+
on repo memory for agent work. Audit reports validation, pending memory inbox
|
|
195
|
+
size, structured context coverage, stale/duplicate risk, memory-to-code graph
|
|
196
|
+
links, precise code index coverage, and concrete recommendations such as
|
|
197
|
+
extending SCIP/LSIF/LSP coverage or adding structured
|
|
198
|
+
`why`/`verification`/`risk_if_forgotten` fields to high-value packets.
|
|
199
|
+
|
|
200
|
+
Use `kage inbox --project <repo>` or the `kage_inbox` MCP tool for the
|
|
201
|
+
actionable review queue. It consolidates pending packets, stale packets,
|
|
202
|
+
duplicates, missing structured context, validation warnings/errors, and concrete
|
|
203
|
+
actions into one report that the viewer also loads.
|
|
204
|
+
|
|
146
205
|
Use `kage benchmark --compare --task "<task>" --project <repo>` or
|
|
147
206
|
`kage_benchmark_compare` to compare the same task on the same repo with and
|
|
148
207
|
without Kage. It estimates manual full-file rediscovery tokens/steps, compares
|
|
149
208
|
them to compact Kage recall plus code graph context, and returns evidence plus
|
|
150
209
|
caveats for honest marketing proof.
|
|
151
210
|
|
|
211
|
+
Plain `kage benchmark --project <repo>` now includes explicit gates and an
|
|
212
|
+
overall score for recall hit rate, evidence coverage, useful memory ratio, and
|
|
213
|
+
code-flow coverage, so benchmark output has pass/fail criteria instead of loose
|
|
214
|
+
claims.
|
|
215
|
+
|
|
216
|
+
Use `kage graph-registry --project <repo>` or `kage_graph_registry` to write
|
|
217
|
+
`.agent_memory/graph_registry/manifest.json`. The manifest is signed with the
|
|
218
|
+
same canonical JSON scheme as registry bundles and records memory/code graph
|
|
219
|
+
artifact hashes, generated index/report paths, source packet IDs and packet
|
|
220
|
+
hashes, git state, audit trust, inbox counts, and metrics readiness. CI, PR, and
|
|
221
|
+
sync workflows build it after refresh.
|
|
222
|
+
|
|
152
223
|
Use `kage refresh --project <repo>` or the `kage_refresh` MCP tool after
|
|
153
224
|
meaningful file changes. Refresh rebuilds indexes, code graph, memory graph,
|
|
154
225
|
metrics, and stale-memory metadata. Memory is marked stale when status or
|
|
@@ -172,13 +243,15 @@ review.
|
|
|
172
243
|
clients. It accepts session, prompt, tool, file-change, command, test, and
|
|
173
244
|
session-end events; deduplicates them; scans for secrets and PII; and stores raw
|
|
174
245
|
observations locally only. `kage distill` turns useful observations into
|
|
175
|
-
repo-local packets with observation session source refs.
|
|
176
|
-
|
|
246
|
+
repo-local packets with observation session source refs. Distillation is not
|
|
247
|
+
limited to action-changing instructions: durable rationale, bug causes, issue
|
|
248
|
+
state, decisions, and code explanations are valid memory when source-backed. It
|
|
249
|
+
never publishes memory.
|
|
177
250
|
|
|
178
251
|
`kage recall --explain --json` exposes the hybrid scoring explanation used for
|
|
179
|
-
ranking:
|
|
180
|
-
placeholder for future local or external embedding providers.
|
|
181
|
-
is
|
|
252
|
+
ranking: BM25 lexical score, graph, path/type/tag, intent, freshness, quality,
|
|
253
|
+
feedback, and a vector placeholder for future local or external embedding providers.
|
|
254
|
+
Current fallback is vectorless BM25 plus graph retrieval.
|
|
182
255
|
|
|
183
256
|
`kage_context` is the primary MCP entrypoint for agents. It validates repo
|
|
184
257
|
memory, recalls relevant packets, and returns code/knowledge graph context in
|
|
@@ -192,6 +265,7 @@ one call. Agents should use it at task start instead of loading separate
|
|
|
192
265
|
- `GET /kage/status`
|
|
193
266
|
- `GET /kage/metrics`
|
|
194
267
|
- `GET /kage/quality`
|
|
268
|
+
- `GET /kage/inbox`
|
|
195
269
|
- `GET /kage/benchmark`
|
|
196
270
|
- `POST /kage/recall`
|
|
197
271
|
- `POST /kage/observe`
|
|
@@ -207,13 +281,13 @@ and refreshes generated graph/index artifacts after a short debounce.
|
|
|
207
281
|
Run `kage viewer --project <repo>` to start the local terminal console. It
|
|
208
282
|
serves the viewer and the selected repo's `.agent_memory/` files from the same
|
|
209
283
|
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.
|
|
284
|
+
metrics, memory inbox, review artifact, and pending packets when present.
|
|
285
|
+
Manual JSON selection remains as a fallback, not the main workflow.
|
|
212
286
|
|
|
213
287
|
The viewer renders nodes and relations in SVG, supports memory/code/combined
|
|
214
288
|
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.
|
|
289
|
+
quarantine items when present, surfaces the memory inbox, and marks risks such
|
|
290
|
+
as low-confidence or missing-evidence edges.
|
|
217
291
|
|
|
218
292
|
For demos or local docs, the viewer also accepts URL params:
|
|
219
293
|
|
|
@@ -232,8 +306,12 @@ Local repo tools:
|
|
|
232
306
|
|
|
233
307
|
- `kage_context`
|
|
234
308
|
- `kage_recall`
|
|
309
|
+
- `kage_graph_registry`
|
|
235
310
|
- `kage_code_graph`
|
|
311
|
+
- `kage_code_index`
|
|
236
312
|
- `kage_metrics`
|
|
313
|
+
- `kage_audit`
|
|
314
|
+
- `kage_inbox`
|
|
237
315
|
- `kage_refresh`
|
|
238
316
|
- `kage_pr_summarize`
|
|
239
317
|
- `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 {
|