@kage-core/kage-graph-mcp 1.1.25 → 1.1.26
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 +41 -2
- package/dist/cli.js +203 -0
- package/dist/daemon.js +19 -1
- package/dist/index.js +208 -0
- package/dist/kernel.js +1523 -12
- package/package.json +1 -1
- package/viewer/app.js +238 -2
- package/viewer/index.html +10 -2
- package/viewer/styles.css +57 -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,6 +60,16 @@ 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 .
|
|
57
75
|
kage pr check --project .
|
|
@@ -61,6 +79,10 @@ kage inbox --project . --json
|
|
|
61
79
|
kage viewer --project .
|
|
62
80
|
```
|
|
63
81
|
|
|
82
|
+
MCP agents should start with `kage_context`. When the query or target list
|
|
83
|
+
mentions file paths, it includes risk and dependency-path context alongside
|
|
84
|
+
memory recall.
|
|
85
|
+
|
|
64
86
|
For stale or wrong memory:
|
|
65
87
|
|
|
66
88
|
```bash
|
|
@@ -99,6 +121,7 @@ Kage keeps learned memory separate from generated code facts.
|
|
|
99
121
|
| Structural map | `.agent_memory/structural/` | files, symbols, imports |
|
|
100
122
|
| Code graph | `.agent_memory/code_graph/` | source-derived code facts |
|
|
101
123
|
| Metrics | `.agent_memory/metrics.json` | readiness, quality, coverage |
|
|
124
|
+
| Reports | `.agent_memory/reports/` | risk, contributors, decisions, module health, graph insights, workspace, quality, benchmark |
|
|
102
125
|
|
|
103
126
|
Repo-local packets are git-visible and reviewable. Generated indexes and graphs
|
|
104
127
|
are rebuildable.
|
|
@@ -114,6 +137,17 @@ Kage is optimized so repeat work scales with changed files, not the whole repo:
|
|
|
114
137
|
- huge files are represented safely instead of deeply expanded
|
|
115
138
|
- recall builds lookup maps once per query instead of repeatedly scanning graph
|
|
116
139
|
edges for every memory packet
|
|
140
|
+
- local risk reports include hidden co-change warnings and ownership-silo
|
|
141
|
+
signals from git history
|
|
142
|
+
- contributor reports show commits, recent activity, touched files/modules,
|
|
143
|
+
primary ownership, ownership silos, hotspot ownership, and commit category mix
|
|
144
|
+
- decision reports show why-memory coverage, weak/stale decision packets, and
|
|
145
|
+
high-signal source paths with no linked decision memory
|
|
146
|
+
- graph insights include language parser coverage and edge mix so large-repo
|
|
147
|
+
index gaps are visible instead of hidden
|
|
148
|
+
- the generic non-TypeScript indexer extracts bounded call edges and test
|
|
149
|
+
function coverage, while SCIP/LSP/LSIF/tree-sitter artifacts override it when
|
|
150
|
+
available
|
|
117
151
|
|
|
118
152
|
## Viewer
|
|
119
153
|
|
|
@@ -123,6 +157,11 @@ Open a local viewer for the current repo:
|
|
|
123
157
|
kage viewer --project .
|
|
124
158
|
```
|
|
125
159
|
|
|
160
|
+
The local viewer loads graph artifacts plus `.agent_memory/reports/*.json` and
|
|
161
|
+
shows a repo-intelligence cockpit for memory-code links, decision memory, risk,
|
|
162
|
+
contributors, module health, graph insights, workspace coverage, quality, and
|
|
163
|
+
benchmark proof.
|
|
164
|
+
|
|
126
165
|
Hosted demo:
|
|
127
166
|
|
|
128
167
|
```text
|
package/dist/cli.js
CHANGED
|
@@ -31,6 +31,12 @@ Usage:
|
|
|
31
31
|
kage upgrade [--dry-run]
|
|
32
32
|
kage branch --project <dir> [--json]
|
|
33
33
|
kage metrics --project <dir> [--json]
|
|
34
|
+
kage contributors --project <dir> [--json]
|
|
35
|
+
kage decisions --project <dir> [--json]
|
|
36
|
+
kage module-health --project <dir> [--json]
|
|
37
|
+
kage graph-insights --project <dir> [--json]
|
|
38
|
+
kage workspace --project <workspace-dir> [--json]
|
|
39
|
+
kage workspace recall "<query>" --project <workspace-dir> [--json]
|
|
34
40
|
kage audit --project <dir> [--json]
|
|
35
41
|
kage inbox --project <dir> [--json]
|
|
36
42
|
kage quality --project <dir> [--json]
|
|
@@ -38,6 +44,10 @@ Usage:
|
|
|
38
44
|
kage benchmark --project <dir> --compare --task <task> [--json]
|
|
39
45
|
kage code-graph --project <dir> [--json]
|
|
40
46
|
kage code-graph "<query>" --project <dir> [--json]
|
|
47
|
+
kage cleanup-candidates --project <dir> [--json]
|
|
48
|
+
kage dependency-path --project <dir> --from <path> --to <path> [--json]
|
|
49
|
+
kage reviewers --project <dir> [--targets a,b] [--changed-files a,b] [--json]
|
|
50
|
+
kage risk --project <dir> [--targets a,b] [--changed-files a,b] [--json]
|
|
41
51
|
kage code-index --project <dir> [--json]
|
|
42
52
|
kage structural-index --project <dir> [--json]
|
|
43
53
|
kage graph --project <dir> [--json]
|
|
@@ -506,6 +516,123 @@ async function main() {
|
|
|
506
516
|
console.log(`- ${symbol.kind} ${symbol.name} (${symbol.path}:${symbol.line})`);
|
|
507
517
|
return;
|
|
508
518
|
}
|
|
519
|
+
if (command === "risk") {
|
|
520
|
+
const result = (0, kernel_js_1.kageRisk)(projectArg(args), listArg(takeArg(args, "--targets")), listArg(takeArg(args, "--changed-files")));
|
|
521
|
+
if (args.includes("--json")) {
|
|
522
|
+
console.log(JSON.stringify(result, null, 2));
|
|
523
|
+
return;
|
|
524
|
+
}
|
|
525
|
+
console.log("Kage risk assessment");
|
|
526
|
+
for (const item of Object.values(result.targets)) {
|
|
527
|
+
console.log(`- ${item.risk_summary}`);
|
|
528
|
+
if (item.dependents.length)
|
|
529
|
+
console.log(` Dependents: ${item.dependents.slice(0, 5).join(", ")}`);
|
|
530
|
+
if (item.git.co_change_partners.length)
|
|
531
|
+
console.log(` Co-change: ${item.git.co_change_partners.map((p) => `${p.file_path} (${p.count})`).join(", ")}`);
|
|
532
|
+
}
|
|
533
|
+
if (result.global_hotspots.length) {
|
|
534
|
+
console.log("Global hotspots:");
|
|
535
|
+
for (const hotspot of result.global_hotspots)
|
|
536
|
+
console.log(`- ${hotspot.file_path}: ${hotspot.commit_count_90d} commits in 90d`);
|
|
537
|
+
}
|
|
538
|
+
if (result.warnings.length)
|
|
539
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
540
|
+
return;
|
|
541
|
+
}
|
|
542
|
+
if (command === "dependency-path") {
|
|
543
|
+
const from = takeArg(args, "--from");
|
|
544
|
+
const to = takeArg(args, "--to");
|
|
545
|
+
if (!from || !to)
|
|
546
|
+
usage();
|
|
547
|
+
const result = (0, kernel_js_1.kageDependencyPath)(projectArg(args), from, to);
|
|
548
|
+
if (args.includes("--json")) {
|
|
549
|
+
console.log(JSON.stringify(result, null, 2));
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
console.log(result.summary);
|
|
553
|
+
if (result.path.length)
|
|
554
|
+
console.log(`Path: ${result.path.join(" -> ")}`);
|
|
555
|
+
for (const edge of result.edges) {
|
|
556
|
+
console.log(`- ${edge.from_path}:${edge.line} ${edge.kind} ${edge.specifier} -> ${edge.to_path}`);
|
|
557
|
+
}
|
|
558
|
+
if (result.warnings.length)
|
|
559
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (command === "cleanup-candidates") {
|
|
563
|
+
const result = (0, kernel_js_1.kageCleanupCandidates)(projectArg(args));
|
|
564
|
+
if (args.includes("--json")) {
|
|
565
|
+
console.log(JSON.stringify(result, null, 2));
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
console.log(`Kage cleanup candidates: ${result.summary}`);
|
|
569
|
+
for (const candidate of result.candidates.slice(0, 20)) {
|
|
570
|
+
console.log(`- ${candidate.path}: ${candidate.confidence} (${Math.round(candidate.score * 100)}%)`);
|
|
571
|
+
console.log(` ${candidate.reasons.join("; ")}`);
|
|
572
|
+
}
|
|
573
|
+
if (result.warnings.length)
|
|
574
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
575
|
+
return;
|
|
576
|
+
}
|
|
577
|
+
if (command === "reviewers") {
|
|
578
|
+
const result = (0, kernel_js_1.kageReviewerSuggestions)(projectArg(args), listArg(takeArg(args, "--targets")), listArg(takeArg(args, "--changed-files")));
|
|
579
|
+
if (args.includes("--json")) {
|
|
580
|
+
console.log(JSON.stringify(result, null, 2));
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
console.log(`Kage reviewer suggestions: ${result.summary}`);
|
|
584
|
+
for (const suggestion of result.suggestions) {
|
|
585
|
+
console.log(`- ${suggestion.reviewer}: ${Math.round(suggestion.score)}%`);
|
|
586
|
+
console.log(` ${suggestion.reasons.slice(0, 3).join("; ")}`);
|
|
587
|
+
}
|
|
588
|
+
if (result.warnings.length)
|
|
589
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
if (command === "contributors") {
|
|
593
|
+
const result = (0, kernel_js_1.kageContributors)(projectArg(args));
|
|
594
|
+
if (args.includes("--json")) {
|
|
595
|
+
console.log(JSON.stringify(result, null, 2));
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
console.log(`Kage contributors: ${result.summary}`);
|
|
599
|
+
for (const profile of result.contributors.slice(0, 10)) {
|
|
600
|
+
console.log(`- ${profile.contributor}: ${profile.commits_total} commits, ${profile.commits_90d} in 90d, ${profile.primary_owned_files} owned files`);
|
|
601
|
+
if (profile.files_touched.length)
|
|
602
|
+
console.log(` Top files: ${profile.files_touched.slice(0, 3).map((file) => `${file.path} (${file.commits})`).join(", ")}`);
|
|
603
|
+
if (profile.silo_files.length)
|
|
604
|
+
console.log(` Silo files: ${profile.silo_files.slice(0, 3).map((file) => `${file.path} (${Math.round(file.ownership_pct * 100)}%)`).join(", ")}`);
|
|
605
|
+
}
|
|
606
|
+
if (result.warnings.length)
|
|
607
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
if (command === "decisions") {
|
|
611
|
+
const result = (0, kernel_js_1.kageDecisionIntelligence)(projectArg(args));
|
|
612
|
+
if (args.includes("--json")) {
|
|
613
|
+
console.log(JSON.stringify(result, null, 2));
|
|
614
|
+
return;
|
|
615
|
+
}
|
|
616
|
+
console.log(`Kage decision intelligence: ${result.summary}`);
|
|
617
|
+
if (result.top_decisions.length) {
|
|
618
|
+
console.log("Top why-memory:");
|
|
619
|
+
for (const item of result.top_decisions.slice(0, 10)) {
|
|
620
|
+
console.log(`- ${item.title} (${item.type})`);
|
|
621
|
+
if (item.paths.length)
|
|
622
|
+
console.log(` Paths: ${item.paths.slice(0, 3).join(", ")}`);
|
|
623
|
+
if (item.why)
|
|
624
|
+
console.log(` Why: ${item.why}`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (result.coverage_gaps.length) {
|
|
628
|
+
console.log("Uncovered important paths:");
|
|
629
|
+
for (const gap of result.coverage_gaps.slice(0, 10))
|
|
630
|
+
console.log(`- ${gap.path}: ${gap.reason}`);
|
|
631
|
+
}
|
|
632
|
+
if (result.warnings.length)
|
|
633
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
509
636
|
if (command === "code-index") {
|
|
510
637
|
const result = (0, kernel_js_1.writeCodeIndex)(projectArg(args));
|
|
511
638
|
if (args.includes("--json")) {
|
|
@@ -616,6 +743,82 @@ async function main() {
|
|
|
616
743
|
}
|
|
617
744
|
return;
|
|
618
745
|
}
|
|
746
|
+
if (command === "module-health") {
|
|
747
|
+
const result = (0, kernel_js_1.kageModuleHealth)(projectArg(args));
|
|
748
|
+
if (args.includes("--json")) {
|
|
749
|
+
console.log(JSON.stringify(result, null, 2));
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
console.log(`Kage module health: ${result.summary}`);
|
|
753
|
+
for (const item of result.modules.slice(0, 20)) {
|
|
754
|
+
console.log(`- ${item.module}: ${item.grade} (${item.score}) - ${item.reasons.join("; ")}`);
|
|
755
|
+
}
|
|
756
|
+
if (result.warnings.length)
|
|
757
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
758
|
+
return;
|
|
759
|
+
}
|
|
760
|
+
if (command === "graph-insights") {
|
|
761
|
+
const result = (0, kernel_js_1.kageGraphInsights)(projectArg(args));
|
|
762
|
+
if (args.includes("--json")) {
|
|
763
|
+
console.log(JSON.stringify(result, null, 2));
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
console.log(`Kage graph insights: ${result.summary}`);
|
|
767
|
+
if (result.central_files.length) {
|
|
768
|
+
console.log("Central files:");
|
|
769
|
+
for (const file of result.central_files.slice(0, 10))
|
|
770
|
+
console.log(`- ${file.path}: pagerank ${file.pagerank}, ${file.dependents} dependent(s)`);
|
|
771
|
+
}
|
|
772
|
+
if (result.dependency_cycles.length) {
|
|
773
|
+
console.log("Dependency cycles:");
|
|
774
|
+
for (const cycle of result.dependency_cycles.slice(0, 5))
|
|
775
|
+
console.log(`- ${cycle.files.join(" -> ")}`);
|
|
776
|
+
}
|
|
777
|
+
if (result.entry_flows.length) {
|
|
778
|
+
console.log("Entry flows:");
|
|
779
|
+
for (const flow of result.entry_flows.slice(0, 5))
|
|
780
|
+
console.log(`- ${flow.path.join(" -> ")}`);
|
|
781
|
+
}
|
|
782
|
+
return;
|
|
783
|
+
}
|
|
784
|
+
if (command === "workspace") {
|
|
785
|
+
const subcommand = args[1];
|
|
786
|
+
if (subcommand === "recall") {
|
|
787
|
+
const query = args.find((arg, index) => index > 1 && !arg.startsWith("--") && !args[index - 1]?.startsWith("--"));
|
|
788
|
+
if (!query)
|
|
789
|
+
usage();
|
|
790
|
+
const result = (0, kernel_js_1.kageWorkspaceRecall)(projectArg(args), query, numberArg(args, "--limit", 8));
|
|
791
|
+
if (args.includes("--json")) {
|
|
792
|
+
console.log(JSON.stringify(result, null, 2));
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
console.log(result.context_block);
|
|
796
|
+
if (result.warnings.length)
|
|
797
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
798
|
+
return;
|
|
799
|
+
}
|
|
800
|
+
const result = (0, kernel_js_1.kageWorkspace)(projectArg(args));
|
|
801
|
+
if (args.includes("--json")) {
|
|
802
|
+
console.log(JSON.stringify(result, null, 2));
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
console.log(`Kage workspace: ${result.summary}`);
|
|
806
|
+
for (const repo of result.repos) {
|
|
807
|
+
const deps = repo.dependencies_on_workspace_repos.length
|
|
808
|
+
? ` depends on ${repo.dependencies_on_workspace_repos.map((dep) => dep.alias).join(", ")}`
|
|
809
|
+
: "";
|
|
810
|
+
console.log(`- ${repo.alias}: ${repo.path} (${repo.approved_packets} packets, ${repo.code_files} files)${deps}`);
|
|
811
|
+
}
|
|
812
|
+
if (result.route_contracts.length) {
|
|
813
|
+
console.log("Route contracts:");
|
|
814
|
+
for (const contract of result.route_contracts.slice(0, 10)) {
|
|
815
|
+
console.log(`- ${contract.provider_repo} ${contract.method} ${contract.path} -> ${contract.consumer_repo}/${contract.consumer_file}`);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
if (result.warnings.length)
|
|
819
|
+
console.log(`Warnings:\n${result.warnings.map((warning) => ` - ${warning}`).join("\n")}`);
|
|
820
|
+
return;
|
|
821
|
+
}
|
|
619
822
|
if (command === "audit") {
|
|
620
823
|
const result = (0, kernel_js_1.auditProject)(projectArg(args));
|
|
621
824
|
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 {
|