homegraph 1.5.10 → 1.6.0
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 +18 -0
- package/dist/extraction/grammars.d.ts +19 -0
- package/dist/extraction/grammars.js +44 -1
- package/dist/mcp/arkts-evidence-packs.js +1 -0
- package/dist/mcp/daemon.d.ts +21 -3
- package/dist/mcp/daemon.js +60 -6
- package/dist/mcp/engine.d.ts +26 -0
- package/dist/mcp/engine.js +143 -7
- package/dist/mcp/index-availability.d.ts +22 -0
- package/dist/mcp/index-availability.js +40 -1
- package/dist/mcp/index.js +9 -0
- package/dist/mcp/indexable-root.d.ts +22 -0
- package/dist/mcp/indexable-root.js +140 -0
- package/dist/mcp/liveness-watchdog.d.ts +6 -1
- package/dist/mcp/liveness-watchdog.js +17 -5
- package/dist/mcp/locate-contract.d.ts +50 -0
- package/dist/mcp/locate-contract.js +146 -0
- package/dist/mcp/server-instructions.d.ts +2 -2
- package/dist/mcp/server-instructions.js +7 -4
- package/dist/mcp/session.js +15 -0
- package/dist/mcp/tools.d.ts +49 -2
- package/dist/mcp/tools.js +714 -64
- package/dist/project-map/index.d.ts +45 -0
- package/dist/project-map/index.js +371 -3
- package/dist/resolution/callback-synthesizer.js +251 -0
- package/dist/resolution/frameworks/arkts-entry.d.ts +35 -6
- package/dist/resolution/frameworks/arkts-entry.js +513 -30
- package/dist/resolution/index.js +25 -21
- package/dist/runtime-log.d.ts +52 -0
- package/dist/runtime-log.js +199 -0
- package/dist/search/query-plan-provider.js +3 -2
- package/dist/search/query-plan.js +21 -19
- package/dist/search/query-utils.d.ts +13 -0
- package/dist/search/query-utils.js +64 -0
- package/package.json +1 -1
|
@@ -6,12 +6,16 @@
|
|
|
6
6
|
* pending dirty files) to the five states hosts and agents should see.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.PRODUCT_STATUS_GLOSSARY = void 0;
|
|
9
|
+
exports.BOUND_PROJECT_PATH_PIN_MARKER = exports.PROJECT_ROOT_HINT_MARKER = exports.PRODUCT_STATUS_GLOSSARY = void 0;
|
|
10
10
|
exports.productIndexGuidance = productIndexGuidance;
|
|
11
11
|
exports.formatProductStatusLine = formatProductStatusLine;
|
|
12
12
|
exports.resolveProductIndexState = resolveProductIndexState;
|
|
13
13
|
exports.isSqliteBusyMessage = isSqliteBusyMessage;
|
|
14
14
|
exports.textAlreadyHasProductStatus = textAlreadyHasProductStatus;
|
|
15
|
+
exports.formatProjectRootPathHint = formatProjectRootPathHint;
|
|
16
|
+
exports.textAlreadyHasProjectRootHint = textAlreadyHasProjectRootHint;
|
|
17
|
+
exports.formatBoundProjectPathPinNotice = formatBoundProjectPathPinNotice;
|
|
18
|
+
exports.textAlreadyHasBoundProjectPathPinNotice = textAlreadyHasBoundProjectPathPinNotice;
|
|
15
19
|
/** One-line glossary for MCP initialize / tool surface (Spec 0035). */
|
|
16
20
|
exports.PRODUCT_STATUS_GLOSSARY = 'status: empty=not ready · fast=map only (homegraph_project) · full=fresh · dirty=usable but listed paths outdated · syncing=write lock, retry';
|
|
17
21
|
const MAX_DIRTY_PATHS = 5;
|
|
@@ -102,4 +106,39 @@ function isSqliteBusyMessage(message) {
|
|
|
102
106
|
function textAlreadyHasProductStatus(text) {
|
|
103
107
|
return /^\s*HomeGraph status=/m.test(text);
|
|
104
108
|
}
|
|
109
|
+
/** Marker line for Spec 0038 project-root path hint (idempotent prepend). */
|
|
110
|
+
exports.PROJECT_ROOT_HINT_MARKER = 'HomeGraph project root:';
|
|
111
|
+
/**
|
|
112
|
+
* Short preamble: absolute project root + how to join repo-relative paths (Spec 0038).
|
|
113
|
+
* `absRoot` should already be resolved (platform-native absolute path).
|
|
114
|
+
*/
|
|
115
|
+
function formatProjectRootPathHint(absRoot) {
|
|
116
|
+
const root = absRoot.trim();
|
|
117
|
+
return (`${exports.PROJECT_ROOT_HINT_MARKER} \`${root}\`\n` +
|
|
118
|
+
'Paths below are repo-relative to that root. Pass them to Read/Grep as-is, or join as `<root>/<relative>` (use `/`). Do not invent experiment/result directory prefixes.');
|
|
119
|
+
}
|
|
120
|
+
/** True when text already carries a Spec 0038 project-root hint. */
|
|
121
|
+
function textAlreadyHasProjectRootHint(text) {
|
|
122
|
+
return text.includes(exports.PROJECT_ROOT_HINT_MARKER);
|
|
123
|
+
}
|
|
124
|
+
/** Marker for Spec 0043 bound-root projectPath soft-pin notice (idempotent prepend). */
|
|
125
|
+
exports.BOUND_PROJECT_PATH_PIN_MARKER = 'This MCP session is bound to';
|
|
126
|
+
/**
|
|
127
|
+
* English preamble when a tool `projectPath` resolves to a different index root
|
|
128
|
+
* than the MCP session's default bound root (Spec 0043). Success-shaped — not isError.
|
|
129
|
+
*/
|
|
130
|
+
function formatBoundProjectPathPinNotice(opts) {
|
|
131
|
+
const bound = opts.boundRoot.trim();
|
|
132
|
+
const requested = opts.requestedPath.trim();
|
|
133
|
+
const resolved = opts.resolvedRoot === null
|
|
134
|
+
? 'no .homegraph/ found walking up from it'
|
|
135
|
+
: `resolves to \`${opts.resolvedRoot.trim()}\``;
|
|
136
|
+
return (`${exports.BOUND_PROJECT_PATH_PIN_MARKER} \`${bound}\`.\n` +
|
|
137
|
+
`Ignoring projectPath=\`${requested}\` (${resolved}).\n` +
|
|
138
|
+
'Results below are from the bound project root only. Omit projectPath, or pass a path under that root.');
|
|
139
|
+
}
|
|
140
|
+
/** True when text already carries a Spec 0043 pin notice. */
|
|
141
|
+
function textAlreadyHasBoundProjectPathPinNotice(text) {
|
|
142
|
+
return text.includes(exports.BOUND_PROJECT_PATH_PIN_MARKER);
|
|
143
|
+
}
|
|
105
144
|
//# sourceMappingURL=index-availability.js.map
|
package/dist/mcp/index.js
CHANGED
|
@@ -73,6 +73,7 @@ const fs = __importStar(require("fs"));
|
|
|
73
73
|
const path = __importStar(require("path"));
|
|
74
74
|
const child_process_1 = require("child_process");
|
|
75
75
|
const directory_1 = require("../directory");
|
|
76
|
+
const runtime_log_1 = require("../runtime-log");
|
|
76
77
|
const transport_1 = require("./transport");
|
|
77
78
|
const engine_1 = require("./engine");
|
|
78
79
|
const session_1 = require("./session");
|
|
@@ -298,6 +299,7 @@ class MCPServer {
|
|
|
298
299
|
// Runs until the host disconnects; the proxy installs its own watchdog and
|
|
299
300
|
// falls back to an in-process engine if the daemon never comes up.
|
|
300
301
|
this.mode = 'proxy';
|
|
302
|
+
(0, runtime_log_1.logLifecycle)('mcp.start', { projectRoot: root, mode: 'proxy' });
|
|
301
303
|
await this.runProxyWithLocalHandshake(root);
|
|
302
304
|
return;
|
|
303
305
|
}
|
|
@@ -306,6 +308,7 @@ class MCPServer {
|
|
|
306
308
|
// is still safe to recover from with a direct-mode session.
|
|
307
309
|
const msg = err instanceof Error ? err.message : String(err);
|
|
308
310
|
process.stderr.write(`[HomeGraph MCP] Proxy path failed (${msg}); falling back to direct mode.\n`);
|
|
311
|
+
(0, runtime_log_1.logLifecycleError)('mcp.proxy.fail', { projectRoot: root, msg });
|
|
309
312
|
return this.startDirect('proxy path threw');
|
|
310
313
|
}
|
|
311
314
|
}
|
|
@@ -346,6 +349,11 @@ class MCPServer {
|
|
|
346
349
|
if (reason && process.env.HOMEGRAPH_MCP_DEBUG) {
|
|
347
350
|
process.stderr.write(`[HomeGraph MCP] Direct mode: ${reason}.\n`);
|
|
348
351
|
}
|
|
352
|
+
(0, runtime_log_1.logLifecycle)('mcp.start', {
|
|
353
|
+
projectRoot: this.projectPath ?? undefined,
|
|
354
|
+
mode: 'direct',
|
|
355
|
+
reason,
|
|
356
|
+
});
|
|
349
357
|
// Direct mode = one client. Do NOT start a query-pool worker: each worker
|
|
350
358
|
// is a second V8 isolate + a second open of the project DB, and on large
|
|
351
359
|
// indexes (hundreds of MB) that alone pushed process-tree RSS to ~5GB.
|
|
@@ -392,6 +400,7 @@ class MCPServer {
|
|
|
392
400
|
*/
|
|
393
401
|
async startDaemonProcess() {
|
|
394
402
|
const root = resolveDaemonRoot(this.projectPath) ?? this.projectPath ?? process.cwd();
|
|
403
|
+
(0, runtime_log_1.logLifecycle)('mcp.start', { projectRoot: root, mode: 'daemon' });
|
|
395
404
|
for (let attempt = 0; attempt < TAKEOVER_MAX_RETRIES; attempt++) {
|
|
396
405
|
const lock = (0, daemon_1.tryAcquireDaemonLock)(root);
|
|
397
406
|
if (lock.kind === 'acquired') {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cheap “is this root worth creating a HomeGraph index?” probe (Spec 0049).
|
|
3
|
+
*
|
|
4
|
+
* Used by MCP auto-init to avoid writing `.homegraph/` into empty workspaces
|
|
5
|
+
* (e.g. DevEco Code task dirs before `devecocli create`).
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* True when `root` already looks like a project HomeGraph should index.
|
|
9
|
+
* Short-circuits on root `build-profile.json5`, else bounded BFS for any
|
|
10
|
+
* {@link isSourceFile} hit. Ignores `.homegraph*` and common build/vendor dirs.
|
|
11
|
+
*/
|
|
12
|
+
export declare function isIndexableRoot(rootDir: string, opts?: {
|
|
13
|
+
maxDirs?: number;
|
|
14
|
+
maxDepth?: number;
|
|
15
|
+
}): boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Parse `HOMEGRAPH_DEFER_PROBE_MS`. Default **60000**.
|
|
18
|
+
* `0` disables the timer (tool-call kick only). Invalid → default.
|
|
19
|
+
* Clamp: 0, or 1000ms … 10min.
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseDeferProbeMs(raw: string | undefined): number;
|
|
22
|
+
//# sourceMappingURL=indexable-root.d.ts.map
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Cheap “is this root worth creating a HomeGraph index?” probe (Spec 0049).
|
|
4
|
+
*
|
|
5
|
+
* Used by MCP auto-init to avoid writing `.homegraph/` into empty workspaces
|
|
6
|
+
* (e.g. DevEco Code task dirs before `devecocli create`).
|
|
7
|
+
*/
|
|
8
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
9
|
+
if (k2 === undefined) k2 = k;
|
|
10
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
11
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
12
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13
|
+
}
|
|
14
|
+
Object.defineProperty(o, k2, desc);
|
|
15
|
+
}) : (function(o, m, k, k2) {
|
|
16
|
+
if (k2 === undefined) k2 = k;
|
|
17
|
+
o[k2] = m[k];
|
|
18
|
+
}));
|
|
19
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
20
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
21
|
+
}) : function(o, v) {
|
|
22
|
+
o["default"] = v;
|
|
23
|
+
});
|
|
24
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
25
|
+
var ownKeys = function(o) {
|
|
26
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
27
|
+
var ar = [];
|
|
28
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
29
|
+
return ar;
|
|
30
|
+
};
|
|
31
|
+
return ownKeys(o);
|
|
32
|
+
};
|
|
33
|
+
return function (mod) {
|
|
34
|
+
if (mod && mod.__esModule) return mod;
|
|
35
|
+
var result = {};
|
|
36
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
37
|
+
__setModuleDefault(result, mod);
|
|
38
|
+
return result;
|
|
39
|
+
};
|
|
40
|
+
})();
|
|
41
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
|
+
exports.isIndexableRoot = isIndexableRoot;
|
|
43
|
+
exports.parseDeferProbeMs = parseDeferProbeMs;
|
|
44
|
+
const fs = __importStar(require("fs"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const grammars_1 = require("../extraction/grammars");
|
|
47
|
+
const directory_1 = require("../directory");
|
|
48
|
+
const HARMONY_PROFILE = 'build-profile.json5';
|
|
49
|
+
/** Dirs skipped during the bounded source walk (names only). */
|
|
50
|
+
const SKIP_DIR_NAMES = new Set([
|
|
51
|
+
'node_modules',
|
|
52
|
+
'oh_modules',
|
|
53
|
+
'.git',
|
|
54
|
+
'.hvigor',
|
|
55
|
+
'.preview',
|
|
56
|
+
'.cxx',
|
|
57
|
+
'dist',
|
|
58
|
+
'build',
|
|
59
|
+
'out',
|
|
60
|
+
'coverage',
|
|
61
|
+
'vendor',
|
|
62
|
+
'Pods',
|
|
63
|
+
'DerivedData',
|
|
64
|
+
'__pycache__',
|
|
65
|
+
'.venv',
|
|
66
|
+
'venv',
|
|
67
|
+
'target',
|
|
68
|
+
'.gradle',
|
|
69
|
+
'obj',
|
|
70
|
+
]);
|
|
71
|
+
const DEFAULT_MAX_DIRS = 4000;
|
|
72
|
+
const DEFAULT_MAX_DEPTH = 8;
|
|
73
|
+
/**
|
|
74
|
+
* True when `root` already looks like a project HomeGraph should index.
|
|
75
|
+
* Short-circuits on root `build-profile.json5`, else bounded BFS for any
|
|
76
|
+
* {@link isSourceFile} hit. Ignores `.homegraph*` and common build/vendor dirs.
|
|
77
|
+
*/
|
|
78
|
+
function isIndexableRoot(rootDir, opts) {
|
|
79
|
+
const root = path.resolve(rootDir);
|
|
80
|
+
let st;
|
|
81
|
+
try {
|
|
82
|
+
st = fs.statSync(root);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
if (!st.isDirectory())
|
|
88
|
+
return false;
|
|
89
|
+
if (fs.existsSync(path.join(root, HARMONY_PROFILE)))
|
|
90
|
+
return true;
|
|
91
|
+
const maxDirs = opts?.maxDirs ?? DEFAULT_MAX_DIRS;
|
|
92
|
+
const maxDepth = opts?.maxDepth ?? DEFAULT_MAX_DEPTH;
|
|
93
|
+
const queue = [{ dir: root, depth: 0 }];
|
|
94
|
+
let dirsSeen = 0;
|
|
95
|
+
while (queue.length > 0 && dirsSeen < maxDirs) {
|
|
96
|
+
const { dir, depth } = queue.shift();
|
|
97
|
+
dirsSeen += 1;
|
|
98
|
+
let entries;
|
|
99
|
+
try {
|
|
100
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
for (const ent of entries) {
|
|
106
|
+
const name = ent.name;
|
|
107
|
+
if (name === '.' || name === '..')
|
|
108
|
+
continue;
|
|
109
|
+
if ((0, directory_1.isHomeGraphDataDir)(name) || SKIP_DIR_NAMES.has(name))
|
|
110
|
+
continue;
|
|
111
|
+
const full = path.join(dir, name);
|
|
112
|
+
if (ent.isDirectory()) {
|
|
113
|
+
if (depth + 1 <= maxDepth)
|
|
114
|
+
queue.push({ dir: full, depth: depth + 1 });
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
if (ent.isFile() && (0, grammars_1.isSourceFile)(full))
|
|
118
|
+
return true;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Parse `HOMEGRAPH_DEFER_PROBE_MS`. Default **60000**.
|
|
125
|
+
* `0` disables the timer (tool-call kick only). Invalid → default.
|
|
126
|
+
* Clamp: 0, or 1000ms … 10min.
|
|
127
|
+
*/
|
|
128
|
+
function parseDeferProbeMs(raw) {
|
|
129
|
+
if (raw === undefined || !raw.trim())
|
|
130
|
+
return 60_000;
|
|
131
|
+
const n = Number(raw);
|
|
132
|
+
if (!Number.isFinite(n) || !Number.isInteger(n))
|
|
133
|
+
return 60_000;
|
|
134
|
+
if (n === 0)
|
|
135
|
+
return 0;
|
|
136
|
+
if (n < 1000 || n > 10 * 60 * 1000)
|
|
137
|
+
return 60_000;
|
|
138
|
+
return n;
|
|
139
|
+
}
|
|
140
|
+
//# sourceMappingURL=indexable-root.js.map
|
|
@@ -7,6 +7,11 @@ export declare const DEFAULT_WATCHDOG_TIMEOUT_MS = 60000;
|
|
|
7
7
|
* beyond any legitimate statement). 10× the 60s default ⇒ 10 minutes.
|
|
8
8
|
*/
|
|
9
9
|
export declare const PROGRESS_CAP_MULTIPLIER = 10;
|
|
10
|
+
/**
|
|
11
|
+
* Spec 0047: liveness watchdog is opt-in.
|
|
12
|
+
* Armed only when `HOMEGRAPH_WATCHDOG` is truthy and `HOMEGRAPH_NO_WATCHDOG` is not.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isLivenessWatchdogEnabled(env?: NodeJS.ProcessEnv): boolean;
|
|
10
15
|
/** Parse the timeout env, falling back to the default for missing/invalid values. */
|
|
11
16
|
export declare function parseWatchdogTimeoutMs(raw: string | undefined, fallback?: number): number;
|
|
12
17
|
/** Derive a heartbeat cadence that emits several beats inside the timeout window. */
|
|
@@ -42,7 +47,7 @@ export declare function installMainThreadWatchdog(options?: WatchdogOptions): Wa
|
|
|
42
47
|
* resume function. Nested calls refcount; a `stop()` while suspended cancels
|
|
43
48
|
* the pending re-arm.
|
|
44
49
|
*
|
|
45
|
-
* No-op when nothing is armed (tests, `HOMEGRAPH_NO_WATCHDOG`, library embeds).
|
|
50
|
+
* No-op when nothing is armed (tests, default-off Spec 0047, `HOMEGRAPH_NO_WATCHDOG`, library embeds).
|
|
46
51
|
*/
|
|
47
52
|
export declare function suspendLivenessWatchdog(): () => void;
|
|
48
53
|
/**
|
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.PROGRESS_CAP_MULTIPLIER = exports.DEFAULT_WATCHDOG_TIMEOUT_MS = void 0;
|
|
37
|
+
exports.isLivenessWatchdogEnabled = isLivenessWatchdogEnabled;
|
|
37
38
|
exports.parseWatchdogTimeoutMs = parseWatchdogTimeoutMs;
|
|
38
39
|
exports.deriveCheckIntervalMs = deriveCheckIntervalMs;
|
|
39
40
|
exports.installMainThreadWatchdog = installMainThreadWatchdog;
|
|
@@ -73,8 +74,9 @@ exports.runWithoutLivenessWatchdog = runWithoutLivenessWatchdog;
|
|
|
73
74
|
* (off-thread) and the daemon's indexing shells out to a child process, so the
|
|
74
75
|
* daemon's main thread only ever does fast, bounded work. The default timeout
|
|
75
76
|
* is ~300× the 5h #850 wedge shorter, yet far longer than any legitimate
|
|
76
|
-
* main-thread block.
|
|
77
|
-
* `
|
|
77
|
+
* main-thread block. Spec 0047: **opt in** with `HOMEGRAPH_WATCHDOG=1` (default
|
|
78
|
+
* off for product / DevEco concurrent init); `HOMEGRAPH_NO_WATCHDOG=1` still
|
|
79
|
+
* forces off. Tune with `HOMEGRAPH_WATCHDOG_TIMEOUT_MS`.
|
|
78
80
|
*
|
|
79
81
|
* **Disk-progress deferral (`progressPaths`).** The CLI `index`/`init` path is
|
|
80
82
|
* different: it runs the SQLite store on this thread, and one long synchronous
|
|
@@ -118,6 +120,15 @@ function isEnvTruthy(raw) {
|
|
|
118
120
|
return false;
|
|
119
121
|
return ['1', 'true', 'yes', 'on'].includes(raw.trim().toLowerCase());
|
|
120
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Spec 0047: liveness watchdog is opt-in.
|
|
125
|
+
* Armed only when `HOMEGRAPH_WATCHDOG` is truthy and `HOMEGRAPH_NO_WATCHDOG` is not.
|
|
126
|
+
*/
|
|
127
|
+
function isLivenessWatchdogEnabled(env = process.env) {
|
|
128
|
+
if (isEnvTruthy(env.HOMEGRAPH_NO_WATCHDOG))
|
|
129
|
+
return false;
|
|
130
|
+
return isEnvTruthy(env.HOMEGRAPH_WATCHDOG);
|
|
131
|
+
}
|
|
121
132
|
/** Parse the timeout env, falling back to the default for missing/invalid values. */
|
|
122
133
|
function parseWatchdogTimeoutMs(raw, fallback = exports.DEFAULT_WATCHDOG_TIMEOUT_MS) {
|
|
123
134
|
if (raw === undefined)
|
|
@@ -153,7 +164,7 @@ const capMs = Number(process.argv[3]);
|
|
|
153
164
|
const progressPaths = process.argv.slice(4);
|
|
154
165
|
const secs = Math.round(timeoutMs / 1000);
|
|
155
166
|
function kill(extra) {
|
|
156
|
-
try { fs.writeSync(2, Buffer.from('[HomeGraph] Main thread unresponsive for ~' + secs + 's' + (extra || '') + ' — killing the wedged process so a fresh one can start (#850). Disable with HOMEGRAPH_NO_WATCHDOG=1.\\n')); } catch (e) {}
|
|
167
|
+
try { fs.writeSync(2, Buffer.from('[HomeGraph] Main thread unresponsive for ~' + secs + 's' + (extra || '') + ' — killing the wedged process so a fresh one can start (#850). Disable with HOMEGRAPH_NO_WATCHDOG=1 (default off; enable with HOMEGRAPH_WATCHDOG=1).\\n')); } catch (e) {}
|
|
157
168
|
try { process.kill(parentPid, 'SIGKILL'); } catch (e) {}
|
|
158
169
|
process.exit(0);
|
|
159
170
|
}
|
|
@@ -318,7 +329,8 @@ function rearmFromOptions(options) {
|
|
|
318
329
|
* current generation.
|
|
319
330
|
*/
|
|
320
331
|
function installMainThreadWatchdog(options = {}) {
|
|
321
|
-
|
|
332
|
+
// Spec 0047: default off — set HOMEGRAPH_WATCHDOG=1 to arm.
|
|
333
|
+
if (!isLivenessWatchdogEnabled())
|
|
322
334
|
return null;
|
|
323
335
|
// Replace any prior generation (including a mid-suspend pending re-arm).
|
|
324
336
|
suspendDepth = 0;
|
|
@@ -336,7 +348,7 @@ function installMainThreadWatchdog(options = {}) {
|
|
|
336
348
|
* resume function. Nested calls refcount; a `stop()` while suspended cancels
|
|
337
349
|
* the pending re-arm.
|
|
338
350
|
*
|
|
339
|
-
* No-op when nothing is armed (tests, `HOMEGRAPH_NO_WATCHDOG`, library embeds).
|
|
351
|
+
* No-op when nothing is armed (tests, default-off Spec 0047, `HOMEGRAPH_NO_WATCHDOG`, library embeds).
|
|
340
352
|
*/
|
|
341
353
|
function suspendLivenessWatchdog() {
|
|
342
354
|
suspendDepth++;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec 0044 — explore locate contract helpers (Located / Miss / Partial,
|
|
3
|
+
* literal-first demotion of log/toast spines, filename≠declaration hint,
|
|
4
|
+
* depth-fuse exemption for already-located symbols).
|
|
5
|
+
*/
|
|
6
|
+
import type { ExploreEmission, ExploreCallRecord, ExploreProjectState } from './explore-session-state';
|
|
7
|
+
export type LocateOutcome = 'located' | 'partial' | 'miss';
|
|
8
|
+
/** Marker strings (idempotent checks). */
|
|
9
|
+
export declare const LOCATED_MARKER = "**Located**";
|
|
10
|
+
export declare const MISS_MARKER = "**Miss**";
|
|
11
|
+
export declare const FILENAME_DECL_MISMATCH_MARKER = "Filename does not match primary declaration";
|
|
12
|
+
/** Query explicitly names a log/toast API — then those nodes may stay on the spine. */
|
|
13
|
+
export declare function queryNamesLogOrToast(query: string): boolean;
|
|
14
|
+
export declare function isLogOrToastSpineName(name: string): boolean;
|
|
15
|
+
/** Demote log/toast when the user did not ask for them. */
|
|
16
|
+
export declare function shouldDemoteLogToastSpine(name: string, query: string): boolean;
|
|
17
|
+
/**
|
|
18
|
+
* Classify locate outcome for explore (Spec 0044 §8–9).
|
|
19
|
+
* Exact anchor / literal witness beats fuzzy-only FTS.
|
|
20
|
+
*/
|
|
21
|
+
export declare function classifyLocateOutcome(opts: {
|
|
22
|
+
hasExactAnchorHit: boolean;
|
|
23
|
+
hasLiteralWitness: boolean;
|
|
24
|
+
hasFullDeclarations: boolean;
|
|
25
|
+
missingStaticRelation: boolean;
|
|
26
|
+
budgetPartial: boolean;
|
|
27
|
+
}): LocateOutcome;
|
|
28
|
+
export declare function formatLocatedBanner(): string;
|
|
29
|
+
export declare function formatPartialBanner(): string;
|
|
30
|
+
/** Miss: paths only, no source bodies. */
|
|
31
|
+
export declare function formatMissBanner(candidatePaths: readonly string[]): string;
|
|
32
|
+
/**
|
|
33
|
+
* When file basename (sans extension) ≠ primary declaration name.
|
|
34
|
+
* Returns null when names match or inputs are empty.
|
|
35
|
+
*/
|
|
36
|
+
export declare function formatFilenameDeclarationMismatch(filePath: string, primaryDeclName: string | null | undefined): string | null;
|
|
37
|
+
/** Names recorded on the latest counted explore emission. */
|
|
38
|
+
export declare function locatedNamesFromEmission(emission: ExploreEmission | ExploreCallRecord | null | undefined): string[];
|
|
39
|
+
export declare function symbolMatchesLocatedNames(symbol: string | undefined, locatedNames: readonly string[]): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Spec 0044 §10.2 — do not depth-fuse-refuse a symbol already present on the
|
|
42
|
+
* latest explore locate list (even after Partial spent its one generic drill).
|
|
43
|
+
*/
|
|
44
|
+
export declare function shouldExemptDepthFuseForLocatedSymbol(prior: ExploreProjectState | null | undefined, symbol: string | undefined): boolean;
|
|
45
|
+
/** Strip CJK→ASCII synonym seeds when Miss-bound (Spec 0044 §9). */
|
|
46
|
+
export declare function shouldSuppressSynonymExpansion(opts: {
|
|
47
|
+
hasExactAnchorHit: boolean;
|
|
48
|
+
hasLiteralWitness: boolean;
|
|
49
|
+
}): boolean;
|
|
50
|
+
//# sourceMappingURL=locate-contract.d.ts.map
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Spec 0044 — explore locate contract helpers (Located / Miss / Partial,
|
|
4
|
+
* literal-first demotion of log/toast spines, filename≠declaration hint,
|
|
5
|
+
* depth-fuse exemption for already-located symbols).
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.FILENAME_DECL_MISMATCH_MARKER = exports.MISS_MARKER = exports.LOCATED_MARKER = void 0;
|
|
9
|
+
exports.queryNamesLogOrToast = queryNamesLogOrToast;
|
|
10
|
+
exports.isLogOrToastSpineName = isLogOrToastSpineName;
|
|
11
|
+
exports.shouldDemoteLogToastSpine = shouldDemoteLogToastSpine;
|
|
12
|
+
exports.classifyLocateOutcome = classifyLocateOutcome;
|
|
13
|
+
exports.formatLocatedBanner = formatLocatedBanner;
|
|
14
|
+
exports.formatPartialBanner = formatPartialBanner;
|
|
15
|
+
exports.formatMissBanner = formatMissBanner;
|
|
16
|
+
exports.formatFilenameDeclarationMismatch = formatFilenameDeclarationMismatch;
|
|
17
|
+
exports.locatedNamesFromEmission = locatedNamesFromEmission;
|
|
18
|
+
exports.symbolMatchesLocatedNames = symbolMatchesLocatedNames;
|
|
19
|
+
exports.shouldExemptDepthFuseForLocatedSymbol = shouldExemptDepthFuseForLocatedSymbol;
|
|
20
|
+
exports.shouldSuppressSynonymExpansion = shouldSuppressSynonymExpansion;
|
|
21
|
+
const explore_session_state_1 = require("./explore-session-state");
|
|
22
|
+
const explore_repeat_guard_1 = require("./explore-repeat-guard");
|
|
23
|
+
/** Marker strings (idempotent checks). */
|
|
24
|
+
exports.LOCATED_MARKER = '**Located**';
|
|
25
|
+
exports.MISS_MARKER = '**Miss**';
|
|
26
|
+
exports.FILENAME_DECL_MISMATCH_MARKER = 'Filename does not match primary declaration';
|
|
27
|
+
const LOG_TOAST_NAME_RE = /^(?:Logger|Hilog|HiLog|Toast|promptAction|log(?:Info|Error|Warn|Debug|Fatal)?|hilog)$/i;
|
|
28
|
+
/** Query explicitly names a log/toast API — then those nodes may stay on the spine. */
|
|
29
|
+
function queryNamesLogOrToast(query) {
|
|
30
|
+
return /\b(?:Logger|hilog|HiLog|Toast|promptAction|logInfo|logError|logWarn|logDebug)\b/i.test(query)
|
|
31
|
+
|| /日志|弹窗|吐司/.test(query);
|
|
32
|
+
}
|
|
33
|
+
function isLogOrToastSpineName(name) {
|
|
34
|
+
return LOG_TOAST_NAME_RE.test(name.trim());
|
|
35
|
+
}
|
|
36
|
+
/** Demote log/toast when the user did not ask for them. */
|
|
37
|
+
function shouldDemoteLogToastSpine(name, query) {
|
|
38
|
+
return isLogOrToastSpineName(name) && !queryNamesLogOrToast(query);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Classify locate outcome for explore (Spec 0044 §8–9).
|
|
42
|
+
* Exact anchor / literal witness beats fuzzy-only FTS.
|
|
43
|
+
*/
|
|
44
|
+
function classifyLocateOutcome(opts) {
|
|
45
|
+
if (!opts.hasExactAnchorHit && !opts.hasLiteralWitness)
|
|
46
|
+
return 'miss';
|
|
47
|
+
if (opts.budgetPartial || opts.missingStaticRelation)
|
|
48
|
+
return 'partial';
|
|
49
|
+
if (opts.hasFullDeclarations && (opts.hasExactAnchorHit || opts.hasLiteralWitness)) {
|
|
50
|
+
return 'located';
|
|
51
|
+
}
|
|
52
|
+
return 'partial';
|
|
53
|
+
}
|
|
54
|
+
function formatLocatedBanner() {
|
|
55
|
+
return (`> ${exports.LOCATED_MARKER} — exact anchor declaration(s) are in this reply. `
|
|
56
|
+
+ 'Reuse them; do **not** Grep the same symbol names again. '
|
|
57
|
+
+ 'Runtime behavior may still need validation after edits.');
|
|
58
|
+
}
|
|
59
|
+
function formatPartialBanner() {
|
|
60
|
+
return ('> **Partial** — evidence is incomplete (missing relation or budget). '
|
|
61
|
+
+ 'Next: `homegraph_node` / `homegraph_usages` / `homegraph_search` for the named gap — '
|
|
62
|
+
+ 'do **not** Grep the same symbol names already shown.');
|
|
63
|
+
}
|
|
64
|
+
/** Miss: paths only, no source bodies. */
|
|
65
|
+
function formatMissBanner(candidatePaths) {
|
|
66
|
+
const paths = candidatePaths
|
|
67
|
+
.map((p) => p.replace(/\\/g, '/').trim())
|
|
68
|
+
.filter(Boolean)
|
|
69
|
+
.slice(0, 12);
|
|
70
|
+
const list = paths.length
|
|
71
|
+
? paths.map((p) => `- \`${p}\``).join('\n')
|
|
72
|
+
: '- _(no fuzzy path candidates)_';
|
|
73
|
+
return [
|
|
74
|
+
`> ${exports.MISS_MARKER} — no exact in-repo anchor or literal witness. Source bodies omitted.`,
|
|
75
|
+
'Fuzzy candidate paths (no source):',
|
|
76
|
+
list,
|
|
77
|
+
'Next: `homegraph_search` with a more precise symbol, file basename, or quoted UI label.',
|
|
78
|
+
].join('\n');
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* When file basename (sans extension) ≠ primary declaration name.
|
|
82
|
+
* Returns null when names match or inputs are empty.
|
|
83
|
+
*/
|
|
84
|
+
function formatFilenameDeclarationMismatch(filePath, primaryDeclName) {
|
|
85
|
+
if (!primaryDeclName?.trim())
|
|
86
|
+
return null;
|
|
87
|
+
const base = filePath.replace(/\\/g, '/').split('/').pop() ?? '';
|
|
88
|
+
const stem = base.replace(/\.[^.]+$/, '');
|
|
89
|
+
if (!stem)
|
|
90
|
+
return null;
|
|
91
|
+
const norm = (s) => s.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
92
|
+
if (norm(stem) === norm(primaryDeclName))
|
|
93
|
+
return null;
|
|
94
|
+
return (`> ⚠ ${exports.FILENAME_DECL_MISMATCH_MARKER}: file \`${stem}\` vs \`${primaryDeclName.trim()}\` — `
|
|
95
|
+
+ 'prefer the declaration name for edits and follow-up queries.');
|
|
96
|
+
}
|
|
97
|
+
/** Names recorded on the latest counted explore emission. */
|
|
98
|
+
function locatedNamesFromEmission(emission) {
|
|
99
|
+
if (!emission?.locatedNodes?.length)
|
|
100
|
+
return [];
|
|
101
|
+
const out = new Set();
|
|
102
|
+
for (const n of emission.locatedNodes) {
|
|
103
|
+
if (n?.name)
|
|
104
|
+
out.add(n.name);
|
|
105
|
+
if (n?.qualifiedName) {
|
|
106
|
+
out.add(n.qualifiedName);
|
|
107
|
+
const leaf = n.qualifiedName.split(/[.::]/).pop();
|
|
108
|
+
if (leaf)
|
|
109
|
+
out.add(leaf);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return [...out];
|
|
113
|
+
}
|
|
114
|
+
function symbolMatchesLocatedNames(symbol, locatedNames) {
|
|
115
|
+
const raw = symbol?.trim();
|
|
116
|
+
if (!raw || locatedNames.length === 0)
|
|
117
|
+
return false;
|
|
118
|
+
const needle = raw.toLowerCase();
|
|
119
|
+
const leaf = needle.split(/[.::]/).pop() ?? needle;
|
|
120
|
+
for (const n of locatedNames) {
|
|
121
|
+
const ln = n.toLowerCase();
|
|
122
|
+
if (ln === needle || ln === leaf)
|
|
123
|
+
return true;
|
|
124
|
+
if (ln.endsWith(`.${leaf}`) || ln.endsWith(`::${leaf}`))
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Spec 0044 §10.2 — do not depth-fuse-refuse a symbol already present on the
|
|
131
|
+
* latest explore locate list (even after Partial spent its one generic drill).
|
|
132
|
+
*/
|
|
133
|
+
function shouldExemptDepthFuseForLocatedSymbol(prior, symbol) {
|
|
134
|
+
const last = (0, explore_repeat_guard_1.latestCountedExplore)(prior);
|
|
135
|
+
if (!last)
|
|
136
|
+
return false;
|
|
137
|
+
// Closed explores already skip the fuse; exemption matters for Partial/Miss.
|
|
138
|
+
if ((0, explore_session_state_1.inferExploreEvidenceStatus)(last) === 'complete')
|
|
139
|
+
return false;
|
|
140
|
+
return symbolMatchesLocatedNames(symbol, locatedNamesFromEmission(last));
|
|
141
|
+
}
|
|
142
|
+
/** Strip CJK→ASCII synonym seeds when Miss-bound (Spec 0044 §9). */
|
|
143
|
+
function shouldSuppressSynonymExpansion(opts) {
|
|
144
|
+
return !opts.hasExactAnchorHit && !opts.hasLiteralWitness;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=locate-contract.js.map
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare const SERVER_INSTRUCTIONS = "# HomeGraph \u2014 optional structural evidence for this repo\n\n## When to call (path-first, bash-first)\n\nUse ordinary bash/search/read tools first for repository paths, symbols, literal strings and local changes. Skip HomeGraph when those tools provide sufficient evidence, including for difficult implementation tasks. A known path can be read directly; an adequate source result does not need a second graph lookup.\n\nHomeGraph is optional. Use it only for a concrete unresolved relationship that benefits from graph evidence: cross-file state/event propagation, callers/callees, module dependencies or ArkTS-to-native registration. Name the missing relation and use anchors from the current task or source. There is no mandatory number of bash searches before a useful graph query.\n\nChoose the smallest available tool for that gap:\n- Exact usage/reference locations \u2192 `homegraph_usages`; callers/callees \u2192 the corresponding tool.\n- Named module dependencies/cycles \u2192 `homegraph_modules`; native exports/registration \u2192 `homegraph_native`.\n- One missing symbol body \u2192 `homegraph_node`; prefer direct read if its path is already known.\n- An unresolved cross-symbol mechanism \u2192 `homegraph_explore
|
|
2
|
-
export declare const SERVER_INSTRUCTIONS_NO_ROOT_INDEX = "# HomeGraph \u2014 optional per-project evidence\n\nPass `projectPath` to an already indexed folder with `.homegraph/`. No index \u2192 use ordinary tools; indexing is managed by the host.\n\n## When to call (path-first, bash-first)\n\nUse ordinary bash/search/read tools first for repository paths, symbols, literal strings and local changes. Skip HomeGraph when those tools provide sufficient evidence, including for difficult implementation tasks. A known path can be read directly; an adequate source result does not need a second graph lookup.\n\nHomeGraph is optional. Use it only for a concrete unresolved relationship that benefits from graph evidence: cross-file state/event propagation, callers/callees, module dependencies or ArkTS-to-native registration. Name the missing relation and use anchors from the current task or source. There is no mandatory number of bash searches before a useful graph query.\n\nChoose the smallest available tool for that gap:\n- Exact usage/reference locations \u2192 `homegraph_usages`; callers/callees \u2192 the corresponding tool.\n- Named module dependencies/cycles \u2192 `homegraph_modules`; native exports/registration \u2192 `homegraph_native`.\n- One missing symbol body \u2192 `homegraph_node`; prefer direct read if its path is already known.\n- An unresolved cross-symbol mechanism \u2192 `homegraph_explore
|
|
1
|
+
export declare const SERVER_INSTRUCTIONS = "# HomeGraph \u2014 optional structural evidence for this repo\n\n## When to call (path-first, bash-first)\n\nUse ordinary bash/search/read tools first for repository paths, symbols, literal strings and local changes. Skip HomeGraph when those tools provide sufficient evidence, including for difficult implementation tasks. A known path can be read directly; an adequate source result does not need a second graph lookup.\n\nHomeGraph is optional. Use it only for a concrete unresolved relationship that benefits from graph evidence: cross-file state/event propagation, callers/callees, module dependencies or ArkTS-to-native registration. Name the missing relation and use anchors from the current task or source. There is no mandatory number of bash searches before a useful graph query.\n\nChoose the smallest available tool for that gap:\n- Engineering overview / which module owns a feature / where `route_map.json` or resource dirs live \u2192 `homegraph_project` (module map + Harmony skeleton pointers + Module roster with local `file:` deps + bounded resources path inventory: string.json / form_config|shortcuts_config / rawfile / media / on-disk modules not in the graph). It does **not** return symbol bodies, call graphs, or JSON/media contents.\n- Exact usage/reference locations \u2192 `homegraph_usages`; callers/callees \u2192 the corresponding tool.\n- Named module dependencies/cycles \u2192 `homegraph_modules`; native exports/registration \u2192 `homegraph_native`.\n- One missing symbol body \u2192 `homegraph_node`; prefer direct read if its path is already known.\n- An unresolved cross-symbol mechanism or route registration edges \u2192 `homegraph_explore` (may include Spec 0039 Registration sources for route_map; Spec 0041 Resource hits for `element/string.json` literals; Spec 0048 Capability profiles for form/shortcuts, or an explicit no-in-repo form note; Seam notes for stubs). Do not use it for routine pre-edit orientation, a literal search, or to re-confirm source already found with bash.\n- ArkUI migration analysis \u2192 `homegraph_arkui_migrate` when that analysis is needed; SDK contracts \u2192 project declarations or SDK documentation.\n- Harmony `element/string.json` key/value lookup is searchable via explore/search (Resource hits may include bound `.ets` anchors; no graph edges). Project lists resource **paths**; color/other non-allowlisted files still need Grep/Read.\n- Harmony `form_config.json` / `shortcuts_config.json` are indexed as capability profiles (paths + names; no UI edges). Card/shortcut tasks should use explore/project \u2014 do not treat SDK Form `.d.ts` as project wiring when the negative note says no in-repo form.\n\nDo not call explore after a focused tool already answered the relation. Keep working directly once the edit location and affected behavior are sufficiently supported. Task difficulty and file count alone do not require graph use.\n\n## Query and recovery\n\nWrite one focused sentence: requested action + target + known anchors + unresolved relation + preservation constraints. Use the full public task as `taskContext` when needed. Keep UI labels verbatim; use exact symbols from the task or source instead of inventing names or piling up generic keywords. Tool replies may begin with `HomeGraph project root: `\u2026`` \u2014 that absolute root is the join base for repo-relative paths below; pass those paths to Read/Grep as-is, or join as `<root>/<relative>` (use `/`). Do not invent experiment/result directory prefixes; a path refusal requires valid in-repo relocation, not broader permissions. When this MCP session already has a bound project root, a mismatched `projectPath` is ignored (results stay on the bound root) with a short English notice \u2014 do not treat that as a hard error.\n\nA project map (`homegraph_project`) is navigation and Harmony skeleton pointers, not proof of a located feature and not a call graph. Preserve requested product/module scope and verify each candidate before editing. Start with one focused graph request; recover only a named missing body/relation. Budget: \u22642 `homegraph_explore` attempts per project, \u22641 focused depth recovery; existing runtime budgets may be tighter. These are ceilings, never a required sequence. If evidence is still missing, use targeted bash/search/read and continue implementation. Do not expand into unrelated files merely to exhaust a budget.\n\n## Index status\n\nTool replies may start with `HomeGraph project root: \u2026` (absolute join base for relative paths) and end with `HomeGraph status=\u2026`. status: empty=not ready \u00B7 fast=map only (homegraph_project) \u00B7 full=fresh \u00B7 dirty=usable but listed paths outdated \u00B7 syncing=write lock, retry.\n\n\n- ArkTS evidence packs keep complete declarations and the source dependencies of displayed static relations together. Gaps identify omitted, stale or unindexed evidence; inspect only a gap relevant to the task. A static link does not prove runtime ordering or value propagation, and a complete declaration does not prove its enclosing call conditions.\n- ArkTS path evidence follows typed, directed relations between named anchors within a bounded search. A provided path includes intermediate declarations and registration sites. Check its goal and stop reason; no path in scope does not prove no relationship. Qualify ambiguous symbols with their owning type or file.\n- Reuse complete, unchanged, line-numbered source ranges already visible. An outline, path list, truncated body or SDK declaration cannot replace missing implementation evidence. A slice hash identifies that excerpt, not the whole file. Refresh affected ranges after edits.\n- An empty edge set means the relation may be unindexed, not absent. For partial, stale or irrelevant results, inspect the exact missing source with scoped search/read. After a query adds no evidence, change the method or scope rather than paraphrasing the same explore.\n- Retrieval completion is not task completion. Continue the requested edits and validation; check the original task's behavior and preservation constraints. Build success alone does not establish functional correctness.\n\nNo index \u2192 use ordinary tools; indexing is managed by the host. Do not run HomeGraph initialization as part of solving the task.\n";
|
|
2
|
+
export declare const SERVER_INSTRUCTIONS_NO_ROOT_INDEX = "# HomeGraph \u2014 optional per-project evidence\n\nPass `projectPath` to an already indexed folder with `.homegraph/`. No index \u2192 use ordinary tools; indexing is managed by the host.\n\n## When to call (path-first, bash-first)\n\nUse ordinary bash/search/read tools first for repository paths, symbols, literal strings and local changes. Skip HomeGraph when those tools provide sufficient evidence, including for difficult implementation tasks. A known path can be read directly; an adequate source result does not need a second graph lookup.\n\nHomeGraph is optional. Use it only for a concrete unresolved relationship that benefits from graph evidence: cross-file state/event propagation, callers/callees, module dependencies or ArkTS-to-native registration. Name the missing relation and use anchors from the current task or source. There is no mandatory number of bash searches before a useful graph query.\n\nChoose the smallest available tool for that gap:\n- Engineering overview / which module owns a feature / where `route_map.json` or resource dirs live \u2192 `homegraph_project` (module map + Harmony skeleton pointers + Module roster with local `file:` deps + bounded resources path inventory: string.json / form_config|shortcuts_config / rawfile / media / on-disk modules not in the graph). It does **not** return symbol bodies, call graphs, or JSON/media contents.\n- Exact usage/reference locations \u2192 `homegraph_usages`; callers/callees \u2192 the corresponding tool.\n- Named module dependencies/cycles \u2192 `homegraph_modules`; native exports/registration \u2192 `homegraph_native`.\n- One missing symbol body \u2192 `homegraph_node`; prefer direct read if its path is already known.\n- An unresolved cross-symbol mechanism or route registration edges \u2192 `homegraph_explore` (may include Spec 0039 Registration sources for route_map; Spec 0041 Resource hits for `element/string.json` literals; Spec 0048 Capability profiles for form/shortcuts, or an explicit no-in-repo form note; Seam notes for stubs). Do not use it for routine pre-edit orientation, a literal search, or to re-confirm source already found with bash.\n- ArkUI migration analysis \u2192 `homegraph_arkui_migrate` when that analysis is needed; SDK contracts \u2192 project declarations or SDK documentation.\n- Harmony `element/string.json` key/value lookup is searchable via explore/search (Resource hits may include bound `.ets` anchors; no graph edges). Project lists resource **paths**; color/other non-allowlisted files still need Grep/Read.\n- Harmony `form_config.json` / `shortcuts_config.json` are indexed as capability profiles (paths + names; no UI edges). Card/shortcut tasks should use explore/project \u2014 do not treat SDK Form `.d.ts` as project wiring when the negative note says no in-repo form.\n\nDo not call explore after a focused tool already answered the relation. Keep working directly once the edit location and affected behavior are sufficiently supported. Task difficulty and file count alone do not require graph use.\n\n## Query and recovery\n\nWrite one focused sentence: requested action + target + known anchors + unresolved relation + preservation constraints. Use the full public task as `taskContext` when needed. Keep UI labels verbatim; use exact symbols from the task or source instead of inventing names or piling up generic keywords. Tool replies may begin with `HomeGraph project root: `\u2026`` \u2014 that absolute root is the join base for repo-relative paths below; pass those paths to Read/Grep as-is, or join as `<root>/<relative>` (use `/`). Do not invent experiment/result directory prefixes; a path refusal requires valid in-repo relocation, not broader permissions. When this MCP session already has a bound project root, a mismatched `projectPath` is ignored (results stay on the bound root) with a short English notice \u2014 do not treat that as a hard error.\n\nA project map (`homegraph_project`) is navigation and Harmony skeleton pointers, not proof of a located feature and not a call graph. Preserve requested product/module scope and verify each candidate before editing. Start with one focused graph request; recover only a named missing body/relation. Budget: \u22642 `homegraph_explore` attempts per project, \u22641 focused depth recovery; existing runtime budgets may be tighter. These are ceilings, never a required sequence. If evidence is still missing, use targeted bash/search/read and continue implementation. Do not expand into unrelated files merely to exhaust a budget.\n\n## Index status\n\nTool replies may start with `HomeGraph project root: \u2026` (absolute join base for relative paths) and end with `HomeGraph status=\u2026`. status: empty=not ready \u00B7 fast=map only (homegraph_project) \u00B7 full=fresh \u00B7 dirty=usable but listed paths outdated \u00B7 syncing=write lock, retry.\n\n\n- ArkTS evidence packs keep complete declarations and the source dependencies of displayed static relations together. Gaps identify omitted, stale or unindexed evidence; inspect only a gap relevant to the task. A static link does not prove runtime ordering or value propagation, and a complete declaration does not prove its enclosing call conditions.\n- ArkTS path evidence follows typed, directed relations between named anchors within a bounded search. A provided path includes intermediate declarations and registration sites. Check its goal and stop reason; no path in scope does not prove no relationship. Qualify ambiguous symbols with their owning type or file.\n- Reuse complete, unchanged, line-numbered source ranges already visible. An outline, path list, truncated body or SDK declaration cannot replace missing implementation evidence. A slice hash identifies that excerpt, not the whole file. Refresh affected ranges after edits.\n- An empty edge set means the relation may be unindexed, not absent. For partial, stale or irrelevant results, inspect the exact missing source with scoped search/read. After a query adds no evidence, change the method or scope rather than paraphrasing the same explore.\n- Retrieval completion is not task completion. Continue the requested edits and validation; check the original task's behavior and preservation constraints. Build success alone does not establish functional correctness.\n\n";
|
|
3
3
|
//# sourceMappingURL=server-instructions.d.ts.map
|
|
@@ -20,23 +20,26 @@ Use ordinary bash/search/read tools first for repository paths, symbols, literal
|
|
|
20
20
|
HomeGraph is optional. Use it only for a concrete unresolved relationship that benefits from graph evidence: cross-file state/event propagation, callers/callees, module dependencies or ArkTS-to-native registration. Name the missing relation and use anchors from the current task or source. There is no mandatory number of bash searches before a useful graph query.
|
|
21
21
|
|
|
22
22
|
Choose the smallest available tool for that gap:
|
|
23
|
+
- Engineering overview / which module owns a feature / where \`route_map.json\` or resource dirs live → \`homegraph_project\` (module map + Harmony skeleton pointers + Module roster with local \`file:\` deps + bounded resources path inventory: string.json / form_config|shortcuts_config / rawfile / media / on-disk modules not in the graph). It does **not** return symbol bodies, call graphs, or JSON/media contents.
|
|
23
24
|
- Exact usage/reference locations → \`homegraph_usages\`; callers/callees → the corresponding tool.
|
|
24
25
|
- Named module dependencies/cycles → \`homegraph_modules\`; native exports/registration → \`homegraph_native\`.
|
|
25
26
|
- One missing symbol body → \`homegraph_node\`; prefer direct read if its path is already known.
|
|
26
|
-
- An unresolved cross-symbol mechanism → \`homegraph_explore
|
|
27
|
+
- An unresolved cross-symbol mechanism or route registration edges → \`homegraph_explore\` (may include Spec 0039 Registration sources for route_map; Spec 0041 Resource hits for \`element/string.json\` literals; Spec 0048 Capability profiles for form/shortcuts, or an explicit no-in-repo form note; Seam notes for stubs). Do not use it for routine pre-edit orientation, a literal search, or to re-confirm source already found with bash.
|
|
27
28
|
- ArkUI migration analysis → \`homegraph_arkui_migrate\` when that analysis is needed; SDK contracts → project declarations or SDK documentation.
|
|
29
|
+
- Harmony \`element/string.json\` key/value lookup is searchable via explore/search (Resource hits may include bound \`.ets\` anchors; no graph edges). Project lists resource **paths**; color/other non-allowlisted files still need Grep/Read.
|
|
30
|
+
- Harmony \`form_config.json\` / \`shortcuts_config.json\` are indexed as capability profiles (paths + names; no UI edges). Card/shortcut tasks should use explore/project — do not treat SDK Form \`.d.ts\` as project wiring when the negative note says no in-repo form.
|
|
28
31
|
|
|
29
32
|
Do not call explore after a focused tool already answered the relation. Keep working directly once the edit location and affected behavior are sufficiently supported. Task difficulty and file count alone do not require graph use.
|
|
30
33
|
`;
|
|
31
34
|
const QUERY = `## Query and recovery
|
|
32
35
|
|
|
33
|
-
Write one focused sentence: requested action + target + known anchors + unresolved relation + preservation constraints. Use the full public task as \`taskContext\` when needed. Keep UI labels verbatim; use exact symbols from the task or source instead of inventing names or piling up generic keywords.
|
|
36
|
+
Write one focused sentence: requested action + target + known anchors + unresolved relation + preservation constraints. Use the full public task as \`taskContext\` when needed. Keep UI labels verbatim; use exact symbols from the task or source instead of inventing names or piling up generic keywords. Tool replies may begin with \`HomeGraph project root: \`…\`\` — that absolute root is the join base for repo-relative paths below; pass those paths to Read/Grep as-is, or join as \`<root>/<relative>\` (use \`/\`). Do not invent experiment/result directory prefixes; a path refusal requires valid in-repo relocation, not broader permissions. When this MCP session already has a bound project root, a mismatched \`projectPath\` is ignored (results stay on the bound root) with a short English notice — do not treat that as a hard error.
|
|
34
37
|
|
|
35
|
-
A project map is navigation, not proof of a located feature. Preserve requested product/module scope and verify each candidate before editing. Start with one focused graph request; recover only a named missing body/relation. Budget: ≤2 \`homegraph_explore\` attempts per project, ≤1 focused depth recovery; existing runtime budgets may be tighter. These are ceilings, never a required sequence. If evidence is still missing, use targeted bash/search/read and continue implementation. Do not expand into unrelated files merely to exhaust a budget.
|
|
38
|
+
A project map (\`homegraph_project\`) is navigation and Harmony skeleton pointers, not proof of a located feature and not a call graph. Preserve requested product/module scope and verify each candidate before editing. Start with one focused graph request; recover only a named missing body/relation. Budget: ≤2 \`homegraph_explore\` attempts per project, ≤1 focused depth recovery; existing runtime budgets may be tighter. These are ceilings, never a required sequence. If evidence is still missing, use targeted bash/search/read and continue implementation. Do not expand into unrelated files merely to exhaust a budget.
|
|
36
39
|
`;
|
|
37
40
|
const INDEX_STATUS = `## Index status
|
|
38
41
|
|
|
39
|
-
Tool replies end with \`HomeGraph status=…\`. ${index_availability_1.PRODUCT_STATUS_GLOSSARY}.
|
|
42
|
+
Tool replies may start with \`HomeGraph project root: …\` (absolute join base for relative paths) and end with \`HomeGraph status=…\`. ${index_availability_1.PRODUCT_STATUS_GLOSSARY}.
|
|
40
43
|
`;
|
|
41
44
|
exports.SERVER_INSTRUCTIONS = `# HomeGraph — optional structural evidence for this repo
|
|
42
45
|
|