homegraph 1.5.5 → 1.5.6
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/CHANGELOG.md +14 -0
- package/dist/bin/homegraph.js +6 -0
- package/dist/bin/homegraph.js.map +1 -1
- package/dist/db/migrations.d.ts +1 -1
- package/dist/db/migrations.d.ts.map +1 -1
- package/dist/db/migrations.js +37 -1
- package/dist/db/migrations.js.map +1 -1
- package/dist/db/queries.d.ts +25 -0
- package/dist/db/queries.d.ts.map +1 -1
- package/dist/db/queries.js +50 -2
- package/dist/db/queries.js.map +1 -1
- package/dist/db/schema.sql +24 -0
- package/dist/extraction/languages/arkts.d.ts +15 -0
- package/dist/extraction/languages/arkts.d.ts.map +1 -1
- package/dist/extraction/languages/arkts.js +118 -0
- package/dist/extraction/languages/arkts.js.map +1 -1
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +87 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/engine.d.ts +26 -0
- package/dist/mcp/engine.d.ts.map +1 -1
- package/dist/mcp/engine.js +229 -8
- package/dist/mcp/engine.js.map +1 -1
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +16 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/query-pool.js +4 -4
- package/dist/mcp/server-instructions.d.ts +1 -1
- package/dist/mcp/server-instructions.d.ts.map +1 -1
- package/dist/mcp/server-instructions.js +2 -0
- package/dist/mcp/server-instructions.js.map +1 -1
- package/dist/mcp/tools.d.ts +10 -0
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +138 -3
- package/dist/mcp/tools.js.map +1 -1
- package/dist/project-map/index.d.ts +33 -0
- package/dist/project-map/index.d.ts.map +1 -0
- package/dist/project-map/index.js +260 -0
- package/dist/project-map/index.js.map +1 -0
- package/dist/sync/watcher.d.ts +15 -3
- package/dist/sync/watcher.d.ts.map +1 -1
- package/dist/sync/watcher.js +14 -1
- package/dist/sync/watcher.js.map +1 -1
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/mcp/tools.js
CHANGED
|
@@ -944,6 +944,27 @@ exports.tools = [
|
|
|
944
944
|
},
|
|
945
945
|
annotations: READ_ONLY_ANNOTATIONS,
|
|
946
946
|
},
|
|
947
|
+
{
|
|
948
|
+
name: 'homegraph_project',
|
|
949
|
+
description: 'Shallow project map: modules + files per module (no symbols/call edges). ' +
|
|
950
|
+
'PRIMARY for engineering overview while the full index is still building; also useful after full index. ' +
|
|
951
|
+
'Optional `module` filters by name/path; `includeFiles` defaults true.',
|
|
952
|
+
inputSchema: {
|
|
953
|
+
type: 'object',
|
|
954
|
+
properties: {
|
|
955
|
+
module: {
|
|
956
|
+
type: 'string',
|
|
957
|
+
description: 'Optional module name or root path substring to filter.',
|
|
958
|
+
},
|
|
959
|
+
includeFiles: {
|
|
960
|
+
type: 'boolean',
|
|
961
|
+
description: 'Include per-module file lists (default true). Set false for module list only.',
|
|
962
|
+
},
|
|
963
|
+
projectPath: projectPathProperty,
|
|
964
|
+
},
|
|
965
|
+
},
|
|
966
|
+
annotations: READ_ONLY_ANNOTATIONS,
|
|
967
|
+
},
|
|
947
968
|
{
|
|
948
969
|
name: 'homegraph_arkui_migrate',
|
|
949
970
|
description: 'ArkUI migrate / state-semantics snapshot in ONE call (components, state decorators + args, ' +
|
|
@@ -1459,6 +1480,7 @@ class ToolHandler {
|
|
|
1459
1480
|
'homegraph_search',
|
|
1460
1481
|
'homegraph_node',
|
|
1461
1482
|
'homegraph_diff_impact',
|
|
1483
|
+
'homegraph_project',
|
|
1462
1484
|
]);
|
|
1463
1485
|
if (stats.fileCount < TINY_REPO_FILE_THRESHOLD) {
|
|
1464
1486
|
visible = visible.filter(t => TINY_REPO_CORE_TOOLS.has(t.name));
|
|
@@ -1953,6 +1975,19 @@ class ToolHandler {
|
|
|
1953
1975
|
if (toolName === 'homegraph_status') {
|
|
1954
1976
|
return await this.handleStatus(args);
|
|
1955
1977
|
}
|
|
1978
|
+
// While auto-init is still on the fast/indexing phases (no symbols yet),
|
|
1979
|
+
// deep tools return guidance so the agent uses homegraph_project instead
|
|
1980
|
+
// of abandoning HomeGraph after empty explore results.
|
|
1981
|
+
if (toolName !== 'homegraph_project') {
|
|
1982
|
+
try {
|
|
1983
|
+
const gated = this.maybeDeepToolPhaseGate(this.getHomeGraph(projectPath), toolName);
|
|
1984
|
+
if (gated)
|
|
1985
|
+
return gated;
|
|
1986
|
+
}
|
|
1987
|
+
catch {
|
|
1988
|
+
/* NotIndexed / sources disabled — fall through to normal handlers */
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1956
1991
|
// Every read tool races a deadline ≪ MCP client ~60s hard timeout and
|
|
1957
1992
|
// Named-member / local-compact questions finish in tens of ms on the warm
|
|
1958
1993
|
// main connection. Serving them here — before the query-pool offload —
|
|
@@ -2155,7 +2190,8 @@ class ToolHandler {
|
|
|
2155
2190
|
|| toolName === 'homegraph_node'
|
|
2156
2191
|
|| toolName === 'homegraph_callers'
|
|
2157
2192
|
|| toolName === 'homegraph_callees'
|
|
2158
|
-
|| toolName === 'homegraph_files'
|
|
2193
|
+
|| toolName === 'homegraph_files'
|
|
2194
|
+
|| toolName === 'homegraph_project';
|
|
2159
2195
|
const work = () => {
|
|
2160
2196
|
if (!light && this.queryPool && this.queryPool.healthy) {
|
|
2161
2197
|
return this.queryPool.run(toolName, args, {
|
|
@@ -2240,6 +2276,16 @@ class ToolHandler {
|
|
|
2240
2276
|
* NotIndexed/PathRefusal, which {@link executeReadTool} classifies.
|
|
2241
2277
|
*/
|
|
2242
2278
|
async dispatchTool(toolName, args) {
|
|
2279
|
+
if (toolName !== 'homegraph_project' && toolName !== 'homegraph_status') {
|
|
2280
|
+
try {
|
|
2281
|
+
const gated = this.maybeDeepToolPhaseGate(this.getHomeGraph(args.projectPath), toolName);
|
|
2282
|
+
if (gated)
|
|
2283
|
+
return gated;
|
|
2284
|
+
}
|
|
2285
|
+
catch {
|
|
2286
|
+
/* fall through */
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2243
2289
|
switch (toolName) {
|
|
2244
2290
|
case 'homegraph_search': return await this.handleSearch(args);
|
|
2245
2291
|
case 'homegraph_callers': return await this.handleCallers(args);
|
|
@@ -2252,6 +2298,7 @@ class ToolHandler {
|
|
|
2252
2298
|
case 'homegraph_native': return await this.handleSpecializedNative(args);
|
|
2253
2299
|
case 'homegraph_node': return await this.handleNode(args);
|
|
2254
2300
|
case 'homegraph_files': return await this.handleFiles(args);
|
|
2301
|
+
case 'homegraph_project': return await this.handleProject(args);
|
|
2255
2302
|
case 'homegraph_arkui_migrate': return await this.handleArkuiMigrate(args);
|
|
2256
2303
|
case 'homegraph_spec_match': return await this.handleSpecMatch(args);
|
|
2257
2304
|
case 'homegraph_spec_find': return await this.handleSpecFind(args);
|
|
@@ -2259,6 +2306,36 @@ class ToolHandler {
|
|
|
2259
2306
|
default: return this.errorResult(`Unknown tool: ${toolName}`);
|
|
2260
2307
|
}
|
|
2261
2308
|
}
|
|
2309
|
+
/**
|
|
2310
|
+
* Gate symbol/graph tools while auto-init is still on the fast map or
|
|
2311
|
+
* background full index (and no nodes exist yet). Success-shaped guidance —
|
|
2312
|
+
* never isError — so agents keep using HomeGraph.
|
|
2313
|
+
*/
|
|
2314
|
+
maybeDeepToolPhaseGate(cg, _toolName) {
|
|
2315
|
+
const phase = cg.getBuildPhase();
|
|
2316
|
+
if (phase === 'full')
|
|
2317
|
+
return null;
|
|
2318
|
+
try {
|
|
2319
|
+
if (cg.getStats().nodeCount > 0)
|
|
2320
|
+
return null;
|
|
2321
|
+
}
|
|
2322
|
+
catch {
|
|
2323
|
+
/* ignore */
|
|
2324
|
+
}
|
|
2325
|
+
if (phase === 'fast' || phase === 'indexing') {
|
|
2326
|
+
return this.textResult([
|
|
2327
|
+
`Full symbol index is still building (phase=${phase}).`,
|
|
2328
|
+
'Use `homegraph_project` for the module/file map now, then retry this tool once indexing finishes.',
|
|
2329
|
+
].join('\n'));
|
|
2330
|
+
}
|
|
2331
|
+
if (phase === 'building_fast' || phase === 'none') {
|
|
2332
|
+
return this.textResult([
|
|
2333
|
+
'HomeGraph is still preparing the project map.',
|
|
2334
|
+
'Retry in a few seconds, or call `homegraph_project` once the fast build completes.',
|
|
2335
|
+
].join('\n'));
|
|
2336
|
+
}
|
|
2337
|
+
return null;
|
|
2338
|
+
}
|
|
2262
2339
|
async handleSearch(args) {
|
|
2263
2340
|
const query = this.validateString(args.query, 'query');
|
|
2264
2341
|
if (typeof query !== 'string')
|
|
@@ -5318,7 +5395,7 @@ class ToolHandler {
|
|
|
5318
5395
|
tryFastInventoryExplore(cg, query, projectRoot) {
|
|
5319
5396
|
if (!(0, query_utils_1.shouldTryFastInventoryExplore)(query))
|
|
5320
5397
|
return null;
|
|
5321
|
-
// Multi-Type dependency asks: inventory-only early exit (avoids fat compact /
|
|
5398
|
+
// Multi-Type dependency asks: inventory-only early exit (avoids fat compact / busy timeout).
|
|
5322
5399
|
if ((0, query_utils_1.queryAsMultiTypeDependencySurvey)(query)) {
|
|
5323
5400
|
const multi = this.buildMultiTypeDependencySection(cg, query);
|
|
5324
5401
|
if (multi.section) {
|
|
@@ -11317,7 +11394,7 @@ class ToolHandler {
|
|
|
11317
11394
|
if (mismatch) {
|
|
11318
11395
|
lines.push(`> ⚠ ${(0, worktree_1.worktreeMismatchWarning)(mismatch).replace(/\n/g, '\n> ')}`, '');
|
|
11319
11396
|
}
|
|
11320
|
-
lines.push(`**Files indexed:** ${stats.fileCount}`, `**Total nodes:** ${stats.nodeCount}`, `**Total edges:** ${stats.edgeCount}`, `**Database size:** ${(stats.dbSizeBytes / 1024 / 1024).toFixed(2)} MB`, ...(stats.walSizeBytes > 0
|
|
11397
|
+
lines.push(`**Build phase:** ${cg.getBuildPhase()}`, `**Files indexed:** ${stats.fileCount}`, `**Total nodes:** ${stats.nodeCount}`, `**Total edges:** ${stats.edgeCount}`, `**Database size:** ${(stats.dbSizeBytes / 1024 / 1024).toFixed(2)} MB`, ...(stats.walSizeBytes > 0
|
|
11321
11398
|
? [`**WAL size:** ${(stats.walSizeBytes / 1024 / 1024).toFixed(2)} MB`]
|
|
11322
11399
|
: []), `**Graph sources:** ${cg.getGraphSources()}`);
|
|
11323
11400
|
// Surface the active SQLite backend: node:sqlite → better-sqlite3 → wasm.
|
|
@@ -11382,6 +11459,64 @@ class ToolHandler {
|
|
|
11382
11459
|
}
|
|
11383
11460
|
return this.textResult(lines.join('\n'));
|
|
11384
11461
|
}
|
|
11462
|
+
/**
|
|
11463
|
+
* Handle homegraph_project — shallow module → file map (fast build).
|
|
11464
|
+
*/
|
|
11465
|
+
async handleProject(args) {
|
|
11466
|
+
const cg = this.getHomeGraph(args.projectPath);
|
|
11467
|
+
const phase = cg.getBuildPhase();
|
|
11468
|
+
if (phase === 'building_fast') {
|
|
11469
|
+
return this.textResult('Fast project map is still building — retry in a few seconds.');
|
|
11470
|
+
}
|
|
11471
|
+
let map = cg.getProjectMap({
|
|
11472
|
+
module: typeof args.module === 'string' ? args.module : undefined,
|
|
11473
|
+
includeFiles: args.includeFiles !== false,
|
|
11474
|
+
});
|
|
11475
|
+
// On-demand build for indexes that never ran the fast path (legacy / CLI init).
|
|
11476
|
+
if (map.modules.length === 0) {
|
|
11477
|
+
try {
|
|
11478
|
+
cg.buildProjectMap();
|
|
11479
|
+
if (phase === 'none') {
|
|
11480
|
+
cg.setBuildPhase(cg.getStats().nodeCount > 0 ? 'full' : 'fast');
|
|
11481
|
+
}
|
|
11482
|
+
map = cg.getProjectMap({
|
|
11483
|
+
module: typeof args.module === 'string' ? args.module : undefined,
|
|
11484
|
+
includeFiles: args.includeFiles !== false,
|
|
11485
|
+
});
|
|
11486
|
+
}
|
|
11487
|
+
catch (err) {
|
|
11488
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
11489
|
+
return this.textResult(`Failed to build project map: ${message}`);
|
|
11490
|
+
}
|
|
11491
|
+
}
|
|
11492
|
+
if (map.modules.length === 0) {
|
|
11493
|
+
return this.textResult('No modules found in the project map.');
|
|
11494
|
+
}
|
|
11495
|
+
const FILE_CAP = 80;
|
|
11496
|
+
const lines = [
|
|
11497
|
+
`**Project map** (phase=${map.phase})`,
|
|
11498
|
+
`modules: ${map.modules.length} · files: ${map.fileCount}`,
|
|
11499
|
+
'',
|
|
11500
|
+
];
|
|
11501
|
+
if (map.phase === 'fast' || map.phase === 'indexing') {
|
|
11502
|
+
lines.push('_Full symbol index still building — this map has modules/files only (no call graph)._', '');
|
|
11503
|
+
}
|
|
11504
|
+
for (const m of map.modules) {
|
|
11505
|
+
const rootLabel = m.rootPath || '.';
|
|
11506
|
+
lines.push(`### ${m.name} (\`${rootLabel}\`) · ${m.kind} · ${m.fileCount} files`);
|
|
11507
|
+
if (m.files && m.files.length > 0) {
|
|
11508
|
+
const shown = m.files.slice(0, FILE_CAP);
|
|
11509
|
+
for (const f of shown) {
|
|
11510
|
+
lines.push(`- ${f.path}`);
|
|
11511
|
+
}
|
|
11512
|
+
if (m.files.length > FILE_CAP) {
|
|
11513
|
+
lines.push(`- … +${m.files.length - FILE_CAP} more`);
|
|
11514
|
+
}
|
|
11515
|
+
}
|
|
11516
|
+
lines.push('');
|
|
11517
|
+
}
|
|
11518
|
+
return this.textResult(lines.join('\n').trimEnd());
|
|
11519
|
+
}
|
|
11385
11520
|
/**
|
|
11386
11521
|
* Handle homegraph_arkui_migrate — one-shot ArkUI migrate / state snapshot (spec 0007).
|
|
11387
11522
|
*/
|