@kage-core/kage-graph-mcp 1.1.25 → 1.1.27
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 +43 -2
- package/dist/cli.js +237 -0
- package/dist/daemon.js +19 -1
- package/dist/index.js +208 -0
- package/dist/kernel.js +1676 -12
- package/package.json +1 -1
- package/viewer/app.js +489 -2
- package/viewer/index.html +10 -2
- package/viewer/styles.css +174 -4
package/README.md
CHANGED
|
@@ -36,10 +36,18 @@ Restart your agent once after setup so MCP tools reload.
|
|
|
36
36
|
|
|
37
37
|
- repo-local memory for decisions, runbooks, bug fixes, gotchas, conventions,
|
|
38
38
|
and code explanations
|
|
39
|
-
- a code graph for files, symbols, imports, calls, routes, tests, and packages
|
|
39
|
+
- a code graph for files, symbols, imports, calls, routes, tests, and packages,
|
|
40
|
+
including generic call/test signals for non-TypeScript repos
|
|
40
41
|
- memory-code links so project knowledge points at the code it affects
|
|
42
|
+
- decision intelligence for why-memory coverage, stale/weak packets, and
|
|
43
|
+
important files that still lack linked repo knowledge
|
|
44
|
+
- lightweight workspace recall across sibling repos, including package and
|
|
45
|
+
route-contract links when existing code graphs expose them
|
|
46
|
+
- local git intelligence for risk, reviewers, contributor profiles, co-change
|
|
47
|
+
warnings, ownership silos, and module health
|
|
41
48
|
- `AGENTS.md` bootstrap instructions so agents recall context automatically
|
|
42
|
-
- a local viewer for memory, code graph,
|
|
49
|
+
- a local viewer for memory, code graph, decision memory, risk, module health,
|
|
50
|
+
workspace reports, metrics, evidence, and review state
|
|
43
51
|
- review and validation commands for stale or risky memory
|
|
44
52
|
|
|
45
53
|
No hosted service, external database, or API key is required.
|
|
@@ -52,8 +60,19 @@ kage init --project .
|
|
|
52
60
|
kage recall "how do I run tests" --project .
|
|
53
61
|
kage recall "how do I run tests" --project . --json --explain
|
|
54
62
|
kage code-graph "auth routes tests" --project .
|
|
63
|
+
kage cleanup-candidates --project . --json
|
|
64
|
+
kage dependency-path --project . --from src/app.ts --to src/auth.ts --json
|
|
65
|
+
kage module-health --project . --json
|
|
66
|
+
kage graph-insights --project . --json
|
|
67
|
+
kage workspace --project .. --json
|
|
68
|
+
kage workspace recall "auth header contract" --project .. --json
|
|
69
|
+
kage contributors --project . --json
|
|
70
|
+
kage decisions --project . --json
|
|
71
|
+
kage reviewers --project . --changed-files src/auth.ts,src/session.ts --json
|
|
72
|
+
kage risk --project . --targets src/auth.ts --json
|
|
55
73
|
kage learn --project . --learning "Use npm test after parser changes."
|
|
56
74
|
kage refresh --project .
|
|
75
|
+
kage hook install --project .
|
|
57
76
|
kage pr check --project .
|
|
58
77
|
kage metrics --project . --json
|
|
59
78
|
kage audit --project . --json
|
|
@@ -61,6 +80,10 @@ kage inbox --project . --json
|
|
|
61
80
|
kage viewer --project .
|
|
62
81
|
```
|
|
63
82
|
|
|
83
|
+
MCP agents should start with `kage_context`. When the query or target list
|
|
84
|
+
mentions file paths, it includes risk and dependency-path context alongside
|
|
85
|
+
memory recall.
|
|
86
|
+
|
|
64
87
|
For stale or wrong memory:
|
|
65
88
|
|
|
66
89
|
```bash
|
|
@@ -99,6 +122,7 @@ Kage keeps learned memory separate from generated code facts.
|
|
|
99
122
|
| Structural map | `.agent_memory/structural/` | files, symbols, imports |
|
|
100
123
|
| Code graph | `.agent_memory/code_graph/` | source-derived code facts |
|
|
101
124
|
| Metrics | `.agent_memory/metrics.json` | readiness, quality, coverage |
|
|
125
|
+
| Reports | `.agent_memory/reports/` | risk, contributors, decisions, module health, graph insights, workspace, quality, benchmark |
|
|
102
126
|
|
|
103
127
|
Repo-local packets are git-visible and reviewable. Generated indexes and graphs
|
|
104
128
|
are rebuildable.
|
|
@@ -110,10 +134,22 @@ Kage is optimized so repeat work scales with changed files, not the whole repo:
|
|
|
110
134
|
- read-only recall reuses fresh graph artifacts
|
|
111
135
|
- unchanged structural file facts are reused
|
|
112
136
|
- generated graphs are compact and avoid duplicated structural JSON
|
|
137
|
+
- optional git `post-commit` hooks keep repo memory and branch summaries current
|
|
113
138
|
- generated/vendor/cache paths are ignored
|
|
114
139
|
- huge files are represented safely instead of deeply expanded
|
|
115
140
|
- recall builds lookup maps once per query instead of repeatedly scanning graph
|
|
116
141
|
edges for every memory packet
|
|
142
|
+
- local risk reports include hidden co-change warnings and ownership-silo
|
|
143
|
+
signals from git history
|
|
144
|
+
- contributor reports show commits, recent activity, touched files/modules,
|
|
145
|
+
primary ownership, ownership silos, hotspot ownership, and commit category mix
|
|
146
|
+
- decision reports show why-memory coverage, weak/stale decision packets, and
|
|
147
|
+
high-signal source paths with no linked decision memory
|
|
148
|
+
- graph insights include language parser coverage and edge mix so large-repo
|
|
149
|
+
index gaps are visible instead of hidden
|
|
150
|
+
- the generic non-TypeScript indexer extracts bounded call edges and test
|
|
151
|
+
function coverage, while SCIP/LSP/LSIF/tree-sitter artifacts override it when
|
|
152
|
+
available
|
|
117
153
|
|
|
118
154
|
## Viewer
|
|
119
155
|
|
|
@@ -123,6 +159,11 @@ Open a local viewer for the current repo:
|
|
|
123
159
|
kage viewer --project .
|
|
124
160
|
```
|
|
125
161
|
|
|
162
|
+
The local viewer loads graph artifacts plus `.agent_memory/reports/*.json` and
|
|
163
|
+
shows a repo-intelligence cockpit for memory-code links, decision memory, risk,
|
|
164
|
+
contributors, module health, graph insights, workspace coverage, quality, and
|
|
165
|
+
benchmark proof.
|
|
166
|
+
|
|
126
167
|
Hosted demo:
|
|
127
168
|
|
|
128
169
|
```text
|
package/dist/cli.js
CHANGED
|
@@ -24,6 +24,9 @@ Usage:
|
|
|
24
24
|
kage daemon status --project <dir> [--json]
|
|
25
25
|
kage daemon doctor --project <dir> [--json]
|
|
26
26
|
kage viewer --project <dir> [--port 3113]
|
|
27
|
+
kage hook install --project <dir> [--json]
|
|
28
|
+
kage hook status --project <dir> [--json]
|
|
29
|
+
kage hook uninstall --project <dir> [--json]
|
|
27
30
|
kage refresh --project <dir> [--full] [--json]
|
|
28
31
|
kage gc --project <dir> [--dry-run] [--force] [--json]
|
|
29
32
|
kage pr summarize --project <dir> [--json]
|
|
@@ -31,6 +34,12 @@ Usage:
|
|
|
31
34
|
kage upgrade [--dry-run]
|
|
32
35
|
kage branch --project <dir> [--json]
|
|
33
36
|
kage metrics --project <dir> [--json]
|
|
37
|
+
kage contributors --project <dir> [--json]
|
|
38
|
+
kage decisions --project <dir> [--json]
|
|
39
|
+
kage module-health --project <dir> [--json]
|
|
40
|
+
kage graph-insights --project <dir> [--json]
|
|
41
|
+
kage workspace --project <workspace-dir> [--json]
|
|
42
|
+
kage workspace recall "<query>" --project <workspace-dir> [--json]
|
|
34
43
|
kage audit --project <dir> [--json]
|
|
35
44
|
kage inbox --project <dir> [--json]
|
|
36
45
|
kage quality --project <dir> [--json]
|
|
@@ -38,6 +47,10 @@ Usage:
|
|
|
38
47
|
kage benchmark --project <dir> --compare --task <task> [--json]
|
|
39
48
|
kage code-graph --project <dir> [--json]
|
|
40
49
|
kage code-graph "<query>" --project <dir> [--json]
|
|
50
|
+
kage cleanup-candidates --project <dir> [--json]
|
|
51
|
+
kage dependency-path --project <dir> --from <path> --to <path> [--json]
|
|
52
|
+
kage reviewers --project <dir> [--targets a,b] [--changed-files a,b] [--json]
|
|
53
|
+
kage risk --project <dir> [--targets a,b] [--changed-files a,b] [--json]
|
|
41
54
|
kage code-index --project <dir> [--json]
|
|
42
55
|
kage structural-index --project <dir> [--json]
|
|
43
56
|
kage graph --project <dir> [--json]
|
|
@@ -309,6 +322,37 @@ async function main() {
|
|
|
309
322
|
await (0, daemon_js_1.startViewer)(projectArg(args), { port: numberArg(args, "--port", 3113) });
|
|
310
323
|
return;
|
|
311
324
|
}
|
|
325
|
+
if (command === "hook") {
|
|
326
|
+
const action = args[1];
|
|
327
|
+
const projectDir = projectArg(args);
|
|
328
|
+
const result = action === "install"
|
|
329
|
+
? (0, kernel_js_1.kageHookInstall)(projectDir)
|
|
330
|
+
: action === "status"
|
|
331
|
+
? (0, kernel_js_1.kageHookStatus)(projectDir)
|
|
332
|
+
: action === "uninstall"
|
|
333
|
+
? (0, kernel_js_1.kageHookUninstall)(projectDir)
|
|
334
|
+
: null;
|
|
335
|
+
if (!result)
|
|
336
|
+
usage();
|
|
337
|
+
if (args.includes("--json")) {
|
|
338
|
+
console.log(JSON.stringify(result, null, 2));
|
|
339
|
+
if (!result.ok)
|
|
340
|
+
process.exit(2);
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
console.log(result.message);
|
|
344
|
+
if (result.hook_path)
|
|
345
|
+
console.log(`Hook: ${result.hook_path}`);
|
|
346
|
+
console.log(`Installed: ${result.installed ? "yes" : "no"}`);
|
|
347
|
+
console.log(`Changed: ${result.changed ? "yes" : "no"}`);
|
|
348
|
+
if (result.warnings.length)
|
|
349
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
350
|
+
if (result.errors.length)
|
|
351
|
+
console.log(`Errors:\n${result.errors.map((error) => ` - ${error}`).join("\n")}`);
|
|
352
|
+
if (!result.ok)
|
|
353
|
+
process.exit(2);
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
312
356
|
if (command === "gc") {
|
|
313
357
|
const project = projectArg(args);
|
|
314
358
|
const dryRun = args.includes("--dry-run");
|
|
@@ -506,6 +550,123 @@ async function main() {
|
|
|
506
550
|
console.log(`- ${symbol.kind} ${symbol.name} (${symbol.path}:${symbol.line})`);
|
|
507
551
|
return;
|
|
508
552
|
}
|
|
553
|
+
if (command === "risk") {
|
|
554
|
+
const result = (0, kernel_js_1.kageRisk)(projectArg(args), listArg(takeArg(args, "--targets")), listArg(takeArg(args, "--changed-files")));
|
|
555
|
+
if (args.includes("--json")) {
|
|
556
|
+
console.log(JSON.stringify(result, null, 2));
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
console.log("Kage risk assessment");
|
|
560
|
+
for (const item of Object.values(result.targets)) {
|
|
561
|
+
console.log(`- ${item.risk_summary}`);
|
|
562
|
+
if (item.dependents.length)
|
|
563
|
+
console.log(` Dependents: ${item.dependents.slice(0, 5).join(", ")}`);
|
|
564
|
+
if (item.git.co_change_partners.length)
|
|
565
|
+
console.log(` Co-change: ${item.git.co_change_partners.map((p) => `${p.file_path} (${p.count})`).join(", ")}`);
|
|
566
|
+
}
|
|
567
|
+
if (result.global_hotspots.length) {
|
|
568
|
+
console.log("Global hotspots:");
|
|
569
|
+
for (const hotspot of result.global_hotspots)
|
|
570
|
+
console.log(`- ${hotspot.file_path}: ${hotspot.commit_count_90d} commits in 90d`);
|
|
571
|
+
}
|
|
572
|
+
if (result.warnings.length)
|
|
573
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
if (command === "dependency-path") {
|
|
577
|
+
const from = takeArg(args, "--from");
|
|
578
|
+
const to = takeArg(args, "--to");
|
|
579
|
+
if (!from || !to)
|
|
580
|
+
usage();
|
|
581
|
+
const result = (0, kernel_js_1.kageDependencyPath)(projectArg(args), from, to);
|
|
582
|
+
if (args.includes("--json")) {
|
|
583
|
+
console.log(JSON.stringify(result, null, 2));
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
console.log(result.summary);
|
|
587
|
+
if (result.path.length)
|
|
588
|
+
console.log(`Path: ${result.path.join(" -> ")}`);
|
|
589
|
+
for (const edge of result.edges) {
|
|
590
|
+
console.log(`- ${edge.from_path}:${edge.line} ${edge.kind} ${edge.specifier} -> ${edge.to_path}`);
|
|
591
|
+
}
|
|
592
|
+
if (result.warnings.length)
|
|
593
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
if (command === "cleanup-candidates") {
|
|
597
|
+
const result = (0, kernel_js_1.kageCleanupCandidates)(projectArg(args));
|
|
598
|
+
if (args.includes("--json")) {
|
|
599
|
+
console.log(JSON.stringify(result, null, 2));
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
console.log(`Kage cleanup candidates: ${result.summary}`);
|
|
603
|
+
for (const candidate of result.candidates.slice(0, 20)) {
|
|
604
|
+
console.log(`- ${candidate.path}: ${candidate.confidence} (${Math.round(candidate.score * 100)}%)`);
|
|
605
|
+
console.log(` ${candidate.reasons.join("; ")}`);
|
|
606
|
+
}
|
|
607
|
+
if (result.warnings.length)
|
|
608
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
609
|
+
return;
|
|
610
|
+
}
|
|
611
|
+
if (command === "reviewers") {
|
|
612
|
+
const result = (0, kernel_js_1.kageReviewerSuggestions)(projectArg(args), listArg(takeArg(args, "--targets")), listArg(takeArg(args, "--changed-files")));
|
|
613
|
+
if (args.includes("--json")) {
|
|
614
|
+
console.log(JSON.stringify(result, null, 2));
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
console.log(`Kage reviewer suggestions: ${result.summary}`);
|
|
618
|
+
for (const suggestion of result.suggestions) {
|
|
619
|
+
console.log(`- ${suggestion.reviewer}: ${Math.round(suggestion.score)}%`);
|
|
620
|
+
console.log(` ${suggestion.reasons.slice(0, 3).join("; ")}`);
|
|
621
|
+
}
|
|
622
|
+
if (result.warnings.length)
|
|
623
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
if (command === "contributors") {
|
|
627
|
+
const result = (0, kernel_js_1.kageContributors)(projectArg(args));
|
|
628
|
+
if (args.includes("--json")) {
|
|
629
|
+
console.log(JSON.stringify(result, null, 2));
|
|
630
|
+
return;
|
|
631
|
+
}
|
|
632
|
+
console.log(`Kage contributors: ${result.summary}`);
|
|
633
|
+
for (const profile of result.contributors.slice(0, 10)) {
|
|
634
|
+
console.log(`- ${profile.contributor}: ${profile.commits_total} commits, ${profile.commits_90d} in 90d, ${profile.primary_owned_files} owned files`);
|
|
635
|
+
if (profile.files_touched.length)
|
|
636
|
+
console.log(` Top files: ${profile.files_touched.slice(0, 3).map((file) => `${file.path} (${file.commits})`).join(", ")}`);
|
|
637
|
+
if (profile.silo_files.length)
|
|
638
|
+
console.log(` Silo files: ${profile.silo_files.slice(0, 3).map((file) => `${file.path} (${Math.round(file.ownership_pct * 100)}%)`).join(", ")}`);
|
|
639
|
+
}
|
|
640
|
+
if (result.warnings.length)
|
|
641
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
642
|
+
return;
|
|
643
|
+
}
|
|
644
|
+
if (command === "decisions") {
|
|
645
|
+
const result = (0, kernel_js_1.kageDecisionIntelligence)(projectArg(args));
|
|
646
|
+
if (args.includes("--json")) {
|
|
647
|
+
console.log(JSON.stringify(result, null, 2));
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
console.log(`Kage decision intelligence: ${result.summary}`);
|
|
651
|
+
if (result.top_decisions.length) {
|
|
652
|
+
console.log("Top why-memory:");
|
|
653
|
+
for (const item of result.top_decisions.slice(0, 10)) {
|
|
654
|
+
console.log(`- ${item.title} (${item.type})`);
|
|
655
|
+
if (item.paths.length)
|
|
656
|
+
console.log(` Paths: ${item.paths.slice(0, 3).join(", ")}`);
|
|
657
|
+
if (item.why)
|
|
658
|
+
console.log(` Why: ${item.why}`);
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
if (result.coverage_gaps.length) {
|
|
662
|
+
console.log("Uncovered important paths:");
|
|
663
|
+
for (const gap of result.coverage_gaps.slice(0, 10))
|
|
664
|
+
console.log(`- ${gap.path}: ${gap.reason}`);
|
|
665
|
+
}
|
|
666
|
+
if (result.warnings.length)
|
|
667
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
509
670
|
if (command === "code-index") {
|
|
510
671
|
const result = (0, kernel_js_1.writeCodeIndex)(projectArg(args));
|
|
511
672
|
if (args.includes("--json")) {
|
|
@@ -616,6 +777,82 @@ async function main() {
|
|
|
616
777
|
}
|
|
617
778
|
return;
|
|
618
779
|
}
|
|
780
|
+
if (command === "module-health") {
|
|
781
|
+
const result = (0, kernel_js_1.kageModuleHealth)(projectArg(args));
|
|
782
|
+
if (args.includes("--json")) {
|
|
783
|
+
console.log(JSON.stringify(result, null, 2));
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
console.log(`Kage module health: ${result.summary}`);
|
|
787
|
+
for (const item of result.modules.slice(0, 20)) {
|
|
788
|
+
console.log(`- ${item.module}: ${item.grade} (${item.score}) - ${item.reasons.join("; ")}`);
|
|
789
|
+
}
|
|
790
|
+
if (result.warnings.length)
|
|
791
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
if (command === "graph-insights") {
|
|
795
|
+
const result = (0, kernel_js_1.kageGraphInsights)(projectArg(args));
|
|
796
|
+
if (args.includes("--json")) {
|
|
797
|
+
console.log(JSON.stringify(result, null, 2));
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
console.log(`Kage graph insights: ${result.summary}`);
|
|
801
|
+
if (result.central_files.length) {
|
|
802
|
+
console.log("Central files:");
|
|
803
|
+
for (const file of result.central_files.slice(0, 10))
|
|
804
|
+
console.log(`- ${file.path}: pagerank ${file.pagerank}, ${file.dependents} dependent(s)`);
|
|
805
|
+
}
|
|
806
|
+
if (result.dependency_cycles.length) {
|
|
807
|
+
console.log("Dependency cycles:");
|
|
808
|
+
for (const cycle of result.dependency_cycles.slice(0, 5))
|
|
809
|
+
console.log(`- ${cycle.files.join(" -> ")}`);
|
|
810
|
+
}
|
|
811
|
+
if (result.entry_flows.length) {
|
|
812
|
+
console.log("Entry flows:");
|
|
813
|
+
for (const flow of result.entry_flows.slice(0, 5))
|
|
814
|
+
console.log(`- ${flow.path.join(" -> ")}`);
|
|
815
|
+
}
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
818
|
+
if (command === "workspace") {
|
|
819
|
+
const subcommand = args[1];
|
|
820
|
+
if (subcommand === "recall") {
|
|
821
|
+
const query = args.find((arg, index) => index > 1 && !arg.startsWith("--") && !args[index - 1]?.startsWith("--"));
|
|
822
|
+
if (!query)
|
|
823
|
+
usage();
|
|
824
|
+
const result = (0, kernel_js_1.kageWorkspaceRecall)(projectArg(args), query, numberArg(args, "--limit", 8));
|
|
825
|
+
if (args.includes("--json")) {
|
|
826
|
+
console.log(JSON.stringify(result, null, 2));
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
console.log(result.context_block);
|
|
830
|
+
if (result.warnings.length)
|
|
831
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
832
|
+
return;
|
|
833
|
+
}
|
|
834
|
+
const result = (0, kernel_js_1.kageWorkspace)(projectArg(args));
|
|
835
|
+
if (args.includes("--json")) {
|
|
836
|
+
console.log(JSON.stringify(result, null, 2));
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
console.log(`Kage workspace: ${result.summary}`);
|
|
840
|
+
for (const repo of result.repos) {
|
|
841
|
+
const deps = repo.dependencies_on_workspace_repos.length
|
|
842
|
+
? ` depends on ${repo.dependencies_on_workspace_repos.map((dep) => dep.alias).join(", ")}`
|
|
843
|
+
: "";
|
|
844
|
+
console.log(`- ${repo.alias}: ${repo.path} (${repo.approved_packets} packets, ${repo.code_files} files)${deps}`);
|
|
845
|
+
}
|
|
846
|
+
if (result.route_contracts.length) {
|
|
847
|
+
console.log("Route contracts:");
|
|
848
|
+
for (const contract of result.route_contracts.slice(0, 10)) {
|
|
849
|
+
console.log(`- ${contract.provider_repo} ${contract.method} ${contract.path} -> ${contract.consumer_repo}/${contract.consumer_file}`);
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
if (result.warnings.length)
|
|
853
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
619
856
|
if (command === "audit") {
|
|
620
857
|
const result = (0, kernel_js_1.auditProject)(projectArg(args));
|
|
621
858
|
if (args.includes("--json")) {
|
package/dist/daemon.js
CHANGED
|
@@ -240,17 +240,35 @@ async function startViewer(projectDir, options = {}) {
|
|
|
240
240
|
const inboxPath = (0, node_path_1.join)(projectRoot, ".agent_memory", "inbox.json");
|
|
241
241
|
const reviewPath = (0, node_path_1.join)(projectRoot, ".agent_memory", "review", "memory-review.md");
|
|
242
242
|
const pendingDir = (0, node_path_1.join)(projectRoot, ".agent_memory", "pending");
|
|
243
|
+
const reportsDir = (0, node_path_1.join)(projectRoot, ".agent_memory", "reports");
|
|
244
|
+
const qualityPath = (0, node_path_1.join)(reportsDir, "quality.json");
|
|
245
|
+
const benchmarkPath = (0, node_path_1.join)(reportsDir, "benchmark.json");
|
|
246
|
+
const contributorsPath = (0, node_path_1.join)(reportsDir, "contributors.json");
|
|
247
|
+
const decisionsPath = (0, node_path_1.join)(reportsDir, "decisions.json");
|
|
248
|
+
const riskPath = (0, node_path_1.join)(reportsDir, "risk.json");
|
|
249
|
+
const moduleHealthPath = (0, node_path_1.join)(reportsDir, "module-health.json");
|
|
250
|
+
const graphInsightsPath = (0, node_path_1.join)(reportsDir, "graph-insights.json");
|
|
251
|
+
const workspacePath = (0, node_path_1.join)(reportsDir, "workspace.json");
|
|
243
252
|
// Pre-generate lightweight JSON reports so the viewer can load them directly.
|
|
244
253
|
try {
|
|
254
|
+
(0, node_fs_1.mkdirSync)(reportsDir, { recursive: true });
|
|
245
255
|
const metrics = (0, kernel_js_1.kageMetrics)(projectDir);
|
|
246
256
|
(0, node_fs_1.writeFileSync)(metricsPath, JSON.stringify(metrics, null, 2));
|
|
247
257
|
const inbox = (0, kernel_js_1.memoryInbox)(projectDir);
|
|
248
258
|
(0, node_fs_1.writeFileSync)(inboxPath, JSON.stringify(inbox, null, 2));
|
|
259
|
+
(0, node_fs_1.writeFileSync)(qualityPath, JSON.stringify((0, kernel_js_1.qualityReport)(projectDir), null, 2));
|
|
260
|
+
(0, node_fs_1.writeFileSync)(benchmarkPath, JSON.stringify((0, kernel_js_1.benchmarkProject)(projectDir), null, 2));
|
|
261
|
+
(0, node_fs_1.writeFileSync)(contributorsPath, JSON.stringify((0, kernel_js_1.kageContributors)(projectDir), null, 2));
|
|
262
|
+
(0, node_fs_1.writeFileSync)(decisionsPath, JSON.stringify((0, kernel_js_1.kageDecisionIntelligence)(projectDir), null, 2));
|
|
263
|
+
(0, node_fs_1.writeFileSync)(riskPath, JSON.stringify((0, kernel_js_1.kageRisk)(projectDir), null, 2));
|
|
264
|
+
(0, node_fs_1.writeFileSync)(moduleHealthPath, JSON.stringify((0, kernel_js_1.kageModuleHealth)(projectDir), null, 2));
|
|
265
|
+
(0, node_fs_1.writeFileSync)(graphInsightsPath, JSON.stringify((0, kernel_js_1.kageGraphInsights)(projectDir), null, 2));
|
|
266
|
+
(0, node_fs_1.writeFileSync)(workspacePath, JSON.stringify((0, kernel_js_1.kageWorkspace)(projectDir), null, 2));
|
|
249
267
|
}
|
|
250
268
|
catch {
|
|
251
269
|
// non-fatal: viewer will show 404 for reports if generation fails
|
|
252
270
|
}
|
|
253
|
-
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)}&view=code`;
|
|
271
|
+
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)}&quality=${encodeURIComponent(qualityPath)}&benchmark=${encodeURIComponent(benchmarkPath)}&contributors=${encodeURIComponent(contributorsPath)}&decisions=${encodeURIComponent(decisionsPath)}&risk=${encodeURIComponent(riskPath)}&moduleHealth=${encodeURIComponent(moduleHealthPath)}&graphInsights=${encodeURIComponent(graphInsightsPath)}&workspace=${encodeURIComponent(workspacePath)}&view=code`;
|
|
254
272
|
const server = (0, node_http_1.createServer)((req, res) => {
|
|
255
273
|
const requestUrl = new URL(req.url ?? "/", `http://${host}:${port}`);
|
|
256
274
|
let filePath = null;
|
package/dist/index.js
CHANGED
|
@@ -55,6 +55,25 @@ function arrayArg(value) {
|
|
|
55
55
|
return value.split(",").map((item) => item.trim()).filter(Boolean);
|
|
56
56
|
return [];
|
|
57
57
|
}
|
|
58
|
+
function filePathHints(query) {
|
|
59
|
+
const matches = query.match(/[A-Za-z0-9_./@-]+\.(?:ts|tsx|js|jsx|mjs|cjs|py|go|rs|java|kt|kts|rb|php|cs|c|h|cc|cpp|hpp|swift|json|md)\b/g) ?? [];
|
|
60
|
+
return [...new Set(matches.map((match) => match.replace(/^\.\//, "")).filter((match) => !/^https?:\/\//.test(match)))];
|
|
61
|
+
}
|
|
62
|
+
function wantsDependencyPath(query) {
|
|
63
|
+
return /\b(connect|connected|dependency|depend|depends|path|impact|flow|trace)\b/i.test(query);
|
|
64
|
+
}
|
|
65
|
+
function riskContextBlock(result) {
|
|
66
|
+
const targets = Object.values(result.targets);
|
|
67
|
+
if (!targets.length)
|
|
68
|
+
return "";
|
|
69
|
+
const lines = targets.slice(0, 5).map((item) => {
|
|
70
|
+
const coChange = item.git.co_change_partners.length
|
|
71
|
+
? ` Co-change: ${item.git.co_change_partners.slice(0, 3).map((partner) => `${partner.file_path} (${partner.count})`).join(", ")}.`
|
|
72
|
+
: "";
|
|
73
|
+
return `- ${item.risk_summary}${coChange}`;
|
|
74
|
+
});
|
|
75
|
+
return `\n## Risk Signals\n${lines.join("\n")}`;
|
|
76
|
+
}
|
|
58
77
|
const server = new index_js_1.Server({ name: "kage-graph", version: "1.1.7" }, { capabilities: { tools: {} } });
|
|
59
78
|
function listTools() {
|
|
60
79
|
return [
|
|
@@ -70,6 +89,8 @@ function listTools() {
|
|
|
70
89
|
project_dir: { type: "string", description: "Absolute path to the project root" },
|
|
71
90
|
query: { type: "string", description: "The task or question — used for both memory recall and code graph search" },
|
|
72
91
|
limit: { type: "number", description: "Max memory packets to return (default 5)" },
|
|
92
|
+
targets: { type: "array", items: { type: "string" }, description: "Optional files the agent may edit or explain; used for risk context" },
|
|
93
|
+
changed_files: { type: "array", items: { type: "string" }, description: "Optional changed files for pre-edit or PR risk context" },
|
|
73
94
|
},
|
|
74
95
|
required: ["project_dir", "query"],
|
|
75
96
|
},
|
|
@@ -172,6 +193,78 @@ function listTools() {
|
|
|
172
193
|
required: ["project_dir"],
|
|
173
194
|
},
|
|
174
195
|
},
|
|
196
|
+
{
|
|
197
|
+
name: "kage_risk",
|
|
198
|
+
description: "Assess modification risk for files using Kage's code graph plus local git history: dependents, impact surface, churn, ownership, co-change partners, and test gaps. Use before editing hotspot or shared files.",
|
|
199
|
+
inputSchema: {
|
|
200
|
+
type: "object",
|
|
201
|
+
properties: {
|
|
202
|
+
project_dir: { type: "string" },
|
|
203
|
+
targets: { type: "array", items: { type: "string" }, description: "File paths to assess" },
|
|
204
|
+
changed_files: { type: "array", items: { type: "string" }, description: "Optional PR/branch changed files. If targets is omitted, these are assessed." },
|
|
205
|
+
},
|
|
206
|
+
required: ["project_dir"],
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
name: "kage_dependency_path",
|
|
211
|
+
description: "Find how two files are connected in Kage's source-derived code graph. Reports direct dependency direction, reverse impact direction, or undirected graph connection.",
|
|
212
|
+
inputSchema: {
|
|
213
|
+
type: "object",
|
|
214
|
+
properties: {
|
|
215
|
+
project_dir: { type: "string" },
|
|
216
|
+
from: { type: "string", description: "Source file path or unique suffix" },
|
|
217
|
+
to: { type: "string", description: "Target file path or unique suffix" },
|
|
218
|
+
},
|
|
219
|
+
required: ["project_dir", "from", "to"],
|
|
220
|
+
},
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
name: "kage_cleanup_candidates",
|
|
224
|
+
description: "Find conservative cleanup candidates from Kage's code graph. Reports unreferenced source files with confidence and reasons; never auto-deletes.",
|
|
225
|
+
inputSchema: {
|
|
226
|
+
type: "object",
|
|
227
|
+
properties: {
|
|
228
|
+
project_dir: { type: "string" },
|
|
229
|
+
},
|
|
230
|
+
required: ["project_dir"],
|
|
231
|
+
},
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
name: "kage_reviewers",
|
|
235
|
+
description: "Suggest reviewers for target or changed files from local git authorship, recent edits, and code-graph co-change ownership. Does not contact GitHub or external services.",
|
|
236
|
+
inputSchema: {
|
|
237
|
+
type: "object",
|
|
238
|
+
properties: {
|
|
239
|
+
project_dir: { type: "string" },
|
|
240
|
+
targets: { type: "array", items: { type: "string" }, description: "File paths to review" },
|
|
241
|
+
changed_files: { type: "array", items: { type: "string" }, description: "Optional PR/branch changed files. If targets is omitted, these are used." },
|
|
242
|
+
},
|
|
243
|
+
required: ["project_dir"],
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
name: "kage_contributors",
|
|
248
|
+
description: "Build local contributor profiles from git history: commits, recent activity, touched files, modules, ownership silos, hotspot ownership, and commit category mix.",
|
|
249
|
+
inputSchema: {
|
|
250
|
+
type: "object",
|
|
251
|
+
properties: {
|
|
252
|
+
project_dir: { type: "string" },
|
|
253
|
+
},
|
|
254
|
+
required: ["project_dir"],
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
name: "kage_decisions",
|
|
259
|
+
description: "Summarize Kage why-memory for a repo: decisions, gotchas, runbooks, conventions, code explanations, path coverage, weak/stale memory, and important code paths that still lack decision memory.",
|
|
260
|
+
inputSchema: {
|
|
261
|
+
type: "object",
|
|
262
|
+
properties: {
|
|
263
|
+
project_dir: { type: "string" },
|
|
264
|
+
},
|
|
265
|
+
required: ["project_dir"],
|
|
266
|
+
},
|
|
267
|
+
},
|
|
175
268
|
{
|
|
176
269
|
name: "kage_code_index",
|
|
177
270
|
description: "Write external code index artifacts consumed by the code graph. Prefers SCIP when scip-typescript and scip are installed, then falls back to the built-in LSP-compatible symbol index.",
|
|
@@ -205,6 +298,52 @@ function listTools() {
|
|
|
205
298
|
required: ["project_dir"],
|
|
206
299
|
},
|
|
207
300
|
},
|
|
301
|
+
{
|
|
302
|
+
name: "kage_module_health",
|
|
303
|
+
description: "Return local module health scorecards from Kage's code graph, test signals, cleanup candidates, git churn, and ownership concentration.",
|
|
304
|
+
inputSchema: {
|
|
305
|
+
type: "object",
|
|
306
|
+
properties: {
|
|
307
|
+
project_dir: { type: "string" },
|
|
308
|
+
},
|
|
309
|
+
required: ["project_dir"],
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
name: "kage_graph_insights",
|
|
314
|
+
description: "Return deterministic code graph intelligence: central files, dependency cycles, import communities, and short entry flows. Use to orient agents before broad architectural edits.",
|
|
315
|
+
inputSchema: {
|
|
316
|
+
type: "object",
|
|
317
|
+
properties: {
|
|
318
|
+
project_dir: { type: "string", description: "Absolute path to the project root" },
|
|
319
|
+
},
|
|
320
|
+
required: ["project_dir"],
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
name: "kage_workspace",
|
|
325
|
+
description: "Summarize a local multi-repo workspace: discovered git repos, Kage memory coverage, code graph counts, and package dependencies between repos. Use when a task spans multiple sibling repos.",
|
|
326
|
+
inputSchema: {
|
|
327
|
+
type: "object",
|
|
328
|
+
properties: {
|
|
329
|
+
project_dir: { type: "string", description: "Workspace root directory to scan for git repos" },
|
|
330
|
+
},
|
|
331
|
+
required: ["project_dir"],
|
|
332
|
+
},
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
name: "kage_workspace_recall",
|
|
336
|
+
description: "Recall Kage memory across every indexed repo in a local workspace and rank the combined hits. Use for cross-repo teammate knowledge and shared context.",
|
|
337
|
+
inputSchema: {
|
|
338
|
+
type: "object",
|
|
339
|
+
properties: {
|
|
340
|
+
project_dir: { type: "string", description: "Workspace root directory to scan for git repos" },
|
|
341
|
+
query: { type: "string", description: "Question or task to recall across repos" },
|
|
342
|
+
limit: { type: "number", description: "Max combined hits to return (default 8)" },
|
|
343
|
+
},
|
|
344
|
+
required: ["project_dir", "query"],
|
|
345
|
+
},
|
|
346
|
+
},
|
|
208
347
|
{
|
|
209
348
|
name: "kage_audit",
|
|
210
349
|
description: "Audit whether repo memory and code intelligence are trustworthy: validation, memory inbox, structured context coverage, code graph precision, graph links, and concrete recommendations.",
|
|
@@ -694,9 +833,18 @@ async function callTool(name, args) {
|
|
|
694
833
|
const recallResult = (0, kernel_js_1.recall)(projectDir, query, limit, false);
|
|
695
834
|
// graph facts on top of recall
|
|
696
835
|
const graphResult = (0, kernel_js_1.queryGraph)(projectDir, query, 5);
|
|
836
|
+
const explicitTargets = [...arrayArg(args?.targets), ...filePathHints(query)];
|
|
837
|
+
const changedFiles = arrayArg(args?.changed_files);
|
|
838
|
+
const riskResult = explicitTargets.length || changedFiles.length ? (0, kernel_js_1.kageRisk)(projectDir, explicitTargets, changedFiles) : null;
|
|
839
|
+
const pathHints = filePathHints(query);
|
|
840
|
+
const dependencyResult = wantsDependencyPath(query) && pathHints.length >= 2
|
|
841
|
+
? (0, kernel_js_1.kageDependencyPath)(projectDir, pathHints[0], pathHints[1])
|
|
842
|
+
: null;
|
|
697
843
|
const sections = [
|
|
698
844
|
recallResult.context_block,
|
|
699
845
|
graphResult.context_block ? `\n## Graph Facts\n${graphResult.context_block}` : "",
|
|
846
|
+
riskResult ? riskContextBlock(riskResult) : "",
|
|
847
|
+
dependencyResult ? `\n## Dependency Path\n${dependencyResult.summary}${dependencyResult.path.length ? `\nPath: ${dependencyResult.path.join(" -> ")}` : ""}` : "",
|
|
700
848
|
`\n_${validationText}_`,
|
|
701
849
|
].filter(Boolean).join("");
|
|
702
850
|
return {
|
|
@@ -736,6 +884,42 @@ async function callTool(name, args) {
|
|
|
736
884
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
737
885
|
};
|
|
738
886
|
}
|
|
887
|
+
if (name === "kage_risk") {
|
|
888
|
+
const result = (0, kernel_js_1.kageRisk)(String(args?.project_dir ?? ""), arrayArg(args?.targets), arrayArg(args?.changed_files));
|
|
889
|
+
return {
|
|
890
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
if (name === "kage_dependency_path") {
|
|
894
|
+
const result = (0, kernel_js_1.kageDependencyPath)(String(args?.project_dir ?? ""), String(args?.from ?? ""), String(args?.to ?? ""));
|
|
895
|
+
return {
|
|
896
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
897
|
+
};
|
|
898
|
+
}
|
|
899
|
+
if (name === "kage_cleanup_candidates") {
|
|
900
|
+
const result = (0, kernel_js_1.kageCleanupCandidates)(String(args?.project_dir ?? ""));
|
|
901
|
+
return {
|
|
902
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
if (name === "kage_reviewers") {
|
|
906
|
+
const result = (0, kernel_js_1.kageReviewerSuggestions)(String(args?.project_dir ?? ""), arrayArg(args?.targets), arrayArg(args?.changed_files));
|
|
907
|
+
return {
|
|
908
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
if (name === "kage_contributors") {
|
|
912
|
+
const result = (0, kernel_js_1.kageContributors)(String(args?.project_dir ?? ""));
|
|
913
|
+
return {
|
|
914
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
if (name === "kage_decisions") {
|
|
918
|
+
const result = (0, kernel_js_1.kageDecisionIntelligence)(String(args?.project_dir ?? ""));
|
|
919
|
+
return {
|
|
920
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
921
|
+
};
|
|
922
|
+
}
|
|
739
923
|
if (name === "kage_code_index") {
|
|
740
924
|
const result = (0, kernel_js_1.writeCodeIndex)(String(args?.project_dir ?? ""));
|
|
741
925
|
return {
|
|
@@ -755,6 +939,30 @@ async function callTool(name, args) {
|
|
|
755
939
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
756
940
|
};
|
|
757
941
|
}
|
|
942
|
+
if (name === "kage_module_health") {
|
|
943
|
+
const result = (0, kernel_js_1.kageModuleHealth)(String(args?.project_dir ?? ""));
|
|
944
|
+
return {
|
|
945
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
946
|
+
};
|
|
947
|
+
}
|
|
948
|
+
if (name === "kage_graph_insights") {
|
|
949
|
+
const result = (0, kernel_js_1.kageGraphInsights)(String(args?.project_dir ?? ""));
|
|
950
|
+
return {
|
|
951
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
if (name === "kage_workspace") {
|
|
955
|
+
const result = (0, kernel_js_1.kageWorkspace)(String(args?.project_dir ?? ""));
|
|
956
|
+
return {
|
|
957
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
958
|
+
};
|
|
959
|
+
}
|
|
960
|
+
if (name === "kage_workspace_recall") {
|
|
961
|
+
const result = (0, kernel_js_1.kageWorkspaceRecall)(String(args?.project_dir ?? ""), String(args?.query ?? ""), Number(args?.limit ?? 8));
|
|
962
|
+
return {
|
|
963
|
+
content: [{ type: "text", text: args?.json ? JSON.stringify(result, null, 2) : result.context_block }],
|
|
964
|
+
};
|
|
965
|
+
}
|
|
758
966
|
if (name === "kage_audit") {
|
|
759
967
|
const result = (0, kernel_js_1.auditProject)(String(args?.project_dir ?? ""));
|
|
760
968
|
return {
|