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
|
@@ -30,4 +30,49 @@ export interface BuiltProjectMap {
|
|
|
30
30
|
*/
|
|
31
31
|
export declare function buildProjectMapScan(projectRoot: string): BuiltProjectMap;
|
|
32
32
|
export declare function parseBuildPhase(raw: string | null | undefined): BuildPhase | null;
|
|
33
|
+
/**
|
|
34
|
+
* Spec 0040 — optional bundleName/name from app.json5 (AppScope or root).
|
|
35
|
+
* Best-effort; returns null on missing/parse failure.
|
|
36
|
+
*/
|
|
37
|
+
export declare function readHarmonyAppBundleName(projectRoot: string): string | null;
|
|
38
|
+
/**
|
|
39
|
+
* Spec 0040 — project-relative paths to Spec 0039 route profile JSON under a module root.
|
|
40
|
+
*/
|
|
41
|
+
export declare function listHarmonyRouteProfilesUnderModule(projectRoot: string, moduleRootPath: string, cap?: number): string[];
|
|
42
|
+
/** Spec 0040 — oh-package name at module root, if any. */
|
|
43
|
+
export declare function readModuleOhPackageName(projectRoot: string, moduleRootPath: string): string | null;
|
|
44
|
+
/** Spec 0042 B — path-only Harmony resource inventory (no parse, no edges). */
|
|
45
|
+
export interface HarmonyResourceInventory {
|
|
46
|
+
stringJson: string[];
|
|
47
|
+
rawfiles: string[];
|
|
48
|
+
mediaDirs: Array<{
|
|
49
|
+
dir: string;
|
|
50
|
+
countsByExt: Record<string, number>;
|
|
51
|
+
}>;
|
|
52
|
+
/** Spec 0048 — form_config.json / shortcuts_config.json paths. */
|
|
53
|
+
capabilityProfiles: string[];
|
|
54
|
+
unindexedDirs: Array<{
|
|
55
|
+
path: string;
|
|
56
|
+
reason: string;
|
|
57
|
+
}>;
|
|
58
|
+
truncated: boolean;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Bounded walk: whitelist paths under `resources/…` plus package dirs with zero
|
|
62
|
+
* indexed sources. Does not read JSON bodies or media bytes.
|
|
63
|
+
*/
|
|
64
|
+
export declare function scanHarmonyResourceInventory(projectRoot: string, options?: {
|
|
65
|
+
indexedPaths?: Iterable<string>;
|
|
66
|
+
}): HarmonyResourceInventory;
|
|
67
|
+
/** Markdown section for Spec 0042 B (empty string when nothing to show). */
|
|
68
|
+
export declare function formatHarmonyResourceInventory(inv: HarmonyResourceInventory): string;
|
|
69
|
+
/**
|
|
70
|
+
* Spec 0048 §4 — module roster with oh-package name + local file: deps.
|
|
71
|
+
*/
|
|
72
|
+
export declare function formatHarmonyModuleRoster(projectRoot: string, modules: Array<{
|
|
73
|
+
name: string;
|
|
74
|
+
rootPath: string;
|
|
75
|
+
kind: string;
|
|
76
|
+
fileCount: number;
|
|
77
|
+
}>, maxRows?: number): string;
|
|
33
78
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -41,6 +41,12 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
42
|
exports.buildProjectMapScan = buildProjectMapScan;
|
|
43
43
|
exports.parseBuildPhase = parseBuildPhase;
|
|
44
|
+
exports.readHarmonyAppBundleName = readHarmonyAppBundleName;
|
|
45
|
+
exports.listHarmonyRouteProfilesUnderModule = listHarmonyRouteProfilesUnderModule;
|
|
46
|
+
exports.readModuleOhPackageName = readModuleOhPackageName;
|
|
47
|
+
exports.scanHarmonyResourceInventory = scanHarmonyResourceInventory;
|
|
48
|
+
exports.formatHarmonyResourceInventory = formatHarmonyResourceInventory;
|
|
49
|
+
exports.formatHarmonyModuleRoster = formatHarmonyModuleRoster;
|
|
44
50
|
const fs = __importStar(require("fs"));
|
|
45
51
|
const path = __importStar(require("path"));
|
|
46
52
|
const extraction_1 = require("../extraction");
|
|
@@ -178,9 +184,19 @@ function discoverOhpmPackages(projectRoot) {
|
|
|
178
184
|
}
|
|
179
185
|
function readOhpmName(manifestAbs) {
|
|
180
186
|
try {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
187
|
+
const text = fs.readFileSync(manifestAbs, 'utf-8');
|
|
188
|
+
try {
|
|
189
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
190
|
+
const parsed = require('jsonc-parser').parse(text);
|
|
191
|
+
if (typeof parsed?.name === 'string' && parsed.name.trim())
|
|
192
|
+
return parsed.name.trim();
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
/* regex */
|
|
196
|
+
}
|
|
197
|
+
const m = text.match(/["']name["']\s*:\s*["']([^"']+)["']/) ||
|
|
198
|
+
text.match(/name\s*:\s*["']([^"']+)["']/);
|
|
199
|
+
return m?.[1]?.trim() || null;
|
|
184
200
|
}
|
|
185
201
|
catch {
|
|
186
202
|
return null;
|
|
@@ -217,4 +233,356 @@ function parseBuildPhase(raw) {
|
|
|
217
233
|
}
|
|
218
234
|
return null;
|
|
219
235
|
}
|
|
236
|
+
const SKELETON_SKIP_DIRS = new Set([
|
|
237
|
+
'node_modules',
|
|
238
|
+
'oh_modules',
|
|
239
|
+
'.git',
|
|
240
|
+
'.homegraph',
|
|
241
|
+
'.hvigor',
|
|
242
|
+
'.preview',
|
|
243
|
+
'build',
|
|
244
|
+
'dist',
|
|
245
|
+
'out',
|
|
246
|
+
'.cxx',
|
|
247
|
+
]);
|
|
248
|
+
const ROUTE_PROFILE_WALK_MAX_DEPTH = 8;
|
|
249
|
+
const ROUTE_PROFILE_DIR_BUDGET = 400;
|
|
250
|
+
const ROUTE_PROFILE_CAP_PER_MODULE = 8;
|
|
251
|
+
/**
|
|
252
|
+
* Spec 0040 — optional bundleName/name from app.json5 (AppScope or root).
|
|
253
|
+
* Best-effort; returns null on missing/parse failure.
|
|
254
|
+
*/
|
|
255
|
+
function readHarmonyAppBundleName(projectRoot) {
|
|
256
|
+
for (const rel of ['AppScope/app.json5', 'app.json5']) {
|
|
257
|
+
const abs = path.join(projectRoot, rel);
|
|
258
|
+
if (!fs.existsSync(abs))
|
|
259
|
+
continue;
|
|
260
|
+
try {
|
|
261
|
+
const text = fs.readFileSync(abs, 'utf-8');
|
|
262
|
+
let bundle = null;
|
|
263
|
+
try {
|
|
264
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
265
|
+
const parsed = require('jsonc-parser').parse(text);
|
|
266
|
+
if (parsed?.app && typeof parsed.app.bundleName === 'string' && parsed.app.bundleName.trim()) {
|
|
267
|
+
bundle = parsed.app.bundleName.trim();
|
|
268
|
+
}
|
|
269
|
+
else if (typeof parsed?.bundleName === 'string' && parsed.bundleName.trim()) {
|
|
270
|
+
bundle = parsed.bundleName.trim();
|
|
271
|
+
}
|
|
272
|
+
else if (typeof parsed?.name === 'string' && parsed.name.trim()) {
|
|
273
|
+
bundle = parsed.name.trim();
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
/* regex fallback below */
|
|
278
|
+
}
|
|
279
|
+
if (!bundle) {
|
|
280
|
+
const m = text.match(/["']bundleName["']\s*:\s*["']([^"']+)["']/) ||
|
|
281
|
+
text.match(/bundleName\s*:\s*["']([^"']+)["']/);
|
|
282
|
+
if (m?.[1])
|
|
283
|
+
bundle = m[1];
|
|
284
|
+
}
|
|
285
|
+
if (bundle)
|
|
286
|
+
return bundle;
|
|
287
|
+
}
|
|
288
|
+
catch {
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return null;
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Spec 0040 — project-relative paths to Spec 0039 route profile JSON under a module root.
|
|
296
|
+
*/
|
|
297
|
+
function listHarmonyRouteProfilesUnderModule(projectRoot, moduleRootPath, cap = ROUTE_PROFILE_CAP_PER_MODULE) {
|
|
298
|
+
const rootAbs = path.resolve(projectRoot);
|
|
299
|
+
const modAbs = moduleRootPath
|
|
300
|
+
? path.resolve(projectRoot, moduleRootPath)
|
|
301
|
+
: rootAbs;
|
|
302
|
+
if (!fs.existsSync(modAbs))
|
|
303
|
+
return [];
|
|
304
|
+
const found = [];
|
|
305
|
+
const queue = [{ abs: modAbs, depth: 0 }];
|
|
306
|
+
let visited = 0;
|
|
307
|
+
while (queue.length > 0 && found.length < cap) {
|
|
308
|
+
const { abs, depth } = queue.shift();
|
|
309
|
+
if (++visited > ROUTE_PROFILE_DIR_BUDGET)
|
|
310
|
+
break;
|
|
311
|
+
let entries;
|
|
312
|
+
try {
|
|
313
|
+
entries = fs.readdirSync(abs, { withFileTypes: true });
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
for (const e of entries) {
|
|
319
|
+
if (found.length >= cap)
|
|
320
|
+
break;
|
|
321
|
+
const child = path.join(abs, e.name);
|
|
322
|
+
if (e.isDirectory()) {
|
|
323
|
+
if (depth >= ROUTE_PROFILE_WALK_MAX_DEPTH)
|
|
324
|
+
continue;
|
|
325
|
+
if (e.name.startsWith('.') || SKELETON_SKIP_DIRS.has(e.name))
|
|
326
|
+
continue;
|
|
327
|
+
queue.push({ abs: child, depth: depth + 1 });
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
if (!e.isFile())
|
|
331
|
+
continue;
|
|
332
|
+
const rel = path.relative(rootAbs, child).replace(/\\/g, '/');
|
|
333
|
+
if ((0, grammars_1.isHarmonyRouteProfileJson)(rel))
|
|
334
|
+
found.push(rel);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
return found.sort();
|
|
338
|
+
}
|
|
339
|
+
/** Spec 0040 — oh-package name at module root, if any. */
|
|
340
|
+
function readModuleOhPackageName(projectRoot, moduleRootPath) {
|
|
341
|
+
const abs = path.join(projectRoot, moduleRootPath || '.', OHPM_MANIFEST);
|
|
342
|
+
return readOhpmName(abs);
|
|
343
|
+
}
|
|
344
|
+
const RESOURCE_WALK_MAX_DEPTH = 14;
|
|
345
|
+
const RESOURCE_DIR_BUDGET = 4000;
|
|
346
|
+
const RESOURCE_STRING_CAP = 24;
|
|
347
|
+
const RESOURCE_RAWFILE_CAP = 40;
|
|
348
|
+
const RESOURCE_MEDIA_DIR_CAP = 24;
|
|
349
|
+
const RESOURCE_UNINDEXED_CAP = 16;
|
|
350
|
+
const RESOURCE_CAPABILITY_CAP = 16;
|
|
351
|
+
const RESOURCE_CONFIG_BASENAMES = new Set([
|
|
352
|
+
'shortcuts_config.json',
|
|
353
|
+
'form_config.json',
|
|
354
|
+
]);
|
|
355
|
+
/**
|
|
356
|
+
* Bounded walk: whitelist paths under `resources/…` plus package dirs with zero
|
|
357
|
+
* indexed sources. Does not read JSON bodies or media bytes.
|
|
358
|
+
*/
|
|
359
|
+
function scanHarmonyResourceInventory(projectRoot, options) {
|
|
360
|
+
const rootAbs = path.resolve(projectRoot);
|
|
361
|
+
const out = {
|
|
362
|
+
stringJson: [],
|
|
363
|
+
rawfiles: [],
|
|
364
|
+
mediaDirs: [],
|
|
365
|
+
capabilityProfiles: [],
|
|
366
|
+
unindexedDirs: [],
|
|
367
|
+
truncated: false,
|
|
368
|
+
};
|
|
369
|
+
if (!fs.existsSync(rootAbs))
|
|
370
|
+
return out;
|
|
371
|
+
const indexed = new Set();
|
|
372
|
+
for (const p of options?.indexedPaths ?? []) {
|
|
373
|
+
indexed.add(p.replace(/\\/g, '/'));
|
|
374
|
+
}
|
|
375
|
+
const mediaCounts = new Map();
|
|
376
|
+
const packageDirs = new Map();
|
|
377
|
+
const queue = [{ abs: rootAbs, depth: 0 }];
|
|
378
|
+
let visited = 0;
|
|
379
|
+
while (queue.length > 0) {
|
|
380
|
+
const { abs, depth } = queue.shift();
|
|
381
|
+
if (++visited > RESOURCE_DIR_BUDGET) {
|
|
382
|
+
out.truncated = true;
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
let entries;
|
|
386
|
+
try {
|
|
387
|
+
entries = fs.readdirSync(abs, { withFileTypes: true });
|
|
388
|
+
}
|
|
389
|
+
catch {
|
|
390
|
+
continue;
|
|
391
|
+
}
|
|
392
|
+
for (const e of entries) {
|
|
393
|
+
const child = path.join(abs, e.name);
|
|
394
|
+
const rel = path.relative(rootAbs, child).replace(/\\/g, '/');
|
|
395
|
+
if (e.isDirectory()) {
|
|
396
|
+
if (depth >= RESOURCE_WALK_MAX_DEPTH)
|
|
397
|
+
continue;
|
|
398
|
+
if (e.name.startsWith('.') || SKELETON_SKIP_DIRS.has(e.name))
|
|
399
|
+
continue;
|
|
400
|
+
queue.push({ abs: child, depth: depth + 1 });
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
if (!e.isFile() || !rel || rel.startsWith('..'))
|
|
404
|
+
continue;
|
|
405
|
+
if (/(?:^|\/)resources\/(?:[^/]+\/)*element\/string\.json$/i.test(rel)) {
|
|
406
|
+
if (out.stringJson.length < RESOURCE_STRING_CAP)
|
|
407
|
+
out.stringJson.push(rel);
|
|
408
|
+
else
|
|
409
|
+
out.truncated = true;
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
if (/(?:^|\/)resources\/(?:[^/]+\/)*rawfile\//i.test(rel)) {
|
|
413
|
+
if (out.rawfiles.length < RESOURCE_RAWFILE_CAP)
|
|
414
|
+
out.rawfiles.push(rel);
|
|
415
|
+
else
|
|
416
|
+
out.truncated = true;
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
const mediaMatch = rel.match(/^(.*?\/resources\/(?:[^/]+\/)*(?:base\/)?media)\/[^/]+$/i);
|
|
420
|
+
if (mediaMatch) {
|
|
421
|
+
const dir = mediaMatch[1].replace(/\\/g, '/');
|
|
422
|
+
const ext = path.extname(rel).slice(1).toLowerCase() || 'bin';
|
|
423
|
+
const counts = mediaCounts.get(dir) ?? {};
|
|
424
|
+
counts[ext] = (counts[ext] ?? 0) + 1;
|
|
425
|
+
mediaCounts.set(dir, counts);
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
428
|
+
const base = rel.split('/').pop()?.toLowerCase() ?? '';
|
|
429
|
+
if (RESOURCE_CONFIG_BASENAMES.has(base)) {
|
|
430
|
+
if (out.capabilityProfiles.length < RESOURCE_CAPABILITY_CAP) {
|
|
431
|
+
if (!out.capabilityProfiles.includes(rel))
|
|
432
|
+
out.capabilityProfiles.push(rel);
|
|
433
|
+
}
|
|
434
|
+
else {
|
|
435
|
+
out.truncated = true;
|
|
436
|
+
}
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
if (base === 'oh-package.json5' || base === 'module.json5') {
|
|
440
|
+
const dirRel = rel.includes('/') ? rel.slice(0, rel.lastIndexOf('/')) : '';
|
|
441
|
+
const cur = packageDirs.get(dirRel) ?? { hasOhpm: false, hasModule: false };
|
|
442
|
+
if (base === 'oh-package.json5')
|
|
443
|
+
cur.hasOhpm = true;
|
|
444
|
+
else
|
|
445
|
+
cur.hasModule = true;
|
|
446
|
+
packageDirs.set(dirRel, cur);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
for (const [dir, counts] of [...mediaCounts.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
451
|
+
if (out.mediaDirs.length >= RESOURCE_MEDIA_DIR_CAP) {
|
|
452
|
+
out.truncated = true;
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
out.mediaDirs.push({ dir, countsByExt: counts });
|
|
456
|
+
}
|
|
457
|
+
out.stringJson.sort((a, b) => a.localeCompare(b));
|
|
458
|
+
out.rawfiles.sort((a, b) => a.localeCompare(b));
|
|
459
|
+
out.capabilityProfiles.sort((a, b) => a.localeCompare(b));
|
|
460
|
+
// Unindexed package/module dirs: on disk but no indexed source under them.
|
|
461
|
+
for (const [dirRel, flags] of [...packageDirs.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
462
|
+
if (out.unindexedDirs.length >= RESOURCE_UNINDEXED_CAP) {
|
|
463
|
+
out.truncated = true;
|
|
464
|
+
break;
|
|
465
|
+
}
|
|
466
|
+
if (!dirRel)
|
|
467
|
+
continue; // project root always "indexed" via map
|
|
468
|
+
const prefix = `${dirRel}/`;
|
|
469
|
+
const hasIndexed = [...indexed].some((p) => p === dirRel || p.startsWith(prefix));
|
|
470
|
+
if (hasIndexed)
|
|
471
|
+
continue;
|
|
472
|
+
// Skip pure resource-only package markers under resources/ (not a module root).
|
|
473
|
+
if (/(?:^|\/)resources\//i.test(dirRel))
|
|
474
|
+
continue;
|
|
475
|
+
const reason = flags.hasOhpm
|
|
476
|
+
? 'oh-package.json5 present; 0 indexed source files'
|
|
477
|
+
: 'module.json5 present; 0 indexed source files';
|
|
478
|
+
out.unindexedDirs.push({ path: dirRel, reason });
|
|
479
|
+
}
|
|
480
|
+
return out;
|
|
481
|
+
}
|
|
482
|
+
/** Markdown section for Spec 0042 B (empty string when nothing to show). */
|
|
483
|
+
function formatHarmonyResourceInventory(inv) {
|
|
484
|
+
const lines = [];
|
|
485
|
+
const hasResources = inv.stringJson.length > 0
|
|
486
|
+
|| inv.rawfiles.length > 0
|
|
487
|
+
|| inv.mediaDirs.length > 0
|
|
488
|
+
|| inv.capabilityProfiles.length > 0;
|
|
489
|
+
if (hasResources) {
|
|
490
|
+
lines.push('### HarmonyOS resources');
|
|
491
|
+
if (inv.stringJson.length) {
|
|
492
|
+
const shown = inv.stringJson.slice(0, 12);
|
|
493
|
+
lines.push(`- string.json (${inv.stringJson.length}): ${shown.map((p) => `\`${p}\``).join(', ')}`
|
|
494
|
+
+ (inv.stringJson.length > shown.length ? `, +${inv.stringJson.length - shown.length} more` : ''));
|
|
495
|
+
}
|
|
496
|
+
if (inv.capabilityProfiles.length) {
|
|
497
|
+
const shown = inv.capabilityProfiles.slice(0, 12);
|
|
498
|
+
lines.push(`- capability profiles (${inv.capabilityProfiles.length}): ${shown.map((p) => `\`${p}\``).join(', ')}`
|
|
499
|
+
+ (inv.capabilityProfiles.length > shown.length
|
|
500
|
+
? `, +${inv.capabilityProfiles.length - shown.length} more`
|
|
501
|
+
: ''));
|
|
502
|
+
}
|
|
503
|
+
if (inv.rawfiles.length) {
|
|
504
|
+
const shown = inv.rawfiles.slice(0, 12);
|
|
505
|
+
lines.push(`- rawfile (${inv.rawfiles.length}): ${shown.map((p) => `\`${p}\``).join(', ')}`
|
|
506
|
+
+ (inv.rawfiles.length > shown.length ? `, +${inv.rawfiles.length - shown.length} more` : ''));
|
|
507
|
+
}
|
|
508
|
+
for (const m of inv.mediaDirs.slice(0, 8)) {
|
|
509
|
+
const counts = Object.entries(m.countsByExt)
|
|
510
|
+
.sort((a, b) => a[0].localeCompare(b[0]))
|
|
511
|
+
.map(([ext, n]) => `${ext}=${n}`)
|
|
512
|
+
.join(', ');
|
|
513
|
+
lines.push(`- media: \`${m.dir}\`${counts ? ` (${counts})` : ''}`);
|
|
514
|
+
}
|
|
515
|
+
if (inv.mediaDirs.length > 8) {
|
|
516
|
+
lines.push(`- media: … +${inv.mediaDirs.length - 8} more dirs`);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (inv.unindexedDirs.length) {
|
|
520
|
+
if (lines.length)
|
|
521
|
+
lines.push('');
|
|
522
|
+
lines.push('### On disk, not in graph');
|
|
523
|
+
for (const u of inv.unindexedDirs) {
|
|
524
|
+
lines.push(`- \`${u.path}/\` (${u.reason})`);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
if (inv.truncated) {
|
|
528
|
+
lines.push('');
|
|
529
|
+
lines.push('_resource inventory truncated (bounded walk)_');
|
|
530
|
+
}
|
|
531
|
+
return lines.join('\n');
|
|
532
|
+
}
|
|
533
|
+
function readOhpmLocalDeps(manifestAbs, cap = 8) {
|
|
534
|
+
try {
|
|
535
|
+
const text = fs.readFileSync(manifestAbs, 'utf-8');
|
|
536
|
+
let deps;
|
|
537
|
+
try {
|
|
538
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
539
|
+
const parsed = require('jsonc-parser').parse(text);
|
|
540
|
+
deps = parsed?.dependencies;
|
|
541
|
+
}
|
|
542
|
+
catch {
|
|
543
|
+
deps = undefined;
|
|
544
|
+
}
|
|
545
|
+
if (!deps || typeof deps !== 'object')
|
|
546
|
+
return [];
|
|
547
|
+
const out = [];
|
|
548
|
+
for (const [name, ver] of Object.entries(deps)) {
|
|
549
|
+
if (out.length >= cap)
|
|
550
|
+
break;
|
|
551
|
+
if (typeof ver !== 'string')
|
|
552
|
+
continue;
|
|
553
|
+
if (!/^(file:|\.\.?\/)/i.test(ver.trim()))
|
|
554
|
+
continue;
|
|
555
|
+
out.push(`\`${name}\` (${ver.trim()})`);
|
|
556
|
+
}
|
|
557
|
+
return out;
|
|
558
|
+
}
|
|
559
|
+
catch {
|
|
560
|
+
return [];
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
/**
|
|
564
|
+
* Spec 0048 §4 — module roster with oh-package name + local file: deps.
|
|
565
|
+
*/
|
|
566
|
+
function formatHarmonyModuleRoster(projectRoot, modules, maxRows = 24) {
|
|
567
|
+
const rows = [];
|
|
568
|
+
for (const m of modules) {
|
|
569
|
+
if (rows.length >= maxRows)
|
|
570
|
+
break;
|
|
571
|
+
if (m.kind === 'root' && !m.rootPath)
|
|
572
|
+
continue;
|
|
573
|
+
const rootLabel = m.rootPath || '.';
|
|
574
|
+
const ohpmAbs = path.join(projectRoot, m.rootPath || '.', OHPM_MANIFEST);
|
|
575
|
+
const pkg = readOhpmName(ohpmAbs);
|
|
576
|
+
const localDeps = readOhpmLocalDeps(ohpmAbs);
|
|
577
|
+
let line = `- \`${m.name}\` (\`${rootLabel}\`) · ${m.kind} · ${m.fileCount} files`;
|
|
578
|
+
if (pkg)
|
|
579
|
+
line += ` · oh-package \`${pkg}\``;
|
|
580
|
+
if (localDeps.length)
|
|
581
|
+
line += ` · deps: ${localDeps.join(', ')}`;
|
|
582
|
+
rows.push(line);
|
|
583
|
+
}
|
|
584
|
+
if (rows.length === 0)
|
|
585
|
+
return '';
|
|
586
|
+
return ['### Module roster', ...rows].join('\n');
|
|
587
|
+
}
|
|
220
588
|
//# sourceMappingURL=index.js.map
|