unity-agentic-tools 0.2.0 → 0.3.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/README.md +34 -9
- package/dist/cli.js +6646 -1030
- package/dist/doc-indexer-cli.js +286 -52
- package/dist/index.js +4209 -388
- package/native/index.d.ts +98 -2
- package/native/index.js +7 -1
- package/native/unity-file-tools.darwin-arm64.node +0 -0
- package/native/unity-file-tools.darwin-x64.node +0 -0
- package/native/unity-file-tools.linux-x64-gnu.node +0 -0
- package/native/unity-file-tools.win32-x64-msvc.node +0 -0
- package/package.json +1 -1
package/dist/doc-indexer-cli.js
CHANGED
|
@@ -5,15 +5,29 @@ var __getProtoOf = Object.getPrototypeOf;
|
|
|
5
5
|
var __defProp = Object.defineProperty;
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
function __accessProp(key) {
|
|
9
|
+
return this[key];
|
|
10
|
+
}
|
|
11
|
+
var __toESMCache_node;
|
|
12
|
+
var __toESMCache_esm;
|
|
8
13
|
var __toESM = (mod, isNodeMode, target) => {
|
|
14
|
+
var canCache = mod != null && typeof mod === "object";
|
|
15
|
+
if (canCache) {
|
|
16
|
+
var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
|
|
17
|
+
var cached = cache.get(mod);
|
|
18
|
+
if (cached)
|
|
19
|
+
return cached;
|
|
20
|
+
}
|
|
9
21
|
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
10
22
|
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
11
23
|
for (let key of __getOwnPropNames(mod))
|
|
12
24
|
if (!__hasOwnProp.call(to, key))
|
|
13
25
|
__defProp(to, key, {
|
|
14
|
-
get: (
|
|
26
|
+
get: __accessProp.bind(mod, key),
|
|
15
27
|
enumerable: true
|
|
16
28
|
});
|
|
29
|
+
if (canCache)
|
|
30
|
+
cache.set(mod, to);
|
|
17
31
|
return to;
|
|
18
32
|
};
|
|
19
33
|
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
@@ -2271,18 +2285,216 @@ function chunkProse(content, filePath) {
|
|
|
2271
2285
|
}
|
|
2272
2286
|
return chunks;
|
|
2273
2287
|
}
|
|
2274
|
-
function
|
|
2288
|
+
function removeBalancedBlock(html, openPattern, tagName) {
|
|
2289
|
+
let result = html;
|
|
2290
|
+
let match;
|
|
2291
|
+
while ((match = openPattern.exec(result)) !== null) {
|
|
2292
|
+
const startIdx = match.index;
|
|
2293
|
+
let depth = 1;
|
|
2294
|
+
let i = startIdx + match[0].length;
|
|
2295
|
+
const openTag = new RegExp(`<${tagName}[\\s>]`, "gi");
|
|
2296
|
+
const closeTag = `</${tagName}>`;
|
|
2297
|
+
while (depth > 0 && i < result.length) {
|
|
2298
|
+
const nextClose = result.indexOf(closeTag, i);
|
|
2299
|
+
if (nextClose === -1)
|
|
2300
|
+
break;
|
|
2301
|
+
openTag.lastIndex = i;
|
|
2302
|
+
let openMatch;
|
|
2303
|
+
while ((openMatch = openTag.exec(result)) !== null && openMatch.index < nextClose) {
|
|
2304
|
+
depth++;
|
|
2305
|
+
}
|
|
2306
|
+
depth--;
|
|
2307
|
+
i = nextClose + closeTag.length;
|
|
2308
|
+
}
|
|
2309
|
+
result = result.substring(0, startIdx) + result.substring(i);
|
|
2310
|
+
openPattern.lastIndex = 0;
|
|
2311
|
+
}
|
|
2312
|
+
return result;
|
|
2313
|
+
}
|
|
2314
|
+
function decodeEntities(text) {
|
|
2315
|
+
return text.replace(/ /g, " ").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, '"').replace(/'/g, "'");
|
|
2316
|
+
}
|
|
2317
|
+
function htmlToMarkdown(html) {
|
|
2318
|
+
let text = html;
|
|
2319
|
+
text = text.replace(/<script[\s\S]*?<\/script>/gi, "");
|
|
2320
|
+
text = text.replace(/<style[\s\S]*?<\/style>/gi, "");
|
|
2321
|
+
text = text.replace(/<form[\s\S]*?<\/form>/gi, "");
|
|
2322
|
+
const noisePatterns = [
|
|
2323
|
+
[/<div[^>]*class="[^"]*header-wrapper[^"]*"[^>]*>/gi, "div"],
|
|
2324
|
+
[/<div[^>]*id="sidebar"[^>]*>/gi, "div"],
|
|
2325
|
+
[/<div[^>]*class="[^"]*footer-wrapper[^"]*"[^>]*>/gi, "div"],
|
|
2326
|
+
[/<div[^>]*class="[^"]*suggest-wrap[^"]*"[^>]*>/gi, "div"],
|
|
2327
|
+
[/<div[^>]*class="[^"]*suggest-form[^"]*"[^>]*>/gi, "div"],
|
|
2328
|
+
[/<div[^>]*class="[^"]*suggest"[^>]*>/gi, "div"],
|
|
2329
|
+
[/<div[^>]*class="[^"]*scrollToFeedback[^"]*"[^>]*>/gi, "div"],
|
|
2330
|
+
[/<div[^>]*class="[^"]*nextprev[^"]*"[^>]*>/gi, "div"],
|
|
2331
|
+
[/<div[^>]*class="[^"]*toolbar[^"]*"[^>]*>/gi, "div"],
|
|
2332
|
+
[/<nav[^>]*>/gi, "nav"]
|
|
2333
|
+
];
|
|
2334
|
+
for (const [pattern, tag] of noisePatterns) {
|
|
2335
|
+
text = removeBalancedBlock(text, pattern, tag);
|
|
2336
|
+
}
|
|
2337
|
+
const contentWrapMatch = text.match(/<div[^>]*id="content-wrap"[^>]*>([\s\S]*)/i);
|
|
2338
|
+
if (contentWrapMatch) {
|
|
2339
|
+
const afterOpen = contentWrapMatch.index + contentWrapMatch[0].indexOf(">") + 1;
|
|
2340
|
+
let depth = 1;
|
|
2341
|
+
let i = afterOpen;
|
|
2342
|
+
while (depth > 0 && i < text.length) {
|
|
2343
|
+
const nextOpen = text.indexOf("<div", i);
|
|
2344
|
+
const nextClose = text.indexOf("</div>", i);
|
|
2345
|
+
if (nextClose === -1)
|
|
2346
|
+
break;
|
|
2347
|
+
if (nextOpen !== -1 && nextOpen < nextClose) {
|
|
2348
|
+
depth++;
|
|
2349
|
+
i = nextOpen + 4;
|
|
2350
|
+
} else {
|
|
2351
|
+
depth--;
|
|
2352
|
+
if (depth === 0) {
|
|
2353
|
+
text = text.substring(afterOpen, nextClose);
|
|
2354
|
+
} else {
|
|
2355
|
+
i = nextClose + 6;
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
} else {
|
|
2360
|
+
const bodyMatch = text.match(/<body[^>]*>([\s\S]*?)<\/body>/i);
|
|
2361
|
+
if (bodyMatch) {
|
|
2362
|
+
text = bodyMatch[1];
|
|
2363
|
+
}
|
|
2364
|
+
}
|
|
2365
|
+
text = text.replace(/<a[^>]*class=['"][^'"]*switch-link[^'"]*['"][^>]*>[\s\S]*?<\/a>/gi, "");
|
|
2366
|
+
text = text.replace(/<span[^>]*class="[^"]*tooltiptext[^"]*"[^>]*>[\s\S]*?<\/span>/gi, "");
|
|
2367
|
+
text = text.replace(/<span[^>]*class="[^"]*search-words[^"]*"[^>]*>[\s\S]*?<\/span>/gi, "");
|
|
2368
|
+
text = text.replace(/<div[^>]*class="[^"]*breadcrumbs[^"]*"[^>]*>([\s\S]*?)<\/div>/gi, (_match, inner) => {
|
|
2369
|
+
const crumbs = inner.replace(/<a[^>]*>([\s\S]*?)<\/a>/gi, "$1").replace(/<[^>]+>/g, "").split(/\s*[>\/]\s*/).map((c) => c.trim()).filter(Boolean);
|
|
2370
|
+
return crumbs.length > 0 ? `
|
|
2371
|
+
> ${crumbs.join(" > ")}
|
|
2372
|
+
|
|
2373
|
+
` : "";
|
|
2374
|
+
});
|
|
2375
|
+
text = text.replace(/<pre[^>]*>\s*<code[^>]*>([\s\S]*?)<\/code>\s*<\/pre>/gi, (_m, code) => {
|
|
2376
|
+
const decoded = decodeEntities(code.replace(/<[^>]+>/g, "").trim());
|
|
2377
|
+
return `
|
|
2378
|
+
\`\`\`csharp
|
|
2379
|
+
${decoded}
|
|
2380
|
+
\`\`\`
|
|
2381
|
+
`;
|
|
2382
|
+
});
|
|
2383
|
+
text = text.replace(/<pre[^>]*class="[^"]*codeExample[^"]*"[^>]*>([\s\S]*?)<\/pre>/gi, (_m, code) => {
|
|
2384
|
+
const decoded = decodeEntities(code.replace(/<[^>]+>/g, "").trim());
|
|
2385
|
+
return `
|
|
2386
|
+
\`\`\`csharp
|
|
2387
|
+
${decoded}
|
|
2388
|
+
\`\`\`
|
|
2389
|
+
`;
|
|
2390
|
+
});
|
|
2391
|
+
text = text.replace(/<pre[^>]*>([\s\S]*?)<\/pre>/gi, (_m, code) => {
|
|
2392
|
+
const decoded = decodeEntities(code.replace(/<[^>]+>/g, "").trim());
|
|
2393
|
+
return `
|
|
2394
|
+
\`\`\`
|
|
2395
|
+
${decoded}
|
|
2396
|
+
\`\`\`
|
|
2397
|
+
`;
|
|
2398
|
+
});
|
|
2399
|
+
text = text.replace(/<h1[^>]*>([\s\S]*?)<\/h1>/gi, (_m, t) => `
|
|
2400
|
+
# ${t.replace(/<[^>]+>/g, "").trim()}
|
|
2401
|
+
|
|
2402
|
+
`);
|
|
2403
|
+
text = text.replace(/<h2[^>]*>([\s\S]*?)<\/h2>/gi, (_m, t) => `
|
|
2404
|
+
## ${t.replace(/<[^>]+>/g, "").trim()}
|
|
2405
|
+
|
|
2406
|
+
`);
|
|
2407
|
+
text = text.replace(/<h3[^>]*>([\s\S]*?)<\/h3>/gi, (_m, t) => `
|
|
2408
|
+
### ${t.replace(/<[^>]+>/g, "").trim()}
|
|
2409
|
+
|
|
2410
|
+
`);
|
|
2411
|
+
text = text.replace(/<h4[^>]*>([\s\S]*?)<\/h4>/gi, (_m, t) => `
|
|
2412
|
+
#### ${t.replace(/<[^>]+>/g, "").trim()}
|
|
2413
|
+
|
|
2414
|
+
`);
|
|
2415
|
+
text = text.replace(/<table[^>]*>([\s\S]*?)<\/table>/gi, (_m, tableHtml) => {
|
|
2416
|
+
const rows = [];
|
|
2417
|
+
let hasHeader = false;
|
|
2418
|
+
const theadMatch = tableHtml.match(/<thead[^>]*>([\s\S]*?)<\/thead>/i);
|
|
2419
|
+
const headerRowMatch = theadMatch ? theadMatch[1].match(/<tr[^>]*>([\s\S]*?)<\/tr>/i) : tableHtml.match(/<tr[^>]*>([\s\S]*?)<\/tr>/i);
|
|
2420
|
+
if (headerRowMatch) {
|
|
2421
|
+
const thCells = Array.from(headerRowMatch[1].matchAll(/<th[^>]*>([\s\S]*?)<\/th>/gi));
|
|
2422
|
+
if (thCells.length > 0) {
|
|
2423
|
+
hasHeader = true;
|
|
2424
|
+
const headerRow = thCells.map((c) => convertInlineHtml(c[1]).trim()).join(" | ");
|
|
2425
|
+
rows.push(`| ${headerRow} |`);
|
|
2426
|
+
rows.push(`|${thCells.map(() => "---").join("|")}|`);
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
const tbodyMatch = tableHtml.match(/<tbody[^>]*>([\s\S]*?)<\/tbody>/i);
|
|
2430
|
+
const bodyHtml = tbodyMatch ? tbodyMatch[1] : tableHtml;
|
|
2431
|
+
const trMatches = Array.from(bodyHtml.matchAll(/<tr[^>]*>([\s\S]*?)<\/tr>/gi));
|
|
2432
|
+
for (const tr of trMatches) {
|
|
2433
|
+
const tdCells = Array.from(tr[1].matchAll(/<td[^>]*>([\s\S]*?)<\/td>/gi));
|
|
2434
|
+
if (tdCells.length === 0)
|
|
2435
|
+
continue;
|
|
2436
|
+
if (!hasHeader && rows.length === 0) {
|
|
2437
|
+
const thCheck = Array.from(tr[1].matchAll(/<th[^>]*>([\s\S]*?)<\/th>/gi));
|
|
2438
|
+
if (thCheck.length > 0)
|
|
2439
|
+
continue;
|
|
2440
|
+
}
|
|
2441
|
+
const cellValues = tdCells.map((c) => convertInlineHtml(c[1]).trim());
|
|
2442
|
+
rows.push(`| ${cellValues.join(" | ")} |`);
|
|
2443
|
+
if (!hasHeader && rows.length === 1) {
|
|
2444
|
+
rows.unshift(`|${tdCells.map(() => "---").join("|")}|`);
|
|
2445
|
+
hasHeader = true;
|
|
2446
|
+
}
|
|
2447
|
+
}
|
|
2448
|
+
return rows.length > 0 ? `
|
|
2449
|
+
${rows.join(`
|
|
2450
|
+
`)}
|
|
2451
|
+
` : "";
|
|
2452
|
+
});
|
|
2453
|
+
text = text.replace(/<a[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi, (_m, href, linkText) => {
|
|
2454
|
+
const clean = linkText.replace(/<[^>]+>/g, "").trim();
|
|
2455
|
+
if (!clean)
|
|
2456
|
+
return "";
|
|
2457
|
+
return `[${clean}](${href})`;
|
|
2458
|
+
});
|
|
2459
|
+
text = text.replace(/<(?:strong|b)>([\s\S]*?)<\/(?:strong|b)>/gi, "**$1**");
|
|
2460
|
+
text = text.replace(/<(?:em|i)>([\s\S]*?)<\/(?:em|i)>/gi, "*$1*");
|
|
2461
|
+
text = text.replace(/<code>([\s\S]*?)<\/code>/gi, "`$1`");
|
|
2462
|
+
text = text.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, (_m, content) => {
|
|
2463
|
+
const clean = content.replace(/<[^>]+>/g, "").trim();
|
|
2464
|
+
return `
|
|
2465
|
+
- ${clean}`;
|
|
2466
|
+
});
|
|
2467
|
+
text = text.replace(/<\/?(?:ul|ol)[^>]*>/gi, `
|
|
2468
|
+
`);
|
|
2469
|
+
text = text.replace(/<br\s*\/?>/gi, `
|
|
2470
|
+
`);
|
|
2471
|
+
text = text.replace(/<\/p>/gi, `
|
|
2472
|
+
|
|
2473
|
+
`);
|
|
2474
|
+
text = text.replace(/<p[^>]*>/gi, "");
|
|
2475
|
+
text = text.replace(/<hr[^>]*>/gi, `
|
|
2476
|
+
---
|
|
2477
|
+
`);
|
|
2478
|
+
text = text.replace(/<[^>]+>/g, "");
|
|
2479
|
+
text = decodeEntities(text);
|
|
2480
|
+
text = text.replace(/[ \t]+$/gm, "");
|
|
2481
|
+
text = text.replace(/\n{3,}/g, `
|
|
2482
|
+
|
|
2483
|
+
`);
|
|
2484
|
+
text = text.trim();
|
|
2485
|
+
return text;
|
|
2486
|
+
}
|
|
2487
|
+
function convertInlineHtml(html) {
|
|
2275
2488
|
let text = html;
|
|
2276
|
-
text = text.replace(/<
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
text = text.replace(
|
|
2281
|
-
text = text.replace(
|
|
2282
|
-
text = text.replace(
|
|
2283
|
-
text = text.replace(
|
|
2284
|
-
text = text
|
|
2285
|
-
text = text.replace(/\s+/g, " ").trim();
|
|
2489
|
+
text = text.replace(/<a[^>]*href="([^"]*)"[^>]*>([\s\S]*?)<\/a>/gi, (_m, href, linkText) => {
|
|
2490
|
+
const clean = linkText.replace(/<[^>]+>/g, "").trim();
|
|
2491
|
+
return clean ? `[${clean}](${href})` : "";
|
|
2492
|
+
});
|
|
2493
|
+
text = text.replace(/<(?:strong|b)>([\s\S]*?)<\/(?:strong|b)>/gi, "**$1**");
|
|
2494
|
+
text = text.replace(/<(?:em|i)>([\s\S]*?)<\/(?:em|i)>/gi, "*$1*");
|
|
2495
|
+
text = text.replace(/<code>([\s\S]*?)<\/code>/gi, "`$1`");
|
|
2496
|
+
text = text.replace(/<[^>]+>/g, "");
|
|
2497
|
+
text = decodeEntities(text);
|
|
2286
2498
|
return text;
|
|
2287
2499
|
}
|
|
2288
2500
|
function walkDirectory(dir) {
|
|
@@ -2347,7 +2559,7 @@ function indexMarkdownFile(filePath, storage) {
|
|
|
2347
2559
|
function indexHtmlFile(filePath, storage) {
|
|
2348
2560
|
const startTime = Date.now();
|
|
2349
2561
|
const html = import_fs.readFileSync(filePath, "utf-8");
|
|
2350
|
-
const text =
|
|
2562
|
+
const text = htmlToMarkdown(html);
|
|
2351
2563
|
const chunks = chunkProse(text, filePath);
|
|
2352
2564
|
let embeddingsGenerated = 0;
|
|
2353
2565
|
if (storage) {
|
|
@@ -2375,7 +2587,7 @@ async function indexDocsDirectory(dirPath, extensions = [".md", ".txt", ".html"]
|
|
|
2375
2587
|
continue;
|
|
2376
2588
|
const content = import_fs.readFileSync(fullPath, "utf-8");
|
|
2377
2589
|
if (ext === ".html") {
|
|
2378
|
-
const text =
|
|
2590
|
+
const text = htmlToMarkdown(content);
|
|
2379
2591
|
allChunks.push(...chunkProse(text, fullPath));
|
|
2380
2592
|
} else {
|
|
2381
2593
|
allChunks.push(...extractCodeBlocks(content));
|
|
@@ -2483,7 +2695,7 @@ async function indexSource(source, storage) {
|
|
|
2483
2695
|
const ext = file.fullPath.substring(file.fullPath.lastIndexOf("."));
|
|
2484
2696
|
const fileChunks = [];
|
|
2485
2697
|
if (ext === ".html") {
|
|
2486
|
-
const text =
|
|
2698
|
+
const text = htmlToMarkdown(content);
|
|
2487
2699
|
fileChunks.push(...chunkProse(text, file.fullPath));
|
|
2488
2700
|
} else {
|
|
2489
2701
|
fileChunks.push(...extractCodeBlocks(content, file.fullPath));
|
|
@@ -2617,28 +2829,37 @@ class DocStorage {
|
|
|
2617
2829
|
await this.init();
|
|
2618
2830
|
const lowerQuery = query.toLowerCase();
|
|
2619
2831
|
const queryTerms = lowerQuery.split(/\s+/).filter((t) => t.length > 0);
|
|
2832
|
+
if (queryTerms.length === 0)
|
|
2833
|
+
return [];
|
|
2834
|
+
const minTermRatio = 0.5;
|
|
2620
2835
|
const results = [];
|
|
2621
2836
|
for (const [id, chunk] of this.chunks) {
|
|
2622
2837
|
const lowerContent = chunk.content.toLowerCase();
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2838
|
+
let matchedTerms = 0;
|
|
2839
|
+
let termHits = 0;
|
|
2840
|
+
for (const term of queryTerms) {
|
|
2841
|
+
let found = false;
|
|
2842
|
+
let idx = 0;
|
|
2843
|
+
while ((idx = lowerContent.indexOf(term, idx)) !== -1) {
|
|
2844
|
+
termHits++;
|
|
2845
|
+
found = true;
|
|
2846
|
+
idx += term.length;
|
|
2847
|
+
}
|
|
2848
|
+
if (found)
|
|
2849
|
+
matchedTerms++;
|
|
2850
|
+
}
|
|
2851
|
+
const termRatio = matchedTerms / queryTerms.length;
|
|
2852
|
+
if (termRatio < minTermRatio)
|
|
2853
|
+
continue;
|
|
2854
|
+
const wordCount = lowerContent.split(/\s+/).length;
|
|
2855
|
+
const score = termHits / Math.max(wordCount, 1);
|
|
2856
|
+
if (score > 0) {
|
|
2857
|
+
results.push({
|
|
2858
|
+
id,
|
|
2859
|
+
content: chunk.content,
|
|
2860
|
+
score,
|
|
2861
|
+
metadata: chunk.metadata
|
|
2862
|
+
});
|
|
2642
2863
|
}
|
|
2643
2864
|
}
|
|
2644
2865
|
results.sort((a, b) => b.score - a.score);
|
|
@@ -2868,6 +3089,20 @@ function read_unity_version(projectRoot) {
|
|
|
2868
3089
|
// src/cli.ts
|
|
2869
3090
|
var program2 = new Command;
|
|
2870
3091
|
program2.name("unity-doc-indexer").description("Fast Unity documentation indexer with local embeddings").version("1.0.0").option("--project-root <path>", "Unity project root (auto-detected if omitted)").option("--storage-path <path>", "Index storage file path (auto-resolved if omitted)");
|
|
3092
|
+
function extract_doc_title(filePath) {
|
|
3093
|
+
if (!filePath)
|
|
3094
|
+
return "Unknown";
|
|
3095
|
+
const filename = filePath.split("/").pop()?.replace(/\.[^.]+$/, "") ?? "Unknown";
|
|
3096
|
+
return filename.replace(/^class-/, "").replace(/-/g, ".");
|
|
3097
|
+
}
|
|
3098
|
+
function extract_relative_source(filePath) {
|
|
3099
|
+
if (!filePath)
|
|
3100
|
+
return;
|
|
3101
|
+
const match = filePath.match(/((?:ScriptReference|Manual|Documentation)\/[^/]*\.html?)$/i);
|
|
3102
|
+
if (match)
|
|
3103
|
+
return match[1];
|
|
3104
|
+
return filePath.split("/").pop();
|
|
3105
|
+
}
|
|
2871
3106
|
function get_storage_path(opts) {
|
|
2872
3107
|
if (opts.storagePath)
|
|
2873
3108
|
return opts.storagePath;
|
|
@@ -2892,7 +3127,7 @@ async function auto_index(storage, projectRoot) {
|
|
|
2892
3127
|
}
|
|
2893
3128
|
return reindexed;
|
|
2894
3129
|
}
|
|
2895
|
-
program2.command("search <query>").description("Search documentation (auto-discovers and indexes on first use)").option("-
|
|
3130
|
+
program2.command("search <query>").description("Search documentation (auto-discovers and indexes on first use)").option("-j, --json", "Output as JSON").action(async (query, options) => {
|
|
2896
3131
|
const globalOpts = program2.opts();
|
|
2897
3132
|
const storagePath = get_storage_path(globalOpts);
|
|
2898
3133
|
const projectRoot = globalOpts.projectRoot || find_project_root() || null;
|
|
@@ -2915,27 +3150,26 @@ program2.command("search <query>").description("Search documentation (auto-disco
|
|
|
2915
3150
|
keyword_weight: 0.4
|
|
2916
3151
|
});
|
|
2917
3152
|
if (options.json) {
|
|
2918
|
-
|
|
2919
|
-
console.log(JSON.stringify(output, null, 2));
|
|
3153
|
+
console.log(JSON.stringify(results, null, 2));
|
|
2920
3154
|
return;
|
|
2921
3155
|
}
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
3156
|
+
const count = results.results.length;
|
|
3157
|
+
console.log(`# Unity Docs: "${query}" (${count} result${count !== 1 ? "s" : ""})
|
|
3158
|
+
`);
|
|
3159
|
+
for (const result of results.results) {
|
|
3160
|
+
const meta = result.metadata;
|
|
3161
|
+
const title = meta?.section || meta?.unity_class || extract_doc_title(meta?.file_path);
|
|
3162
|
+
const source = extract_relative_source(meta?.file_path);
|
|
3163
|
+
console.log(`## ${title}
|
|
3164
|
+
`);
|
|
3165
|
+
console.log(result.content);
|
|
3166
|
+
if (source) {
|
|
3167
|
+
console.log(`
|
|
3168
|
+
*Source: ${source}*`);
|
|
2926
3169
|
}
|
|
2927
|
-
return;
|
|
2928
|
-
}
|
|
2929
|
-
console.log(`Found ${results.results.length} results in ${results.elapsed_ms}ms`);
|
|
2930
|
-
console.log(`Semantic: ${results.semantic_count}, Keyword: ${results.keyword_count}`);
|
|
2931
|
-
for (let i = 0;i < results.results.length; i++) {
|
|
2932
|
-
const result = results.results[i];
|
|
2933
|
-
const title = result.metadata?.section || result.metadata?.unity_class || result.metadata?.file_path;
|
|
2934
|
-
const content = options.summarize ? result.content.slice(0, 200) + "..." : result.content;
|
|
2935
3170
|
console.log(`
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
console.log(`Score: ${result.score.toFixed(4)}`);
|
|
3171
|
+
---
|
|
3172
|
+
`);
|
|
2939
3173
|
}
|
|
2940
3174
|
});
|
|
2941
3175
|
program2.command("index [path]").description("Index documentation (auto-discovers sources if no path given)").action(async (path) => {
|