@dotdotgod/hermes 0.6.0 → 0.6.2

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/mcp/cli.mjs CHANGED
@@ -1,5 +1,759 @@
1
1
  #!/usr/bin/env node
2
2
  // Generated by scripts/build-adapter-runtime.mjs; optional embedding runtime is packaged separately.
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __esm = (fn, res) => function __init() {
10
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
11
+ };
12
+ var __commonJS = (cb, mod) => function __require() {
13
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
14
+ };
15
+ var __export = (target, all) => {
16
+ for (var name in all)
17
+ __defProp(target, name, { get: all[name], enumerable: true });
18
+ };
19
+ var __copyProps = (to, from, except, desc) => {
20
+ if (from && typeof from === "object" || typeof from === "function") {
21
+ for (let key of __getOwnPropNames(from))
22
+ if (!__hasOwnProp.call(to, key) && key !== except)
23
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
24
+ }
25
+ return to;
26
+ };
27
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
28
+ // If the importer is in node compatibility mode or this is not an ESM
29
+ // file that has been converted to a CommonJS file using a Babel-
30
+ // compatible transform (i.e. "__esModule" has not been set), then set
31
+ // "default" to the CommonJS "module.exports" for node compatibility.
32
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
33
+ mod
34
+ ));
35
+
36
+ // node_modules/.pnpm/ignore@7.0.5/node_modules/ignore/index.js
37
+ var require_ignore = __commonJS({
38
+ "node_modules/.pnpm/ignore@7.0.5/node_modules/ignore/index.js"(exports, module) {
39
+ function makeArray(subject) {
40
+ return Array.isArray(subject) ? subject : [subject];
41
+ }
42
+ var UNDEFINED = void 0;
43
+ var EMPTY = "";
44
+ var SPACE = " ";
45
+ var ESCAPE = "\\";
46
+ var REGEX_TEST_BLANK_LINE = /^\s+$/;
47
+ var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
48
+ var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
49
+ var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
50
+ var REGEX_SPLITALL_CRLF = /\r?\n/g;
51
+ var REGEX_TEST_INVALID_PATH = /^\.{0,2}\/|^\.{1,2}$/;
52
+ var REGEX_TEST_TRAILING_SLASH = /\/$/;
53
+ var SLASH = "/";
54
+ var TMP_KEY_IGNORE = "node-ignore";
55
+ if (typeof Symbol !== "undefined") {
56
+ TMP_KEY_IGNORE = /* @__PURE__ */ Symbol.for("node-ignore");
57
+ }
58
+ var KEY_IGNORE = TMP_KEY_IGNORE;
59
+ var define = (object, key, value) => {
60
+ Object.defineProperty(object, key, { value });
61
+ return value;
62
+ };
63
+ var REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
64
+ var RETURN_FALSE = () => false;
65
+ var sanitizeRange = (range) => range.replace(
66
+ REGEX_REGEXP_RANGE,
67
+ (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) ? match : EMPTY
68
+ );
69
+ var cleanRangeBackSlash = (slashes) => {
70
+ const { length } = slashes;
71
+ return slashes.slice(0, length - length % 2);
72
+ };
73
+ var REPLACERS = [
74
+ [
75
+ // Remove BOM
76
+ // TODO:
77
+ // Other similar zero-width characters?
78
+ /^\uFEFF/,
79
+ () => EMPTY
80
+ ],
81
+ // > Trailing spaces are ignored unless they are quoted with backslash ("\")
82
+ [
83
+ // (a\ ) -> (a )
84
+ // (a ) -> (a)
85
+ // (a ) -> (a)
86
+ // (a \ ) -> (a )
87
+ /((?:\\\\)*?)(\\?\s+)$/,
88
+ (_, m1, m2) => m1 + (m2.indexOf("\\") === 0 ? SPACE : EMPTY)
89
+ ],
90
+ // Replace (\ ) with ' '
91
+ // (\ ) -> ' '
92
+ // (\\ ) -> '\\ '
93
+ // (\\\ ) -> '\\ '
94
+ [
95
+ /(\\+?)\s/g,
96
+ (_, m1) => {
97
+ const { length } = m1;
98
+ return m1.slice(0, length - length % 2) + SPACE;
99
+ }
100
+ ],
101
+ // Escape metacharacters
102
+ // which is written down by users but means special for regular expressions.
103
+ // > There are 12 characters with special meanings:
104
+ // > - the backslash \,
105
+ // > - the caret ^,
106
+ // > - the dollar sign $,
107
+ // > - the period or dot .,
108
+ // > - the vertical bar or pipe symbol |,
109
+ // > - the question mark ?,
110
+ // > - the asterisk or star *,
111
+ // > - the plus sign +,
112
+ // > - the opening parenthesis (,
113
+ // > - the closing parenthesis ),
114
+ // > - and the opening square bracket [,
115
+ // > - the opening curly brace {,
116
+ // > These special characters are often called "metacharacters".
117
+ [
118
+ /[\\$.|*+(){^]/g,
119
+ (match) => `\\${match}`
120
+ ],
121
+ [
122
+ // > a question mark (?) matches a single character
123
+ /(?!\\)\?/g,
124
+ () => "[^/]"
125
+ ],
126
+ // leading slash
127
+ [
128
+ // > A leading slash matches the beginning of the pathname.
129
+ // > For example, "/*.c" matches "cat-file.c" but not "mozilla-sha1/sha1.c".
130
+ // A leading slash matches the beginning of the pathname
131
+ /^\//,
132
+ () => "^"
133
+ ],
134
+ // replace special metacharacter slash after the leading slash
135
+ [
136
+ /\//g,
137
+ () => "\\/"
138
+ ],
139
+ [
140
+ // > A leading "**" followed by a slash means match in all directories.
141
+ // > For example, "**/foo" matches file or directory "foo" anywhere,
142
+ // > the same as pattern "foo".
143
+ // > "**/foo/bar" matches file or directory "bar" anywhere that is directly
144
+ // > under directory "foo".
145
+ // Notice that the '*'s have been replaced as '\\*'
146
+ /^\^*\\\*\\\*\\\//,
147
+ // '**/foo' <-> 'foo'
148
+ () => "^(?:.*\\/)?"
149
+ ],
150
+ // starting
151
+ [
152
+ // there will be no leading '/'
153
+ // (which has been replaced by section "leading slash")
154
+ // If starts with '**', adding a '^' to the regular expression also works
155
+ /^(?=[^^])/,
156
+ function startingReplacer() {
157
+ return !/\/(?!$)/.test(this) ? "(?:^|\\/)" : "^";
158
+ }
159
+ ],
160
+ // two globstars
161
+ [
162
+ // Use lookahead assertions so that we could match more than one `'/**'`
163
+ /\\\/\\\*\\\*(?=\\\/|$)/g,
164
+ // Zero, one or several directories
165
+ // should not use '*', or it will be replaced by the next replacer
166
+ // Check if it is not the last `'/**'`
167
+ (_, index, str) => index + 6 < str.length ? "(?:\\/[^\\/]+)*" : "\\/.+"
168
+ ],
169
+ // normal intermediate wildcards
170
+ [
171
+ // Never replace escaped '*'
172
+ // ignore rule '\*' will match the path '*'
173
+ // 'abc.*/' -> go
174
+ // 'abc.*' -> skip this rule,
175
+ // coz trailing single wildcard will be handed by [trailing wildcard]
176
+ /(^|[^\\]+)(\\\*)+(?=.+)/g,
177
+ // '*.js' matches '.js'
178
+ // '*.js' doesn't match 'abc'
179
+ (_, p1, p2) => {
180
+ const unescaped = p2.replace(/\\\*/g, "[^\\/]*");
181
+ return p1 + unescaped;
182
+ }
183
+ ],
184
+ [
185
+ // unescape, revert step 3 except for back slash
186
+ // For example, if a user escape a '\\*',
187
+ // after step 3, the result will be '\\\\\\*'
188
+ /\\\\\\(?=[$.|*+(){^])/g,
189
+ () => ESCAPE
190
+ ],
191
+ [
192
+ // '\\\\' -> '\\'
193
+ /\\\\/g,
194
+ () => ESCAPE
195
+ ],
196
+ [
197
+ // > The range notation, e.g. [a-zA-Z],
198
+ // > can be used to match one of the characters in a range.
199
+ // `\` is escaped by step 3
200
+ /(\\)?\[([^\]/]*?)(\\*)($|\])/g,
201
+ (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}` : close === "]" ? endEscape.length % 2 === 0 ? `[${sanitizeRange(range)}${endEscape}]` : "[]" : "[]"
202
+ ],
203
+ // ending
204
+ [
205
+ // 'js' will not match 'js.'
206
+ // 'ab' will not match 'abc'
207
+ /(?:[^*])$/,
208
+ // WTF!
209
+ // https://git-scm.com/docs/gitignore
210
+ // changes in [2.22.1](https://git-scm.com/docs/gitignore/2.22.1)
211
+ // which re-fixes #24, #38
212
+ // > If there is a separator at the end of the pattern then the pattern
213
+ // > will only match directories, otherwise the pattern can match both
214
+ // > files and directories.
215
+ // 'js*' will not match 'a.js'
216
+ // 'js/' will not match 'a.js'
217
+ // 'js' will match 'a.js' and 'a.js/'
218
+ (match) => /\/$/.test(match) ? `${match}$` : `${match}(?=$|\\/$)`
219
+ ]
220
+ ];
221
+ var REGEX_REPLACE_TRAILING_WILDCARD = /(^|\\\/)?\\\*$/;
222
+ var MODE_IGNORE = "regex";
223
+ var MODE_CHECK_IGNORE = "checkRegex";
224
+ var UNDERSCORE = "_";
225
+ var TRAILING_WILD_CARD_REPLACERS = {
226
+ [MODE_IGNORE](_, p1) {
227
+ const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
228
+ return `${prefix}(?=$|\\/$)`;
229
+ },
230
+ [MODE_CHECK_IGNORE](_, p1) {
231
+ const prefix = p1 ? `${p1}[^/]*` : "[^/]*";
232
+ return `${prefix}(?=$|\\/$)`;
233
+ }
234
+ };
235
+ var makeRegexPrefix = (pattern) => REPLACERS.reduce(
236
+ (prev, [matcher, replacer]) => prev.replace(matcher, replacer.bind(pattern)),
237
+ pattern
238
+ );
239
+ var isString = (subject) => typeof subject === "string";
240
+ var checkPattern = (pattern) => pattern && isString(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0;
241
+ var splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF).filter(Boolean);
242
+ var IgnoreRule = class {
243
+ constructor(pattern, mark, body, ignoreCase, negative, prefix) {
244
+ this.pattern = pattern;
245
+ this.mark = mark;
246
+ this.negative = negative;
247
+ define(this, "body", body);
248
+ define(this, "ignoreCase", ignoreCase);
249
+ define(this, "regexPrefix", prefix);
250
+ }
251
+ get regex() {
252
+ const key = UNDERSCORE + MODE_IGNORE;
253
+ if (this[key]) {
254
+ return this[key];
255
+ }
256
+ return this._make(MODE_IGNORE, key);
257
+ }
258
+ get checkRegex() {
259
+ const key = UNDERSCORE + MODE_CHECK_IGNORE;
260
+ if (this[key]) {
261
+ return this[key];
262
+ }
263
+ return this._make(MODE_CHECK_IGNORE, key);
264
+ }
265
+ _make(mode, key) {
266
+ const str = this.regexPrefix.replace(
267
+ REGEX_REPLACE_TRAILING_WILDCARD,
268
+ // It does not need to bind pattern
269
+ TRAILING_WILD_CARD_REPLACERS[mode]
270
+ );
271
+ const regex = this.ignoreCase ? new RegExp(str, "i") : new RegExp(str);
272
+ return define(this, key, regex);
273
+ }
274
+ };
275
+ var createRule = ({
276
+ pattern,
277
+ mark
278
+ }, ignoreCase) => {
279
+ let negative = false;
280
+ let body = pattern;
281
+ if (body.indexOf("!") === 0) {
282
+ negative = true;
283
+ body = body.substr(1);
284
+ }
285
+ body = body.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, "!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, "#");
286
+ const regexPrefix = makeRegexPrefix(body);
287
+ return new IgnoreRule(
288
+ pattern,
289
+ mark,
290
+ body,
291
+ ignoreCase,
292
+ negative,
293
+ regexPrefix
294
+ );
295
+ };
296
+ var RuleManager = class {
297
+ constructor(ignoreCase) {
298
+ this._ignoreCase = ignoreCase;
299
+ this._rules = [];
300
+ }
301
+ _add(pattern) {
302
+ if (pattern && pattern[KEY_IGNORE]) {
303
+ this._rules = this._rules.concat(pattern._rules._rules);
304
+ this._added = true;
305
+ return;
306
+ }
307
+ if (isString(pattern)) {
308
+ pattern = {
309
+ pattern
310
+ };
311
+ }
312
+ if (checkPattern(pattern.pattern)) {
313
+ const rule = createRule(pattern, this._ignoreCase);
314
+ this._added = true;
315
+ this._rules.push(rule);
316
+ }
317
+ }
318
+ // @param {Array<string> | string | Ignore} pattern
319
+ add(pattern) {
320
+ this._added = false;
321
+ makeArray(
322
+ isString(pattern) ? splitPattern(pattern) : pattern
323
+ ).forEach(this._add, this);
324
+ return this._added;
325
+ }
326
+ // Test one single path without recursively checking parent directories
327
+ //
328
+ // - checkUnignored `boolean` whether should check if the path is unignored,
329
+ // setting `checkUnignored` to `false` could reduce additional
330
+ // path matching.
331
+ // - check `string` either `MODE_IGNORE` or `MODE_CHECK_IGNORE`
332
+ // @returns {TestResult} true if a file is ignored
333
+ test(path, checkUnignored, mode) {
334
+ let ignored2 = false;
335
+ let unignored = false;
336
+ let matchedRule;
337
+ this._rules.forEach((rule) => {
338
+ const { negative } = rule;
339
+ if (unignored === negative && ignored2 !== unignored || negative && !ignored2 && !unignored && !checkUnignored) {
340
+ return;
341
+ }
342
+ const matched = rule[mode].test(path);
343
+ if (!matched) {
344
+ return;
345
+ }
346
+ ignored2 = !negative;
347
+ unignored = negative;
348
+ matchedRule = negative ? UNDEFINED : rule;
349
+ });
350
+ const ret = {
351
+ ignored: ignored2,
352
+ unignored
353
+ };
354
+ if (matchedRule) {
355
+ ret.rule = matchedRule;
356
+ }
357
+ return ret;
358
+ }
359
+ };
360
+ var throwError = (message, Ctor) => {
361
+ throw new Ctor(message);
362
+ };
363
+ var checkPath = (path, originalPath, doThrow) => {
364
+ if (!isString(path)) {
365
+ return doThrow(
366
+ `path must be a string, but got \`${originalPath}\``,
367
+ TypeError
368
+ );
369
+ }
370
+ if (!path) {
371
+ return doThrow(`path must not be empty`, TypeError);
372
+ }
373
+ if (checkPath.isNotRelative(path)) {
374
+ const r = "`path.relative()`d";
375
+ return doThrow(
376
+ `path should be a ${r} string, but got "${originalPath}"`,
377
+ RangeError
378
+ );
379
+ }
380
+ return true;
381
+ };
382
+ var isNotRelative = (path) => REGEX_TEST_INVALID_PATH.test(path);
383
+ checkPath.isNotRelative = isNotRelative;
384
+ checkPath.convert = (p) => p;
385
+ var Ignore = class {
386
+ constructor({
387
+ ignorecase = true,
388
+ ignoreCase = ignorecase,
389
+ allowRelativePaths = false
390
+ } = {}) {
391
+ define(this, KEY_IGNORE, true);
392
+ this._rules = new RuleManager(ignoreCase);
393
+ this._strictPathCheck = !allowRelativePaths;
394
+ this._initCache();
395
+ }
396
+ _initCache() {
397
+ this._ignoreCache = /* @__PURE__ */ Object.create(null);
398
+ this._testCache = /* @__PURE__ */ Object.create(null);
399
+ }
400
+ add(pattern) {
401
+ if (this._rules.add(pattern)) {
402
+ this._initCache();
403
+ }
404
+ return this;
405
+ }
406
+ // legacy
407
+ addPattern(pattern) {
408
+ return this.add(pattern);
409
+ }
410
+ // @returns {TestResult}
411
+ _test(originalPath, cache, checkUnignored, slices) {
412
+ const path = originalPath && checkPath.convert(originalPath);
413
+ checkPath(
414
+ path,
415
+ originalPath,
416
+ this._strictPathCheck ? throwError : RETURN_FALSE
417
+ );
418
+ return this._t(path, cache, checkUnignored, slices);
419
+ }
420
+ checkIgnore(path) {
421
+ if (!REGEX_TEST_TRAILING_SLASH.test(path)) {
422
+ return this.test(path);
423
+ }
424
+ const slices = path.split(SLASH).filter(Boolean);
425
+ slices.pop();
426
+ if (slices.length) {
427
+ const parent = this._t(
428
+ slices.join(SLASH) + SLASH,
429
+ this._testCache,
430
+ true,
431
+ slices
432
+ );
433
+ if (parent.ignored) {
434
+ return parent;
435
+ }
436
+ }
437
+ return this._rules.test(path, false, MODE_CHECK_IGNORE);
438
+ }
439
+ _t(path, cache, checkUnignored, slices) {
440
+ if (path in cache) {
441
+ return cache[path];
442
+ }
443
+ if (!slices) {
444
+ slices = path.split(SLASH).filter(Boolean);
445
+ }
446
+ slices.pop();
447
+ if (!slices.length) {
448
+ return cache[path] = this._rules.test(path, checkUnignored, MODE_IGNORE);
449
+ }
450
+ const parent = this._t(
451
+ slices.join(SLASH) + SLASH,
452
+ cache,
453
+ checkUnignored,
454
+ slices
455
+ );
456
+ return cache[path] = parent.ignored ? parent : this._rules.test(path, checkUnignored, MODE_IGNORE);
457
+ }
458
+ ignores(path) {
459
+ return this._test(path, this._ignoreCache, false).ignored;
460
+ }
461
+ createFilter() {
462
+ return (path) => !this.ignores(path);
463
+ }
464
+ filter(paths) {
465
+ return makeArray(paths).filter(this.createFilter());
466
+ }
467
+ // @returns {TestResult}
468
+ test(path) {
469
+ return this._test(path, this._testCache, true);
470
+ }
471
+ };
472
+ var factory = (options) => new Ignore(options);
473
+ var isPathValid = (path) => checkPath(path && checkPath.convert(path), path, RETURN_FALSE);
474
+ var setupWindows = () => {
475
+ const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
476
+ checkPath.convert = makePosix;
477
+ const REGEX_TEST_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
478
+ checkPath.isNotRelative = (path) => REGEX_TEST_WINDOWS_PATH_ABSOLUTE.test(path) || isNotRelative(path);
479
+ };
480
+ if (
481
+ // Detect `process` so that it can run in browsers.
482
+ typeof process !== "undefined" && process.platform === "win32"
483
+ ) {
484
+ setupWindows();
485
+ }
486
+ module.exports = factory;
487
+ factory.default = factory;
488
+ module.exports.isPathValid = isPathValid;
489
+ define(module.exports, /* @__PURE__ */ Symbol.for("setupWindows"), setupWindows);
490
+ }
491
+ });
492
+
493
+ // packages/cli/src/symbols/languages.mjs
494
+ import { extname as extname4 } from "node:path";
495
+ function languageForPath(path) {
496
+ return Object.entries(LANGUAGE_EXTENSIONS).find(([, extensions]) => extensions.includes(extname4(path)))?.[0] ?? null;
497
+ }
498
+ var LANGUAGE_EXTENSIONS, SOURCE_EXTENSIONS, EXTRACTOR_ID, DECLARATIONS, COMMENT_TYPES;
499
+ var init_languages = __esm({
500
+ "packages/cli/src/symbols/languages.mjs"() {
501
+ LANGUAGE_EXTENSIONS = {
502
+ javascript: [".js", ".jsx", ".mjs", ".cjs"],
503
+ typescript: [".ts"],
504
+ tsx: [".tsx"],
505
+ python: [".py", ".pyw"],
506
+ go: [".go"],
507
+ rust: [".rs"],
508
+ java: [".java"],
509
+ ruby: [".rb"],
510
+ php: [".php"],
511
+ "c-sharp": [".cs"],
512
+ cpp: [".cpp", ".cc", ".cxx", ".hpp"],
513
+ bash: [".sh", ".bash"],
514
+ powershell: [".ps1"]
515
+ };
516
+ SOURCE_EXTENSIONS = /* @__PURE__ */ new Set([
517
+ ...Object.values(LANGUAGE_EXTENSIONS).flat(),
518
+ ".c",
519
+ ".h",
520
+ ".kt",
521
+ ".kts",
522
+ ".swift",
523
+ ".scala",
524
+ ".clj",
525
+ ".cljs",
526
+ ".ex",
527
+ ".exs",
528
+ ".erl",
529
+ ".hrl",
530
+ ".lua",
531
+ ".pl",
532
+ ".pm",
533
+ ".r",
534
+ ".R",
535
+ ".sql",
536
+ ".m",
537
+ ".mm",
538
+ ".zsh",
539
+ ".fish"
540
+ ]);
541
+ EXTRACTOR_ID = "outline-v3:vscode-tree-sitter-wasm-0.3.1";
542
+ DECLARATIONS = {
543
+ function_declaration: "function",
544
+ generator_function_declaration: "function",
545
+ function_definition: "function",
546
+ function_item: "function",
547
+ function_signature_item: "function",
548
+ function_signature: "function",
549
+ method_definition: "method",
550
+ method_declaration: "method",
551
+ method_signature: "method",
552
+ method: "method",
553
+ singleton_method: "method",
554
+ constructor_declaration: "constructor",
555
+ destructor_declaration: "destructor",
556
+ class_declaration: "class",
557
+ class_definition: "class",
558
+ class: "class",
559
+ class_specifier: "class",
560
+ struct_specifier: "struct",
561
+ interface_declaration: "interface",
562
+ interface_definition: "interface",
563
+ trait_item: "trait",
564
+ struct_item: "struct",
565
+ enum_item: "enum",
566
+ enum_declaration: "enum",
567
+ record_declaration: "class",
568
+ namespace_definition: "namespace",
569
+ mod_item: "module",
570
+ module: "module",
571
+ function_statement: "function"
572
+ };
573
+ COMMENT_TYPES = /* @__PURE__ */ new Set(["comment", "line_comment", "block_comment"]);
574
+ }
575
+ });
576
+
577
+ // packages/cli/src/symbols/runtime.mjs
578
+ import { createRequire } from "node:module";
579
+ import { existsSync as existsSync9, readFileSync as readFileSync9 } from "node:fs";
580
+ import { dirname as dirname5, join as join8 } from "node:path";
581
+ import { fileURLToPath } from "node:url";
582
+ import { createHash as createHash4 } from "node:crypto";
583
+ function parserEntry() {
584
+ const bundled = fileURLToPath(new URL("./parsers/tree-sitter.cjs", import.meta.url));
585
+ return existsSync9(bundled) ? bundled : require2.resolve("@vscode/tree-sitter-wasm");
586
+ }
587
+ function parserIdentity() {
588
+ if (!identity) {
589
+ const entry = parserEntry();
590
+ const hash2 = createHash4("sha256").update(readFileSync9(entry));
591
+ for (const name of ["tree-sitter.wasm", ...Object.keys(LANGUAGE_EXTENSIONS).map((language) => `tree-sitter-${language}.wasm`)]) hash2.update(name).update(readFileSync9(join8(dirname5(entry), name)));
592
+ identity = hash2.digest("hex");
593
+ }
594
+ return identity;
595
+ }
596
+ function loadParserApi() {
597
+ return require2(parserEntry());
598
+ }
599
+ var require2, identity;
600
+ var init_runtime = __esm({
601
+ "packages/cli/src/symbols/runtime.mjs"() {
602
+ init_languages();
603
+ require2 = createRequire(import.meta.url);
604
+ }
605
+ });
606
+
607
+ // packages/cli/src/symbols/parser.mjs
608
+ var parser_exports = {};
609
+ __export(parser_exports, {
610
+ extractSymbols: () => extractSymbols
611
+ });
612
+ import { dirname as dirname6, join as join10 } from "node:path";
613
+ import { createHash as createHash5 } from "node:crypto";
614
+ async function parserFor(language) {
615
+ if (!runtime) runtime = (async () => {
616
+ const entry = parserEntry();
617
+ const api2 = loadParserApi();
618
+ await api2.Parser.init();
619
+ return { api: api2, directory: dirname6(entry) };
620
+ })();
621
+ const { api, directory } = await runtime;
622
+ if (!languages.has(language)) languages.set(language, api.Language.load(join10(directory, `tree-sitter-${language}.wasm`)));
623
+ const loaded = await languages.get(language);
624
+ const parser = new api.Parser();
625
+ parser.setLanguage(loaded);
626
+ return parser;
627
+ }
628
+ function declaredName(node2) {
629
+ const name = field(node2, "name") ?? node2.namedChildren.find((child) => child.type === "function_name");
630
+ if (name) return name.text;
631
+ const declarator = field(node2, "declarator");
632
+ if (declarator) return declaredName(declarator) || (["identifier", "field_identifier"].includes(declarator.type) ? declarator.text : "");
633
+ return "";
634
+ }
635
+ function declarationHeader(node2, source, end) {
636
+ const cuts = [];
637
+ function visit(child) {
638
+ const body = field(child, "body");
639
+ if (body && body.startIndex < end && body.endIndex <= end) {
640
+ cuts.push([body.startIndex, body.endIndex]);
641
+ return;
642
+ }
643
+ for (const nested of child.namedChildren) if (nested.startIndex < end) visit(nested);
644
+ }
645
+ visit(node2);
646
+ let header = source.slice(node2.startIndex, end);
647
+ for (const [start, finish] of cuts.sort((a, b) => b[0] - a[0])) header = header.slice(0, start - node2.startIndex) + "\u2026" + header.slice(finish - node2.startIndex);
648
+ return header.trim();
649
+ }
650
+ function documentation(node2, body, language, source) {
651
+ if (language === "python") {
652
+ const first = body?.namedChildren[0];
653
+ const string = first?.type === "expression_statement" ? first.namedChildren[0] : null;
654
+ const literals = string?.type === "concatenated_string" ? string.namedChildren : string ? [string] : [];
655
+ const valid = literals.length && literals.every((literal) => literal.type === "string" && /^[rRuU]*['"]/.test(literal.text));
656
+ return valid ? { text: string.text, startLine: string.startPosition.row + 1 } : { text: "" };
657
+ }
658
+ let anchor = node2;
659
+ while (anchor.parent && (["export_statement", "decorated_definition"].includes(anchor.parent.type) || ["lexical_declaration", "variable_declaration"].includes(anchor.parent.type) && !anchor.previousNamedSibling || language === "powershell" && anchor.parent.type === "statement_list" && !anchor.previousNamedSibling)) anchor = anchor.parent;
660
+ const comments = [];
661
+ let previous = anchor.previousNamedSibling;
662
+ let start = anchor.startIndex;
663
+ while (previous && source.slice(previous.endIndex, start).trim() === "") {
664
+ if (COMMENT_TYPES.has(previous.type)) comments.unshift(previous);
665
+ else if (!(language === "rust" && previous.type === "attribute_item")) break;
666
+ start = previous.startIndex;
667
+ previous = previous.previousNamedSibling;
668
+ }
669
+ const markers = ["javascript", "typescript", "tsx", "java", "php"].includes(language) ? /^\/\*\*/ : language === "rust" ? /^(\/\/\/(?!\/)|\/\*\*)/ : ["cpp", "c-sharp"].includes(language) ? /^(\/\*\*|\/\/\/|\/\/!|\/\*!)/ : null;
670
+ const docs = markers ? comments.filter((comment) => markers.test(comment.text)) : comments;
671
+ return { text: docs.map((comment) => comment.text).join("\n"), startLine: docs[0] ? docs[0].startPosition.row + 1 : void 0 };
672
+ }
673
+ async function extractSymbols(path, source, language) {
674
+ const parser = await parserFor(language);
675
+ let tree;
676
+ try {
677
+ let walk = function(node2, owner = null) {
678
+ let kind = DECLARATIONS[node2.type];
679
+ let declaration = node2;
680
+ let body = field(node2, "body") ?? (language === "powershell" ? node2.namedChildren.find((child) => child.type === "script_block") : null);
681
+ if (["variable_declarator", "public_field_definition"].includes(node2.type)) {
682
+ const value = field(node2, "value");
683
+ if (value && ["arrow_function", "function_expression", "generator_function"].includes(value.type)) {
684
+ kind = "function";
685
+ body = field(value, "body");
686
+ }
687
+ }
688
+ if (node2.type === "type_spec" && ["struct_type", "interface_type"].includes(field(node2, "type")?.type)) {
689
+ kind = "class";
690
+ body = field(node2, "type").namedChildren.find((child) => child.type.endsWith("_list"));
691
+ }
692
+ if (node2.type === "impl_item") {
693
+ const type = field(node2, "type");
694
+ if (type) owner = symbols.find((symbol) => symbol.qualifiedName === type.text && ["struct", "class"].includes(symbol.kind)) ?? { qualifiedName: type.text, id: null, kind: "struct" };
695
+ }
696
+ if (kind === "function" && owner && ["class", "struct", "interface", "trait"].includes(owner.kind)) kind = "method";
697
+ const name = kind ? declaredName(declaration) : "";
698
+ if (kind && name) {
699
+ const qualifiedName = owner ? `${owner.qualifiedName}.${name}` : name;
700
+ const signature = declarationHeader(declaration, source, body?.startIndex ?? declaration.endIndex);
701
+ const key = `${path}#${qualifiedName}:${kind}:${hash(signature)}`;
702
+ const occurrence = occurrences.get(key) ?? 0;
703
+ occurrences.set(key, occurrence + 1);
704
+ const doc = documentation(declaration, body, language, source);
705
+ const symbol = {
706
+ id: `symbol:${key}:${occurrence}`,
707
+ kind,
708
+ name,
709
+ qualifiedName,
710
+ ownerId: owner?.id ?? null,
711
+ language,
712
+ path,
713
+ signature,
714
+ documentation: doc.text,
715
+ startLine: Math.min(doc.startLine ?? Infinity, declaration.startPosition.row + 1),
716
+ declarationStartLine: declaration.startPosition.row + 1,
717
+ endLine: lastLine(declaration),
718
+ bodyStartLine: body ? body.startPosition.row + 1 : null,
719
+ bodyEndLine: body ? lastLine(body) : null,
720
+ declarationStartOffset: declaration.startIndex,
721
+ declarationEndOffset: declaration.endIndex,
722
+ bodyStartOffset: body?.startIndex ?? null,
723
+ bodyEndOffset: body?.endIndex ?? null
724
+ };
725
+ symbols.push(symbol);
726
+ owner = symbol;
727
+ }
728
+ if (["import_statement", "export_statement", "import_from_statement", "use_declaration", "include_directive", "preproc_include"].includes(node2.type)) {
729
+ const target = field(node2, "source") ?? field(node2, "module_name") ?? field(node2, "path") ?? field(node2, "argument");
730
+ if (target) imports.push({ specifier: target.text.replace(/^['"]|['"]$/g, ""), line: node2.startPosition.row + 1, syntax: node2.type });
731
+ }
732
+ for (const child of node2.namedChildren) walk(child, owner);
733
+ };
734
+ tree = parser.parse(source);
735
+ if (!tree) throw new Error("Parser returned no syntax tree");
736
+ const symbols = [];
737
+ const imports = [];
738
+ const occurrences = /* @__PURE__ */ new Map();
739
+ walk(tree.rootNode);
740
+ return { symbols, imports, status: tree.rootNode.hasError ? "partial" : "parsed" };
741
+ } finally {
742
+ tree?.delete();
743
+ parser.delete();
744
+ }
745
+ }
746
+ var runtime, languages, field, lastLine, hash;
747
+ var init_parser = __esm({
748
+ "packages/cli/src/symbols/parser.mjs"() {
749
+ init_runtime();
750
+ init_languages();
751
+ languages = /* @__PURE__ */ new Map();
752
+ field = (node2, name) => node2.childForFieldName(name);
753
+ lastLine = (node2) => Math.max(node2.startPosition.row + 1, node2.endPosition.row + (node2.endPosition.column ? 1 : 0));
754
+ hash = (text) => createHash5("sha256").update(text).digest("hex").slice(0, 16);
755
+ }
756
+ });
3
757
 
4
758
  // packages/cli/src/init.mjs
5
759
  import { existsSync as existsSync4, mkdirSync, readFileSync as readFileSync4, statSync, writeFileSync } from "node:fs";
@@ -136,8 +890,8 @@ function sanitizeEmbeddingProfile(profile) {
136
890
  return { ...safe, ...apiKey ? { apiKey: "[redacted]" } : {} };
137
891
  }
138
892
  function embeddingProfileIdentity(profile) {
139
- const { apiKey, apiKeyEnv, ...identity } = profile;
140
- return identity;
893
+ const { apiKey, apiKeyEnv, ...identity2 } = profile;
894
+ return identity2;
141
895
  }
142
896
  function resolveEmbeddingProfile(root = ".", options = {}) {
143
897
  const home = options.home ?? homedir();
@@ -334,10 +1088,10 @@ function cloneReferenceExpansionPolicy(policy = {}) {
334
1088
  const defaults = uniqueNormalizedTerms(lowSignal.defaults ?? DEFAULT_FUZZY_LOW_SIGNAL_TERMS);
335
1089
  const add = uniqueNormalizedTerms(lowSignal.add ?? []);
336
1090
  const remove = uniqueNormalizedTerms(lowSignal.remove ?? []);
337
- const terms = new Set(defaults);
338
- for (const term of remove) terms.delete(term);
339
- for (const term of add) terms.add(term);
340
- return { fuzzy: { lowSignal: { defaults, add, remove, terms: [...terms].sort() } } };
1091
+ const terms2 = new Set(defaults);
1092
+ for (const term of remove) terms2.delete(term);
1093
+ for (const term of add) terms2.add(term);
1094
+ return { fuzzy: { lowSignal: { defaults, add, remove, terms: [...terms2].sort() } } };
341
1095
  }
342
1096
  function normalizeReferenceExpansionPolicy(raw) {
343
1097
  const lowSignal = raw?.fuzzy?.lowSignal ?? {};
@@ -481,11 +1235,11 @@ function isFiniteNumberInRange(value, min, max) {
481
1235
  }
482
1236
  function validateMemoryConfigData(data, root = ".", file = "dotdotgod.config.json") {
483
1237
  const errors = [];
484
- const add = (code, field, message, fix = null) => errors.push({
1238
+ const add = (code, field2, message, fix = null) => errors.push({
485
1239
  file: rel(root, resolve(root, file)),
486
1240
  code,
487
- message: `${field ? `Field "${field}": ` : ""}${message}
488
- Fix: ${fix ?? `update ${field ?? "this config"} in ${file} to match the expected dotdotgod config schema.`}`
1241
+ message: `${field2 ? `Field "${field2}": ` : ""}${message}
1242
+ Fix: ${fix ?? `update ${field2 ?? "this config"} in ${file} to match the expected dotdotgod config schema.`}`
489
1243
  });
490
1244
  if (!data || typeof data !== "object" || Array.isArray(data)) {
491
1245
  add("MEMORY_CONFIG_INVALID", null, "Config must be a JSON object.");
@@ -1306,9 +2060,9 @@ Create dotdotgod.config.json from a bundled or user template. Without --template
1306
2060
  dotdotgod status <root> [--json]`;
1307
2061
  case "query":
1308
2062
  return `Usage:
1309
- dotdotgod query <root> <query> [--limit n] [--json]
2063
+ dotdotgod query <root> <query> [--limit n] [--scope docs|code|all] [--search hybrid|keyword|vector] [--allow-code-embedding] [--json]
1310
2064
 
1311
- Search shared project documentation with the resolved local or remote embedding provider.`;
2065
+ Search full documentation and code outlines/comments. Default: hybrid docs+code. Remote code metadata requires explicit consent.`;
1312
2066
  case "embedding":
1313
2067
  return `Usage:
1314
2068
  dotdotgod embedding status [<root>] [--json]
@@ -1440,8 +2194,8 @@ function parseCommon(argv) {
1440
2194
  }
1441
2195
 
1442
2196
  // packages/cli/src/validate/run.mjs
1443
- import { existsSync as existsSync10, readFileSync as readFileSync9, readdirSync as readdirSync2 } from "node:fs";
1444
- import { basename as basename7, dirname as dirname6, extname as extname4, join as join8, resolve as resolve8 } from "node:path";
2197
+ import { existsSync as existsSync13, readFileSync as readFileSync13, readdirSync as readdirSync3 } from "node:fs";
2198
+ import { basename as basename7, dirname as dirname10, extname as extname6, join as join13, resolve as resolve8 } from "node:path";
1445
2199
 
1446
2200
  // packages/cli/src/docs/traceability.mjs
1447
2201
  import { existsSync as existsSync5 } from "node:fs";
@@ -1496,8 +2250,8 @@ function isLocalRelativeTraceabilityPath(value) {
1496
2250
  if (value.split("/").includes("..")) return false;
1497
2251
  return !isSecretIndexPath(value);
1498
2252
  }
1499
- function traceabilityFieldError(file, code, field, message, line = null, config = defaultMemoryConfig()) {
1500
- return { file, line, code, message: `${field ? `Field "${field}": ` : ""}${message}
2253
+ function traceabilityFieldError(file, code, field2, message, line = null, config = defaultMemoryConfig()) {
2254
+ return { file, line, code, message: `${field2 ? `Field "${field2}": ` : ""}${message}
1501
2255
  Fix: update the traceability block so it matches the expected schema and points to existing project files or commands.
1502
2256
 
1503
2257
  ${traceabilityExample(config)}` };
@@ -1629,13 +2383,13 @@ ${generated}
1629
2383
  ${suffix}`;
1630
2384
  return { ok: true, changed: next !== content, content: next };
1631
2385
  }
1632
- function validateTraceabilityPathArray(data, field, add, root, config, { required = true, displayField = field } = {}) {
1633
- if (data[field] === void 0 && !required) return;
1634
- if (!Array.isArray(data[field])) {
2386
+ function validateTraceabilityPathArray(data, field2, add, root, config, { required = true, displayField = field2 } = {}) {
2387
+ if (data[field2] === void 0 && !required) return;
2388
+ if (!Array.isArray(data[field2])) {
1635
2389
  add("TRACEABILITY_INVALID_FIELD", displayField, "must be an array of local relative paths.");
1636
2390
  return;
1637
2391
  }
1638
- for (const value of data[field]) {
2392
+ for (const value of data[field2]) {
1639
2393
  if (!isLocalRelativeTraceabilityPath(value)) {
1640
2394
  add("TRACEABILITY_INVALID_PATH", displayField, `invalid local relative path: ${JSON.stringify(value)}.`);
1641
2395
  continue;
@@ -1648,13 +2402,13 @@ function validateTraceabilityPathArray(data, field, add, root, config, { require
1648
2402
  if (!existsSync5(resolve4(root, value))) add("TRACEABILITY_MISSING_TARGET", displayField, `target does not exist: ${value}.`);
1649
2403
  }
1650
2404
  }
1651
- function validateTraceabilityCommandArray(data, field, add, { required = true, displayField = field } = {}) {
1652
- if (data[field] === void 0 && !required) return;
1653
- if (!Array.isArray(data[field])) {
2405
+ function validateTraceabilityCommandArray(data, field2, add, { required = true, displayField = field2 } = {}) {
2406
+ if (data[field2] === void 0 && !required) return;
2407
+ if (!Array.isArray(data[field2])) {
1654
2408
  add("TRACEABILITY_INVALID_FIELD", displayField, "must be an array of executable project-local command strings.");
1655
2409
  return;
1656
2410
  }
1657
- for (const value of data[field]) if (typeof value !== "string" || value.trim().length === 0) add("TRACEABILITY_INVALID_COMMAND", displayField, `invalid command: ${JSON.stringify(value)}.`);
2411
+ for (const value of data[field2]) if (typeof value !== "string" || value.trim().length === 0) add("TRACEABILITY_INVALID_COMMAND", displayField, `invalid command: ${JSON.stringify(value)}.`);
1658
2412
  }
1659
2413
  function validateConfiguredArray(data, definition, add, root, config, options) {
1660
2414
  if (definition.target === "command") validateTraceabilityCommandArray(data, definition.key, add, options);
@@ -1675,8 +2429,8 @@ function validateTraceabilityContracts(data, root, add, config) {
1675
2429
  add("TRACEABILITY_INVALID_FIELD", base, "must be an object with non-empty id and title fields.");
1676
2430
  continue;
1677
2431
  }
1678
- for (const field of Object.keys(contract)) {
1679
- if (!allowedFields.has(field)) add("TRACEABILITY_INVALID_FIELD", `${base}.${field}`, "is not a supported contract field.");
2432
+ for (const field2 of Object.keys(contract)) {
2433
+ if (!allowedFields.has(field2)) add("TRACEABILITY_INVALID_FIELD", `${base}.${field2}`, "is not a supported contract field.");
1680
2434
  }
1681
2435
  if (typeof contract.id !== "string" || contract.id.trim().length === 0) {
1682
2436
  add("TRACEABILITY_INVALID_FIELD", `${base}.id`, "must be a non-empty stable contract ID string.");
@@ -1697,7 +2451,7 @@ function validateTraceabilityContracts(data, root, add, config) {
1697
2451
  }
1698
2452
  function validateTraceabilityBlock(data, root, file, line = null, config = defaultMemoryConfig()) {
1699
2453
  const errors = [];
1700
- const add = (code, field, message) => errors.push(traceabilityFieldError(rel(root, file), code, field, message, line, config));
2454
+ const add = (code, field2, message) => errors.push(traceabilityFieldError(rel(root, file), code, field2, message, line, config));
1701
2455
  if (!data || typeof data !== "object" || Array.isArray(data)) {
1702
2456
  add("TRACEABILITY_INVALID_JSON", null, "Traceability block must be a JSON object.");
1703
2457
  return errors;
@@ -1705,7 +2459,7 @@ function validateTraceabilityBlock(data, root, file, line = null, config = defau
1705
2459
  if (data.kind !== "spec") add("TRACEABILITY_INVALID_KIND", "kind", 'must be "spec" for behavior specs.');
1706
2460
  const definitions2 = traceabilityKeys(config);
1707
2461
  const allowedFields = /* @__PURE__ */ new Set(["kind", "contracts", ...definitions2.map((definition) => definition.key)]);
1708
- for (const field of Object.keys(data)) if (!allowedFields.has(field)) add("TRACEABILITY_INVALID_FIELD", field, "is not configured as a traceability field.");
2462
+ for (const field2 of Object.keys(data)) if (!allowedFields.has(field2)) add("TRACEABILITY_INVALID_FIELD", field2, "is not configured as a traceability field.");
1709
2463
  for (const definition of definitions2) validateConfiguredArray(data, definition, add, root, config, { required: true, displayField: definition.key });
1710
2464
  validateTraceabilityContracts(data, root, add, config);
1711
2465
  return errors;
@@ -1932,8 +2686,8 @@ function cacheFile(root) {
1932
2686
  }
1933
2687
 
1934
2688
  // packages/cli/src/index/cache.mjs
1935
- import { existsSync as existsSync9, readFileSync as readFileSync8, statSync as statSync4 } from "node:fs";
1936
- import { basename as basename6, join as join7 } from "node:path";
2689
+ import { existsSync as existsSync12, readFileSync as readFileSync12, statSync as statSync4 } from "node:fs";
2690
+ import { basename as basename6, join as join12 } from "node:path";
1937
2691
 
1938
2692
  // packages/cli/src/graph/store.mjs
1939
2693
  import { existsSync as existsSync7, mkdirSync as mkdirSync2, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
@@ -2231,6 +2985,290 @@ function buildGraph(root, files, config = defaultMemoryConfig()) {
2231
2985
  return graph;
2232
2986
  }
2233
2987
 
2988
+ // packages/cli/src/symbols/store.mjs
2989
+ import { existsSync as existsSync11, lstatSync as lstatSync2, mkdirSync as mkdirSync3, readFileSync as readFileSync11, renameSync, writeFileSync as writeFileSync3 } from "node:fs";
2990
+ import { randomUUID } from "node:crypto";
2991
+ import { dirname as dirname7, join as join11 } from "node:path";
2992
+
2993
+ // packages/cli/src/symbols/discovery.mjs
2994
+ var import_ignore = __toESM(require_ignore(), 1);
2995
+ import { existsSync as existsSync10, lstatSync, readFileSync as readFileSync10, realpathSync } from "node:fs";
2996
+ import { extname as extname5, join as join9, relative as relative4, isAbsolute } from "node:path";
2997
+
2998
+ // packages/cli/src/query/chunks.mjs
2999
+ import { readFileSync as readFileSync8, readdirSync as readdirSync2 } from "node:fs";
3000
+ import { join as join7, relative as relative3 } from "node:path";
3001
+ import { createHash as createHash3 } from "node:crypto";
3002
+ var MAX_CHUNK_CHARS = 1600;
3003
+ var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", ".git", ".dotdotgod"]);
3004
+ function textFingerprint(text) {
3005
+ return createHash3("sha256").update(text).digest("hex");
3006
+ }
3007
+ function collectDocumentationMarkdown(root, exclude = ["docs/plan", "docs/archive"], documentationRoot = "docs") {
3008
+ const docsRoot = join7(root, documentationRoot);
3009
+ const files = [];
3010
+ const walk = (directory) => {
3011
+ let entries;
3012
+ try {
3013
+ entries = readdirSync2(directory, { withFileTypes: true, encoding: "utf8" });
3014
+ } catch {
3015
+ return;
3016
+ }
3017
+ entries.sort((a, b) => a.name.localeCompare(b.name));
3018
+ for (const entry of entries) {
3019
+ if (entry.name.startsWith(".")) continue;
3020
+ const absolute = join7(directory, entry.name);
3021
+ const path = relative3(root, absolute).replaceAll("\\", "/");
3022
+ if (isSecretLikePathPattern(path) || exclude.some((pattern) => matchPathPattern(path, pattern) || path === pattern || path.startsWith(`${pattern}/`))) continue;
3023
+ if (entry.isDirectory()) {
3024
+ if (!SKIPPED_DIRECTORIES.has(entry.name)) walk(absolute);
3025
+ } else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) files.push(path);
3026
+ }
3027
+ };
3028
+ walk(docsRoot);
3029
+ return files.sort();
3030
+ }
3031
+ function splitSection(text, maxChars = MAX_CHUNK_CHARS) {
3032
+ if (text.length <= maxChars) return [text];
3033
+ const paragraphs = text.split(/\n\s*\n/);
3034
+ const chunks = [];
3035
+ let current = "";
3036
+ for (const paragraph of paragraphs) {
3037
+ if (current && current.length + paragraph.length + 2 > maxChars) {
3038
+ chunks.push(current.trim());
3039
+ current = "";
3040
+ }
3041
+ if (paragraph.length > maxChars) {
3042
+ for (let offset = 0; offset < paragraph.length; offset += maxChars) chunks.push(paragraph.slice(offset, offset + maxChars).trim());
3043
+ } else current += `${current ? "\n\n" : ""}${paragraph}`;
3044
+ }
3045
+ if (current.trim()) chunks.push(current.trim());
3046
+ return chunks.filter(Boolean);
3047
+ }
3048
+ function chunkMarkdown(path, content, maxChars = MAX_CHUNK_CHARS) {
3049
+ const lines = content.split("\n");
3050
+ const sections = [];
3051
+ let headings = [];
3052
+ let body = [];
3053
+ const flush = () => {
3054
+ const text = body.join("\n").trim();
3055
+ if (!text && headings.length === 0) return;
3056
+ const heading = headings.length > 0 ? headings.join(" > ") : path;
3057
+ const pieces = splitSection(text || heading, maxChars);
3058
+ pieces.forEach((piece, index) => {
3059
+ const passage = `Path: ${path}
3060
+ Section: ${heading}
3061
+
3062
+ ${piece}`;
3063
+ const fingerprint2 = textFingerprint(passage);
3064
+ sections.push({ id: `${path}#${fingerprint2.slice(0, 16)}`, path, heading, part: index + 1, text: piece, passage, fingerprint: fingerprint2 });
3065
+ });
3066
+ };
3067
+ for (const line of lines) {
3068
+ const match = /^(#{1,6})\s+(.+?)\s*$/.exec(line);
3069
+ if (!match) {
3070
+ body.push(line);
3071
+ continue;
3072
+ }
3073
+ flush();
3074
+ body = [];
3075
+ const level = match[1].length;
3076
+ headings = [...headings.slice(0, level - 1), match[2]];
3077
+ }
3078
+ flush();
3079
+ return sections;
3080
+ }
3081
+ function collectDocumentationChunks(root, exclude, documentationRoot = "docs") {
3082
+ return collectDocumentationMarkdown(root, exclude, documentationRoot).flatMap((path) => {
3083
+ const content = readFileSync8(join7(root, path), "utf8");
3084
+ return chunkMarkdown(path, content);
3085
+ });
3086
+ }
3087
+
3088
+ // packages/cli/src/symbols/discovery.mjs
3089
+ init_languages();
3090
+ init_runtime();
3091
+ function inside(root, file) {
3092
+ if (!lstatSync(file).isFile()) return false;
3093
+ const path = relative4(realpathSync(root), realpathSync(file));
3094
+ return !path.startsWith("..") && !isAbsolute(path);
3095
+ }
3096
+ function ignoreRules(root, directory, cache) {
3097
+ if (!cache.has(directory)) {
3098
+ const file = join9(root, directory, ".gitignore");
3099
+ cache.set(directory, (0, import_ignore.default)().add(existsSync10(file) && inside(root, file) ? readFileSync10(file, "utf8") : ""));
3100
+ }
3101
+ return cache.get(directory);
3102
+ }
3103
+ function ignored(root, path, cache) {
3104
+ const parts = path.split("/");
3105
+ let excluded = false;
3106
+ for (let depth = 0; depth < parts.length; depth += 1) {
3107
+ const directory = parts.slice(0, depth).join("/");
3108
+ const remaining = parts.slice(depth).join("/");
3109
+ const rules = ignoreRules(root, directory, cache);
3110
+ for (let end = 1; end < parts.length - depth; end += 1) {
3111
+ if (rules.ignores(parts.slice(depth, depth + end).join("/") + "/")) return true;
3112
+ }
3113
+ const result = rules.test(remaining);
3114
+ if (result.ignored) excluded = true;
3115
+ else if (result.unignored) excluded = false;
3116
+ }
3117
+ return excluded;
3118
+ }
3119
+ function sourceFiles(root) {
3120
+ const cache = /* @__PURE__ */ new Map();
3121
+ return collectIndexFiles(root).filter((file) => {
3122
+ const path = relative4(root, file).replaceAll("\\", "/");
3123
+ if (!SOURCE_EXTENSIONS.has(extname5(path)) || path.split("/").some((part) => part.startsWith(".")) || isSecretLikePathPattern(path)) return false;
3124
+ if (!inside(root, file) || ignored(root, path, cache)) return false;
3125
+ return !/^#![^\n]*\n\/\/ Generated|^\/\/ Generated|@generated/m.test(readFileSync10(file, "utf8").slice(0, 256));
3126
+ }).map((file) => {
3127
+ const source = readFileSync10(file, "utf8");
3128
+ return { path: relative4(root, file).replaceAll("\\", "/"), hash: fingerprint(file), language: languageForPath(file), sourceLength: source.length, lineCount: source.split("\n").length };
3129
+ });
3130
+ }
3131
+ function outlinePolicy(root) {
3132
+ const config = readMemoryConfig(root);
3133
+ const ignores = collectIndexFiles(root).filter((file) => file.endsWith("/.gitignore") && inside(root, file)).map((file) => [relative4(root, file), fingerprint(file)]);
3134
+ return textFingerprint(JSON.stringify({ areas: config.areas, ignores, extractor: EXTRACTOR_ID, declarations: DECLARATIONS, extensions: LANGUAGE_EXTENSIONS, parser: parserIdentity() }));
3135
+ }
3136
+
3137
+ // packages/cli/src/symbols/store.mjs
3138
+ init_languages();
3139
+ var OUTLINE_SCHEMA = 1;
3140
+ var outlinePath = (root) => join11(root, ".dotdotgod", "outlines.json");
3141
+ function assertCachePath(root) {
3142
+ for (const path of [dirname7(outlinePath(root)), outlinePath(root)]) {
3143
+ let stat;
3144
+ try {
3145
+ stat = lstatSync2(path);
3146
+ } catch (error) {
3147
+ if (error.code !== "ENOENT") throw error;
3148
+ }
3149
+ if (stat?.isSymbolicLink()) throw new Error(`Outline cache must not use symlinks: ${path}`);
3150
+ }
3151
+ }
3152
+ function readOutlines(root) {
3153
+ try {
3154
+ assertCachePath(root);
3155
+ const index = JSON.parse(readFileSync11(outlinePath(root), "utf8"));
3156
+ if (index.schema !== OUTLINE_SCHEMA || index.extractor !== EXTRACTOR_ID || !Array.isArray(index.files)) return null;
3157
+ const paths = /* @__PURE__ */ new Set();
3158
+ const ids = /* @__PURE__ */ new Set();
3159
+ for (const file of index.files) {
3160
+ if (typeof file.path !== "string" || file.path.startsWith("/") || file.path.split("/").includes("..") || paths.has(file.path) || !/^[a-f0-9]{64}$/.test(file.hash)) return null;
3161
+ if (!Number.isInteger(file.sourceLength) || file.sourceLength < 0 || !Number.isInteger(file.lineCount) || file.lineCount < 1) return null;
3162
+ if (file.language !== languageForPath(file.path) || file.language === null !== (file.status === "unsupported")) return null;
3163
+ paths.add(file.path);
3164
+ if (!["parsed", "partial", "unsupported", "failed"].includes(file.status) || !Array.isArray(file.symbols) || !Array.isArray(file.imports)) return null;
3165
+ if (file.imports.some((item) => typeof item.specifier !== "string" || !Number.isInteger(item.line) || item.line < 1)) return null;
3166
+ for (const symbol of file.symbols) {
3167
+ if (symbol.path !== file.path || typeof symbol.id !== "string" || !symbol.id.startsWith(`symbol:${file.path}#`) || ids.has(symbol.id) || typeof symbol.signature !== "string" || typeof symbol.documentation !== "string") return null;
3168
+ if (!Number.isInteger(symbol.startLine) || !Number.isInteger(symbol.endLine) || symbol.startLine < 1 || symbol.endLine < symbol.startLine || symbol.endLine > file.lineCount) return null;
3169
+ if (typeof symbol.name !== "string" || typeof symbol.qualifiedName !== "string" || !Number.isInteger(symbol.declarationStartLine) || symbol.declarationStartLine < symbol.startLine || symbol.declarationStartLine > symbol.endLine) return null;
3170
+ if (symbol.bodyStartLine === null !== (symbol.bodyEndLine === null)) return null;
3171
+ if (symbol.bodyStartLine !== null && (!Number.isInteger(symbol.bodyStartLine) || !Number.isInteger(symbol.bodyEndLine) || symbol.bodyStartLine < symbol.declarationStartLine || symbol.bodyEndLine > symbol.endLine || symbol.bodyEndLine < symbol.bodyStartLine)) return null;
3172
+ if (!Number.isInteger(symbol.declarationStartOffset) || !Number.isInteger(symbol.declarationEndOffset) || symbol.declarationStartOffset < 0 || symbol.declarationEndOffset < symbol.declarationStartOffset || symbol.declarationEndOffset > file.sourceLength) return null;
3173
+ if (symbol.bodyStartOffset === null !== (symbol.bodyEndOffset === null)) return null;
3174
+ if (symbol.bodyStartOffset !== null && (!Number.isInteger(symbol.bodyStartOffset) || !Number.isInteger(symbol.bodyEndOffset) || symbol.bodyStartOffset < symbol.declarationStartOffset || symbol.bodyEndOffset > symbol.declarationEndOffset || symbol.bodyEndOffset < symbol.bodyStartOffset)) return null;
3175
+ ids.add(symbol.id);
3176
+ }
3177
+ }
3178
+ for (const file of index.files) for (const symbol of file.symbols) if (symbol.ownerId !== null && !ids.has(symbol.ownerId)) return null;
3179
+ return index;
3180
+ } catch {
3181
+ return null;
3182
+ }
3183
+ }
3184
+ function outlineStatus(root) {
3185
+ assertCachePath(root);
3186
+ const files = sourceFiles(root);
3187
+ const index = readOutlines(root);
3188
+ if (!index) return { ok: files.length === 0 && !existsSync11(outlinePath(root)), reason: existsSync11(outlinePath(root)) ? "missing-or-invalid" : "missing", paths: files.map((file) => file.path) };
3189
+ const old = new Map(index.files.map((file) => [file.path, file]));
3190
+ const current = new Set(files.map((file) => file.path));
3191
+ const changed = files.filter((file) => old.get(file.path)?.hash !== file.hash || old.get(file.path)?.sourceLength !== file.sourceLength || old.get(file.path)?.lineCount !== file.lineCount).map((file) => file.path);
3192
+ const removed = [...old.keys()].filter((path) => !current.has(path));
3193
+ const failures = index.files.filter((file) => ["failed", "partial"].includes(file.status)).map((file) => file.path);
3194
+ const policyOk = index.policy === outlinePolicy(root);
3195
+ return { ok: policyOk && !changed.length && !removed.length && !failures.length, reason: !policyOk ? "policy-changed" : failures.length ? "extraction-incomplete" : changed.length || removed.length ? "source-changed" : "fresh", paths: [.../* @__PURE__ */ new Set([...changed, ...removed, ...failures])], unsupported: index.files.filter((file) => file.status === "unsupported").map((file) => file.path) };
3196
+ }
3197
+ async function buildOutlines(root) {
3198
+ assertCachePath(root);
3199
+ const files = sourceFiles(root);
3200
+ const old = readOutlines(root);
3201
+ const previous = new Map(old?.policy === outlinePolicy(root) ? old.files.map((file) => [file.path, file]) : []);
3202
+ let parsed = 0;
3203
+ let reused = 0;
3204
+ const records = [];
3205
+ for (const file of files) {
3206
+ const cached = previous.get(file.path);
3207
+ if (cached?.hash === file.hash && ["parsed", "unsupported"].includes(cached.status)) {
3208
+ records.push(cached);
3209
+ reused += 1;
3210
+ continue;
3211
+ }
3212
+ if (!file.language) {
3213
+ records.push({ ...file, status: "unsupported", symbols: [], imports: [] });
3214
+ continue;
3215
+ }
3216
+ const { extractSymbols: extractSymbols2 } = await Promise.resolve().then(() => (init_parser(), parser_exports));
3217
+ const source = readFileSync11(join11(root, file.path), "utf8");
3218
+ const extracted = await extractSymbols2(file.path, source, file.language);
3219
+ if (fingerprint(join11(root, file.path)) !== file.hash) throw new Error(`Source changed during outline extraction: ${file.path}`);
3220
+ records.push({ ...file, ...extracted });
3221
+ parsed += 1;
3222
+ }
3223
+ const index = { schema: OUTLINE_SCHEMA, extractor: EXTRACTOR_ID, policy: outlinePolicy(root), files: records, refresh: { parsed, reused } };
3224
+ const target = outlinePath(root);
3225
+ mkdirSync3(dirname7(target), { recursive: true });
3226
+ const temporary = `${target}.tmp-${process.pid}-${randomUUID()}`;
3227
+ writeFileSync3(temporary, JSON.stringify(index), { flag: "wx" });
3228
+ renameSync(temporary, target);
3229
+ return index;
3230
+ }
3231
+ function outlineChunks(index) {
3232
+ return index.files.flatMap((file) => file.symbols.map((symbol) => {
3233
+ const text = `${symbol.signature}
3234
+ ${symbol.documentation}`.trim();
3235
+ const passage = `Path: ${file.path}
3236
+ Symbol: ${symbol.qualifiedName}
3237
+ Kind: ${symbol.kind}
3238
+
3239
+ ${text}`;
3240
+ return { ...symbol, kind: "symbol", symbolKind: symbol.kind, heading: symbol.qualifiedName, text, passage, sourceHash: file.hash, fingerprint: textFingerprint(passage) };
3241
+ }));
3242
+ }
3243
+
3244
+ // packages/cli/src/symbols/graph.mjs
3245
+ import { dirname as dirname8, posix } from "node:path";
3246
+ function importTarget(file, specifier, paths) {
3247
+ if (!specifier.startsWith(".")) return null;
3248
+ const base = posix.normalize(posix.join(dirname8(file).replaceAll("\\", "/"), specifier));
3249
+ if (base.startsWith("../")) return null;
3250
+ const stem = base.replace(/\.(js|jsx|mjs|cjs)$/, "");
3251
+ const candidates = [base, ...[".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py"].map((extension) => `${stem}${extension}`), ...[".ts", ".js", ".py"].map((extension) => `${base}/index${extension}`)];
3252
+ return candidates.find((path) => paths.has(path)) ?? null;
3253
+ }
3254
+ function addOutlineGraph(graph, index) {
3255
+ const paths = new Set(index.files.map((file) => file.path));
3256
+ for (const file of index.files) {
3257
+ const source = `file:${file.path}`;
3258
+ for (const symbol of file.symbols) {
3259
+ addNode(graph, symbol.id, "symbol", { ...symbol, sourceHash: file.hash });
3260
+ addEdge(graph, symbol.ownerId ?? source, symbol.id, symbol.ownerId ? "contains_symbol" : "declares_symbol", { confidence: "PARSED", path: file.path });
3261
+ }
3262
+ for (const item of file.imports) {
3263
+ const resolved = importTarget(file.path, item.specifier, paths);
3264
+ const target = resolved ? `file:${resolved}` : `import:${file.path}#${item.specifier}`;
3265
+ if (!resolved) addNode(graph, target, "unresolved_import", { path: file.path, specifier: item.specifier });
3266
+ addEdge(graph, source, target, "imports", { ...item, resolved: Boolean(resolved), confidence: resolved ? "RESOLVED_RELATIVE_FILE" : "SYNTAX_ONLY", path: file.path });
3267
+ }
3268
+ }
3269
+ return graph;
3270
+ }
3271
+
2234
3272
  // packages/cli/src/index/cache.mjs
2235
3273
  function collectFingerprints(root, config = readMemoryConfig(root)) {
2236
3274
  return collectIndexFiles(root, config).map((file) => {
@@ -2261,11 +3299,23 @@ function buildIndex(root, previous = readIndex(root)) {
2261
3299
  const currentPaths = new Set(files.map((file) => file.path));
2262
3300
  const removedPaths = (previous?.files ?? []).filter((file) => !currentPaths.has(file.path)).map((file) => file.path);
2263
3301
  const changedPaths = [...files.filter((file) => indexed.get(file.path) !== file.sha256).map((file) => file.path), ...removedPaths];
2264
- const changedFiles = files.filter((file) => changedPaths.includes(file.path)).map((file) => join7(root, file.path));
3302
+ const changedFiles = files.filter((file) => changedPaths.includes(file.path)).map((file) => join12(root, file.path));
2265
3303
  const fullRebuild = !previous?.graph || previous.version !== CACHE_VERSION;
2266
3304
  const refreshReason = !previous ? "missing" : previous.version !== CACHE_VERSION ? "schema-mismatch" : removedPaths.length > 0 ? "content-removed" : changedPaths.length > 0 ? "content-changed" : "fresh";
2267
- const rawGraph = fullRebuild ? buildGraph(root, files.map((file) => join7(root, file.path)), memoryConfig) : mergeIncrementalGraph(previous.graph, buildGraph(root, changedFiles, memoryConfig), changedPaths);
2268
- const graph = rawGraph;
3305
+ const rawGraph = fullRebuild ? buildGraph(root, files.map((file) => join12(root, file.path)), memoryConfig) : mergeIncrementalGraph(previous.graph, buildGraph(root, changedFiles, memoryConfig), changedPaths);
3306
+ const graph = { nodes: rawGraph.nodes.filter((node2) => !["symbol", "unresolved_import"].includes(node2.type)), edges: rawGraph.edges.filter((edge) => !["declares_symbol", "contains_symbol", "imports"].includes(edge.relation)) };
3307
+ const outlines = readOutlines(root);
3308
+ let outlinePolicyMatches = false;
3309
+ if (outlines) {
3310
+ try {
3311
+ outlinePolicyMatches = outlines.policy === outlinePolicy(root);
3312
+ } catch {
3313
+ }
3314
+ }
3315
+ if (outlinePolicyMatches) {
3316
+ const hashes = new Map(files.map((file) => [file.path, file.sha256]));
3317
+ addOutlineGraph(graph, { files: outlines.files.filter((file) => hashes.get(file.path) === file.hash && file.status === "parsed") });
3318
+ }
2269
3319
  const archiveBodiesIncluded = (memoryConfig.areas ?? []).some((area2) => area2.id === "archive-body" && area2.includeBodiesByDefault !== false);
2270
3320
  return { version: CACHE_VERSION, schemaVersion: CACHE_VERSION, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), archiveBodiesIncluded, memoryConfig: memoryConfigSummary(memoryConfig), files, graph, stats: graphStats(graph), incremental: { enabled: true, fullRebuild, changedFiles: changedPaths.length, refreshReason, elapsedMs: Date.now() - startedAt } };
2271
3321
  }
@@ -2276,20 +3326,20 @@ function writeIndex(root, index) {
2276
3326
  const graphShards = { nodes: Object.fromEntries(Object.keys(compact.nodes).map((name) => [name, rel(root, shardFile(root, "nodes", name))])), edges: Object.fromEntries(Object.keys(compact.edges).map((name) => [name, rel(root, shardFile(root, "edges", name))])) };
2277
3327
  const manifest = { version: index.version, schemaVersion: index.schemaVersion ?? index.version, generatedAt: index.generatedAt, archiveBodiesIncluded: index.archiveBodiesIncluded, memoryConfig: index.memoryConfig, files: index.files, graph: { ...graphStats(index.graph), compactSchema: true, shards: graphShards }, incremental: index.incremental };
2278
3328
  writeJson(cacheFile(root), manifest);
2279
- const shardBytes = [...Object.values(graphShards.nodes), ...Object.values(graphShards.edges)].reduce((sum, path) => sum + jsonSize(join7(root, path)), 0);
3329
+ const shardBytes = [...Object.values(graphShards.nodes), ...Object.values(graphShards.edges)].reduce((sum, path) => sum + jsonSize(join12(root, path)), 0);
2280
3330
  const manifestBytes = jsonSize(cacheFile(root));
2281
3331
  return { ...manifest, indexSizeBytes: manifestBytes + shardBytes, manifestBytes, shardBytes };
2282
3332
  }
2283
3333
  function readIndex(root) {
2284
3334
  const file = cacheFile(root);
2285
- if (!existsSync9(file)) return null;
3335
+ if (!existsSync12(file)) return null;
2286
3336
  try {
2287
- const manifest = JSON.parse(readFileSync8(file, "utf8"));
3337
+ const manifest = JSON.parse(readFileSync12(file, "utf8"));
2288
3338
  const shards = manifest.graph?.shards;
2289
3339
  if (!shards) return manifest;
2290
3340
  const compact = { nodes: {}, edges: {} };
2291
- for (const [name, path] of Object.entries(shards.nodes ?? {})) compact.nodes[name] = existsSync9(join7(root, path)) ? JSON.parse(readFileSync8(join7(root, path), "utf8")) : [];
2292
- for (const [name, path] of Object.entries(shards.edges ?? {})) compact.edges[name] = existsSync9(join7(root, path)) ? JSON.parse(readFileSync8(join7(root, path), "utf8")) : [];
3341
+ for (const [name, path] of Object.entries(shards.nodes ?? {})) compact.nodes[name] = existsSync12(join12(root, path)) ? JSON.parse(readFileSync12(join12(root, path), "utf8")) : [];
3342
+ for (const [name, path] of Object.entries(shards.edges ?? {})) compact.edges[name] = existsSync12(join12(root, path)) ? JSON.parse(readFileSync12(join12(root, path), "utf8")) : [];
2293
3343
  return { ...manifest, graph: expandGraph(compact) };
2294
3344
  } catch {
2295
3345
  return null;
@@ -2311,11 +3361,12 @@ function getStatus(root) {
2311
3361
  const reason = ok ? "fresh" : !schemaOk ? "schema-mismatch" : staleFiles.length > 0 ? "content-changed" : "unknown";
2312
3362
  return { ok, status: ok ? "fresh" : "stale", cachePath: rel(root, cacheFile(root)), indexedFiles: index.files?.length ?? 0, currentFiles: currentFiles.length, staleFiles: staleFiles.length, examples: staleFiles.slice(0, 10), archiveBodiesIncluded: index.archiveBodiesIncluded === true, schemaVersion, expectedSchemaVersion: CACHE_VERSION, schemaOk, reason, graph: graphStats(index.graph ?? { nodes: [], edges: [] }) };
2313
3363
  }
2314
- function runIndex(argv) {
3364
+ async function runIndex(argv) {
2315
3365
  const options = parseCommon(argv);
3366
+ const outlines = await buildOutlines(options.root);
2316
3367
  const index = buildIndex(options.root);
2317
3368
  const manifest = writeIndex(options.root, index);
2318
- const result = { ok: true, cachePath: rel(options.root, cacheFile(options.root)), schemaVersion: CACHE_VERSION, indexedFiles: index.files.length, nodes: index.graph.nodes.length, edges: index.graph.edges.length, indexSizeBytes: manifest.indexSizeBytes, shards: manifest.graph.shards, incremental: index.incremental, archiveBodiesIncluded: index.archiveBodiesIncluded === true, memoryConfig: index.memoryConfig };
3369
+ const result = { ok: true, cachePath: rel(options.root, cacheFile(options.root)), schemaVersion: CACHE_VERSION, indexedFiles: index.files.length, outlines: { refresh: outlines.refresh, unsupported: outlines.files.filter((file) => file.status === "unsupported").map((file) => file.path), partial: outlines.files.filter((file) => file.status === "partial").map((file) => file.path) }, nodes: index.graph.nodes.length, edges: index.graph.edges.length, indexSizeBytes: manifest.indexSizeBytes, shards: manifest.graph.shards, incremental: index.incremental, archiveBodiesIncluded: index.archiveBodiesIncluded === true, memoryConfig: index.memoryConfig };
2319
3370
  if (options.json) console.log(JSON.stringify(result, null, 2));
2320
3371
  else console.log(`\u2705 index written (${result.indexedFiles} files, ${result.nodes} nodes, ${result.edges} edges, ${(result.indexSizeBytes / 1024).toFixed(1)} KiB, cache: ${result.cachePath})`);
2321
3372
  }
@@ -2344,7 +3395,7 @@ function graphSummary(index) {
2344
3395
 
2345
3396
  // packages/cli/src/validate/references.mjs
2346
3397
  import { statSync as statSync5 } from "node:fs";
2347
- import { dirname as dirname5, relative as relative3, resolve as resolve7 } from "node:path";
3398
+ import { dirname as dirname9, relative as relative5, resolve as resolve7 } from "node:path";
2348
3399
  var blank = (text) => text.replace(/[^\n\r]/g, " ");
2349
3400
  var unescape = (text) => text.replace(/\\([!"#$%&'()*+,\-./:;<=>?@[\]\\^_`{|}~])/g, "$1");
2350
3401
  var labelKey = (text) => unescape(text).trim().replace(/\s+/g, " ").toLowerCase();
@@ -2444,7 +3495,7 @@ function extractMemoryReferences(content) {
2444
3495
  return references;
2445
3496
  }
2446
3497
  function sharedLocalReferences(content, root, file, config) {
2447
- const source = resolveMemoryArea(relative3(root, file).split("\\").join("/"), config);
3498
+ const source = resolveMemoryArea(relative5(root, file).split("\\").join("/"), config);
2448
3499
  if (source?.scope !== "shared") return [];
2449
3500
  const errors = [];
2450
3501
  for (const item of extractMemoryReferences(content)) {
@@ -2456,8 +3507,8 @@ function sharedLocalReferences(content, root, file, config) {
2456
3507
  continue;
2457
3508
  }
2458
3509
  if (!path) continue;
2459
- const base = item.kind === "markdown-link" || /^\.\.?\//.test(path) ? dirname5(file) : root;
2460
- const target = relative3(root, resolve7(base, path)).split("\\").join("/");
3510
+ const base = item.kind === "markdown-link" || /^\.\.?\//.test(path) ? dirname9(file) : root;
3511
+ const target = relative5(root, resolve7(base, path)).split("\\").join("/");
2461
3512
  if (target === ".." || target.startsWith("../")) continue;
2462
3513
  if (item.kind === "inline-code") {
2463
3514
  const configuredDirectory = (config.areas ?? []).some((area3) => (area3.paths ?? []).some((pattern) => pattern === `${target}/**`));
@@ -2491,7 +3542,7 @@ function runValidate(argv) {
2491
3542
  const root = resolve8(options.root);
2492
3543
  const memoryConfig = readMemoryConfig(root);
2493
3544
  const documentationRoot = memoryConfig.documentation?.root ?? "docs";
2494
- const docs = join8(root, documentationRoot);
3545
+ const docs = join13(root, documentationRoot);
2495
3546
  const errors = [];
2496
3547
  const warnings = [];
2497
3548
  const markdownFiles = [];
@@ -2509,9 +3560,9 @@ Fix: ${fix}` : message, ...prompt ? { prompt } : {} });
2509
3560
  return false;
2510
3561
  };
2511
3562
  const walk = (dir) => {
2512
- if (!existsSync10(dir) || shouldSkipDir(dir)) return;
2513
- for (const entry of readdirSync2(dir, { withFileTypes: true })) {
2514
- const path = join8(dir, entry.name);
3563
+ if (!existsSync13(dir) || shouldSkipDir(dir)) return;
3564
+ for (const entry of readdirSync3(dir, { withFileTypes: true })) {
3565
+ const path = join13(dir, entry.name);
2515
3566
  if (entry.isDirectory()) {
2516
3567
  const docsRel = rel(docs, path);
2517
3568
  if (docsRel && !docsRel.startsWith("..")) {
@@ -2521,7 +3572,7 @@ Fix: ${fix}` : message, ...prompt ? { prompt } : {} });
2521
3572
  } else if (entry.isFile() && entry.name.endsWith(".md")) markdownFiles.push(path);
2522
3573
  }
2523
3574
  };
2524
- if (!existsSync10(docs)) usage(`docs directory not found: ${docs}`);
3575
+ if (!existsSync13(docs)) usage(`docs directory not found: ${docs}`);
2525
3576
  const validationPolicy = cloneValidationPolicy(memoryConfig.validation ?? DEFAULT_VALIDATION_POLICY);
2526
3577
  const maxLines = options.maxLines ?? validationPolicy.markdown.maxLines;
2527
3578
  const maxChars = options.maxChars ?? validationPolicy.markdown.maxChars;
@@ -2552,7 +3603,7 @@ README.md directory indexes, clear HTTP-status filenames such as RESPONSE_200_OK
2552
3603
  walk(docs);
2553
3604
  const dirSiblingMap = /* @__PURE__ */ new Map();
2554
3605
  for (const f of markdownFiles) {
2555
- const dir = dirname6(f);
3606
+ const dir = dirname10(f);
2556
3607
  if (!dirSiblingMap.has(dir)) dirSiblingMap.set(dir, []);
2557
3608
  dirSiblingMap.get(dir).push(basename7(f));
2558
3609
  }
@@ -2561,14 +3612,14 @@ README.md directory indexes, clear HTTP-status filenames such as RESPONSE_200_OK
2561
3612
  const docsRel = rel(docs, file);
2562
3613
  const rootRel = rel(root, file);
2563
3614
  if (docsRel && !docsRel.startsWith("..") && !isUpperSnakeMarkdown(name)) addError(file, "FILE_NAMING", `Markdown file must be UPPER_SNAKE_CASE.md or README.md: ${name}`, null, "rename the markdown file to UPPER_SNAKE_CASE.md or README.md and update any links that reference it.");
2564
- const content = readFileSync9(file, "utf8");
3615
+ const content = readFileSync13(file, "utf8");
2565
3616
  fileCache.set(file, content);
2566
3617
  const docArea = docsRel?.split("/")[0];
2567
3618
  if (name !== "README.md" && isUpperSnakeMarkdown(name) && FILENAME_QUALITY_AREAS.has(docArea)) {
2568
3619
  const fp = validationPolicy.markdown?.filename ?? {};
2569
3620
  const allow = fp.allow ?? [];
2570
3621
  if (!allow.some((p) => matchPathPattern(rootRel, p)) && (fp.warnNumberedSeries ?? true)) {
2571
- const siblings = dirSiblingMap.get(dirname6(file)) ?? [];
3622
+ const siblings = dirSiblingMap.get(dirname10(file)) ?? [];
2572
3623
  if (isNumberedSeriesFilename(name, siblings)) {
2573
3624
  const heading = extractFirstHeading(content);
2574
3625
  const suggestion = suggestFilenameFromHeading(heading);
@@ -2610,7 +3661,7 @@ ${traceabilityExample()}`, block.line);
2610
3661
  }
2611
3662
  }
2612
3663
  const byDir = /* @__PURE__ */ new Map();
2613
- for (const file of markdownFiles) byDir.set(dirname6(file), [...byDir.get(dirname6(file)) ?? [], file]);
3664
+ for (const file of markdownFiles) byDir.set(dirname10(file), [...byDir.get(dirname10(file)) ?? [], file]);
2614
3665
  for (const [dir, files] of byDir) {
2615
3666
  if (files.length > 1 && !files.some((file) => basename7(file) === "README.md")) addError(dir, "MISSING_README", "Directory with multiple markdown files must include README.md", null, "add a README.md in this directory that indexes the important markdown files and their purpose.");
2616
3667
  }
@@ -2621,24 +3672,32 @@ ${traceabilityExample()}`, block.line);
2621
3672
  }
2622
3673
  if (options.linkCheck) {
2623
3674
  for (const [file, content] of fileCache) {
2624
- const fileDir = dirname6(file);
3675
+ const fileDir = dirname10(file);
2625
3676
  for (const { href, line } of extractLinks(content)) {
2626
3677
  const hashIndex = href.indexOf("#");
2627
3678
  const pathPart = hashIndex === -1 ? href : href.slice(0, hashIndex);
2628
3679
  const anchor = hashIndex === -1 ? "" : href.slice(hashIndex + 1);
2629
3680
  const target = pathPart ? resolve8(fileDir, pathPart) : file;
2630
- if (pathPart && !existsSync10(target)) {
3681
+ if (pathPart && !existsSync13(target)) {
2631
3682
  addError(file, "BROKEN_LINK", `Local link target does not exist: ${pathPart}`, line, "update the link target to an existing local file, create the intended file, or remove the stale link.");
2632
3683
  continue;
2633
3684
  }
2634
- if (anchor && extname4(target) === ".md") {
2635
- const targetContent = fileCache.get(target) ?? (existsSync10(target) ? readFileSync9(target, "utf8") : "");
3685
+ if (anchor && extname6(target) === ".md") {
3686
+ const targetContent = fileCache.get(target) ?? (existsSync13(target) ? readFileSync13(target, "utf8") : "");
2636
3687
  if (targetContent && !extractAnchors(targetContent).has(decodeURIComponent(anchor))) addError(file, "BROKEN_ANCHOR", `Local anchor target does not exist: ${href}`, line, "update the fragment to a heading that exists in the target markdown file, or add the missing heading.");
2637
3688
  }
2638
3689
  }
2639
3690
  }
2640
3691
  }
2641
3692
  if (options.checkIndex) {
3693
+ let outlines;
3694
+ try {
3695
+ outlines = outlineStatus(root);
3696
+ } catch (error) {
3697
+ outlines = { ok: false, reason: `inspection-unavailable: ${error instanceof Error ? error.message : String(error)}`, paths: [] };
3698
+ }
3699
+ if (!outlines.ok) addError(outlinePath(root), "OUTLINE_INDEX_STALE", `Outline index: ${outlines.reason}${outlines.paths.length ? ` (${outlines.paths.join(", ")})` : ""}`, null, "run `dotdotgod index <root>` to refresh outlines and graph metadata; validation does not parse, embed or repair.");
3700
+ for (const path of outlines.unsupported ?? []) addWarning(join13(root, path), "OUTLINE_LANGUAGE_UNSUPPORTED", "Source language is not supported by the current outline extraction matrix.");
2642
3701
  const index = readIndex(root);
2643
3702
  if (!index) addError(cacheFile(root), "INDEX_MISSING", "Expected .dotdotgod index cache.", null, "run `dotdotgod index <root>` to build the graph index.");
2644
3703
  else {
@@ -2658,20 +3717,20 @@ ${traceabilityExample()}`, block.line);
2658
3717
  }
2659
3718
  if (options.includeLocalMemory) {
2660
3719
  for (const area2 of ["plan", "archive/plan", "archive/report"]) {
2661
- const areaRoot = join8(docs, area2);
2662
- if (!existsSync10(areaRoot)) continue;
2663
- for (const entry of readdirSync2(areaRoot, { withFileTypes: true })) {
3720
+ const areaRoot = join13(docs, area2);
3721
+ if (!existsSync13(areaRoot)) continue;
3722
+ for (const entry of readdirSync3(areaRoot, { withFileTypes: true })) {
2664
3723
  if (!entry.isDirectory()) continue;
2665
- if (!isKebabCase(entry.name)) addError(join8(areaRoot, entry.name), "SLUG_NAMING", `Archive/plan/report slug must be kebab-case: ${entry.name}`, null, "rename the task/report directory to kebab-case and update any README index links that reference it.");
2666
- const readme = join8(areaRoot, entry.name, "README.md");
2667
- if (!existsSync10(readme)) addError(readme, "MISSING_README", `Expected README.md in ${documentationRoot}/${area2}/${entry.name}/`, null, "add a README.md that summarizes the task/report and links any supporting files.");
3724
+ if (!isKebabCase(entry.name)) addError(join13(areaRoot, entry.name), "SLUG_NAMING", `Archive/plan/report slug must be kebab-case: ${entry.name}`, null, "rename the task/report directory to kebab-case and update any README index links that reference it.");
3725
+ const readme = join13(areaRoot, entry.name, "README.md");
3726
+ if (!existsSync13(readme)) addError(readme, "MISSING_README", `Expected README.md in ${documentationRoot}/${area2}/${entry.name}/`, null, "add a README.md that summarizes the task/report and links any supporting files.");
2668
3727
  }
2669
3728
  }
2670
3729
  }
2671
- const gitignore = join8(root, ".gitignore");
2672
- if (!existsSync10(gitignore)) addError(gitignore, "MISSING_GITIGNORE", "Expected .gitignore", null, `create .gitignore and include ${documentationRoot}/plan, ${documentationRoot}/archive, and .dotdotgod entries.`);
3730
+ const gitignore = join13(root, ".gitignore");
3731
+ if (!existsSync13(gitignore)) addError(gitignore, "MISSING_GITIGNORE", "Expected .gitignore", null, `create .gitignore and include ${documentationRoot}/plan, ${documentationRoot}/archive, and .dotdotgod entries.`);
2673
3732
  else {
2674
- const content = readFileSync9(gitignore, "utf8").split("\n").map((line) => line.trim());
3733
+ const content = readFileSync13(gitignore, "utf8").split("\n").map((line) => line.trim());
2675
3734
  for (const required of [`${documentationRoot}/plan`, `${documentationRoot}/archive`, CACHE_DIR]) if (!content.includes(required)) addError(gitignore, "MISSING_GITIGNORE_ENTRY", `Expected .gitignore entry: ${required}`, null, `add ${required} to .gitignore so local plans, archives, and cache files stay untracked.`);
2676
3735
  }
2677
3736
  if (options.json) console.log(JSON.stringify({ ok: errors.length === 0, errors, warnings }, null, 2));
@@ -2696,8 +3755,8 @@ Prompt: ${error.prompt}` : ""}`);
2696
3755
  }
2697
3756
 
2698
3757
  // packages/cli/src/commands/config.mjs
2699
- import { existsSync as existsSync11, statSync as statSync6, writeFileSync as writeFileSync3 } from "node:fs";
2700
- import { join as join9, resolve as resolve9 } from "node:path";
3758
+ import { existsSync as existsSync14, statSync as statSync6, writeFileSync as writeFileSync4 } from "node:fs";
3759
+ import { join as join14, resolve as resolve9 } from "node:path";
2701
3760
  function parseConfigOptions(argv, usageKey = "config") {
2702
3761
  const options = { root: ".", json: false, template: null };
2703
3762
  let rootSet = false;
@@ -2758,17 +3817,17 @@ function runConfig(argv) {
2758
3817
  const isInit = argv[0] === "init";
2759
3818
  const options = parseConfigOptions(isInit ? argv.slice(1) : argv, isInit ? "config init" : "config");
2760
3819
  if (isInit) {
2761
- if (!existsSync11(options.root)) configInitError(options, "ROOT_NOT_FOUND", `Project root not found: ${options.root}`);
3820
+ if (!existsSync14(options.root)) configInitError(options, "ROOT_NOT_FOUND", `Project root not found: ${options.root}`);
2762
3821
  try {
2763
3822
  if (!statSync6(options.root).isDirectory()) configInitError(options, "ROOT_NOT_DIRECTORY", `Project root is not a directory: ${options.root}`);
2764
3823
  } catch {
2765
3824
  configInitError(options, "ROOT_NOT_FOUND", `Project root not found: ${options.root}`);
2766
3825
  }
2767
- const target = join9(options.root, "dotdotgod.config.json");
2768
- if (existsSync11(target)) configInitError(options, "CONFIG_EXISTS", "dotdotgod.config.json already exists.", target);
3826
+ const target = join14(options.root, "dotdotgod.config.json");
3827
+ if (existsSync14(target)) configInitError(options, "CONFIG_EXISTS", "dotdotgod.config.json already exists.", target);
2769
3828
  const template = resolveInitializationTemplate(options.template);
2770
3829
  if (!template.ok) configInitError(options, template.error.code, template.error.message, template.error.path ?? target);
2771
- writeFileSync3(target, template.text);
3830
+ writeFileSync4(target, template.text);
2772
3831
  const payload2 = { ok: true, command: "config init", root: options.root, path: target, created: true, template: { name: template.name, source: template.source, selectedBy: template.selectedBy } };
2773
3832
  if (options.json) console.log(JSON.stringify(payload2, null, 2));
2774
3833
  else console.log(`dotdotgod config init: created ${target} from ${template.source}:${template.name} (${template.selectedBy})`);
@@ -2784,7 +3843,7 @@ function runConfig(argv) {
2784
3843
  errors.push({ file: "embedding config", code: "EMBEDDING_CONFIG_INVALID", message: error instanceof Error ? error.message : String(error) });
2785
3844
  embedding = { source: "invalid", path: null, profile: sanitizeEmbeddingProfile({ provider: "local", model: "Xenova/multilingual-e5-small" }) };
2786
3845
  }
2787
- const payload = { ok: errors.length === 0, command: "config", root: options.root, source: config.source ?? "default", path: config.source && config.source !== "default" ? join9(options.root, config.source) : null, embedding, config: memoryConfigSummary(config), errors };
3846
+ const payload = { ok: errors.length === 0, command: "config", root: options.root, source: config.source ?? "default", path: config.source && config.source !== "default" ? join14(options.root, config.source) : null, embedding, config: memoryConfigSummary(config), errors };
2788
3847
  if (options.json) console.log(JSON.stringify(payload, null, 2));
2789
3848
  else console.log(formatConfigOutput(payload));
2790
3849
  if (errors.length > 0) process.exit(1);
@@ -2793,102 +3852,12 @@ function runConfig(argv) {
2793
3852
  // packages/cli/src/commands/query.mjs
2794
3853
  import { resolve as resolve10 } from "node:path";
2795
3854
 
2796
- // packages/cli/src/query/chunks.mjs
2797
- import { readFileSync as readFileSync10, readdirSync as readdirSync3 } from "node:fs";
2798
- import { join as join10, relative as relative4 } from "node:path";
2799
- import { createHash as createHash3 } from "node:crypto";
2800
- var MAX_CHUNK_CHARS = 1600;
2801
- var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", ".git", ".dotdotgod"]);
2802
- function textFingerprint(text) {
2803
- return createHash3("sha256").update(text).digest("hex");
2804
- }
2805
- function collectDocumentationMarkdown(root, exclude = ["docs/plan", "docs/archive"], documentationRoot = "docs") {
2806
- const docsRoot = join10(root, documentationRoot);
2807
- const files = [];
2808
- const walk = (directory) => {
2809
- let entries;
2810
- try {
2811
- entries = readdirSync3(directory, { withFileTypes: true, encoding: "utf8" });
2812
- } catch {
2813
- return;
2814
- }
2815
- entries.sort((a, b) => a.name.localeCompare(b.name));
2816
- for (const entry of entries) {
2817
- if (entry.name.startsWith(".")) continue;
2818
- const absolute = join10(directory, entry.name);
2819
- const path = relative4(root, absolute).replaceAll("\\", "/");
2820
- if (isSecretLikePathPattern(path) || exclude.some((pattern) => matchPathPattern(path, pattern) || path === pattern || path.startsWith(`${pattern}/`))) continue;
2821
- if (entry.isDirectory()) {
2822
- if (!SKIPPED_DIRECTORIES.has(entry.name)) walk(absolute);
2823
- } else if (entry.isFile() && entry.name.toLowerCase().endsWith(".md")) files.push(path);
2824
- }
2825
- };
2826
- walk(docsRoot);
2827
- return files.sort();
2828
- }
2829
- function splitSection(text, maxChars = MAX_CHUNK_CHARS) {
2830
- if (text.length <= maxChars) return [text];
2831
- const paragraphs = text.split(/\n\s*\n/);
2832
- const chunks = [];
2833
- let current = "";
2834
- for (const paragraph of paragraphs) {
2835
- if (current && current.length + paragraph.length + 2 > maxChars) {
2836
- chunks.push(current.trim());
2837
- current = "";
2838
- }
2839
- if (paragraph.length > maxChars) {
2840
- for (let offset = 0; offset < paragraph.length; offset += maxChars) chunks.push(paragraph.slice(offset, offset + maxChars).trim());
2841
- } else current += `${current ? "\n\n" : ""}${paragraph}`;
2842
- }
2843
- if (current.trim()) chunks.push(current.trim());
2844
- return chunks.filter(Boolean);
2845
- }
2846
- function chunkMarkdown(path, content, maxChars = MAX_CHUNK_CHARS) {
2847
- const lines = content.split("\n");
2848
- const sections = [];
2849
- let headings = [];
2850
- let body = [];
2851
- const flush = () => {
2852
- const text = body.join("\n").trim();
2853
- if (!text && headings.length === 0) return;
2854
- const heading = headings.length > 0 ? headings.join(" > ") : path;
2855
- const pieces = splitSection(text || heading, maxChars);
2856
- pieces.forEach((piece, index) => {
2857
- const passage = `Path: ${path}
2858
- Section: ${heading}
2859
-
2860
- ${piece}`;
2861
- const fingerprint2 = textFingerprint(passage);
2862
- sections.push({ id: `${path}#${fingerprint2.slice(0, 16)}`, path, heading, part: index + 1, text: piece, passage, fingerprint: fingerprint2 });
2863
- });
2864
- };
2865
- for (const line of lines) {
2866
- const match = /^(#{1,6})\s+(.+?)\s*$/.exec(line);
2867
- if (!match) {
2868
- body.push(line);
2869
- continue;
2870
- }
2871
- flush();
2872
- body = [];
2873
- const level = match[1].length;
2874
- headings = [...headings.slice(0, level - 1), match[2]];
2875
- }
2876
- flush();
2877
- return sections;
2878
- }
2879
- function collectDocumentationChunks(root, exclude, documentationRoot = "docs") {
2880
- return collectDocumentationMarkdown(root, exclude, documentationRoot).flatMap((path) => {
2881
- const content = readFileSync10(join10(root, path), "utf8");
2882
- return chunkMarkdown(path, content);
2883
- });
2884
- }
2885
-
2886
3855
  // packages/cli/src/query/embedding-runtime.mjs
2887
- import { existsSync as existsSync12, mkdirSync as mkdirSync3, readFileSync as readFileSync11, rmSync, writeFileSync as writeFileSync4 } from "node:fs";
3856
+ import { existsSync as existsSync15, mkdirSync as mkdirSync4, readFileSync as readFileSync14, rmSync, writeFileSync as writeFileSync5 } from "node:fs";
2888
3857
  import { spawnSync as spawnSync2 } from "node:child_process";
2889
- import { createRequire } from "node:module";
3858
+ import { createRequire as createRequire2 } from "node:module";
2890
3859
  import { homedir as homedir3 } from "node:os";
2891
- import { join as join11 } from "node:path";
3860
+ import { join as join15 } from "node:path";
2892
3861
  import { pathToFileURL } from "node:url";
2893
3862
  var EMBEDDING_RUNTIME_PACKAGE = "@huggingface/transformers";
2894
3863
  var EMBEDDING_RUNTIME_VERSION = "4.2.0";
@@ -2900,13 +3869,13 @@ var EmbeddingRuntimeMissingError = class extends Error {
2900
3869
  }
2901
3870
  };
2902
3871
  function embeddingRuntimeRoot(options = {}) {
2903
- return join11(options.home ?? homedir3(), ".dotdotgod", "runtime", "embedding");
3872
+ return join15(options.home ?? homedir3(), ".dotdotgod", "runtime", "embedding");
2904
3873
  }
2905
3874
  function resolvePersistentTransformers(options = {}) {
2906
3875
  const root = embeddingRuntimeRoot(options);
2907
3876
  try {
2908
- const require2 = createRequire(join11(root, "package.json"));
2909
- return require2.resolve(EMBEDDING_RUNTIME_PACKAGE);
3877
+ const require3 = createRequire2(join15(root, "package.json"));
3878
+ return require3.resolve(EMBEDDING_RUNTIME_PACKAGE);
2910
3879
  } catch {
2911
3880
  throw new EmbeddingRuntimeMissingError();
2912
3881
  }
@@ -2914,10 +3883,10 @@ function resolvePersistentTransformers(options = {}) {
2914
3883
  function embeddingRuntimeStatus(options = {}) {
2915
3884
  const root = embeddingRuntimeRoot(options);
2916
3885
  try {
2917
- const require2 = createRequire(join11(root, "package.json"));
2918
- require2.resolve(EMBEDDING_RUNTIME_PACKAGE);
2919
- const packagePath = join11(root, "node_modules", ...EMBEDDING_RUNTIME_PACKAGE.split("/"), "package.json");
2920
- const version = JSON.parse(readFileSync11(packagePath, "utf8")).version;
3886
+ const require3 = createRequire2(join15(root, "package.json"));
3887
+ require3.resolve(EMBEDDING_RUNTIME_PACKAGE);
3888
+ const packagePath = join15(root, "node_modules", ...EMBEDDING_RUNTIME_PACKAGE.split("/"), "package.json");
3889
+ const version = JSON.parse(readFileSync14(packagePath, "utf8")).version;
2921
3890
  return { ok: true, installed: true, installAvailable: true, package: EMBEDDING_RUNTIME_PACKAGE, requiredVersion: EMBEDDING_RUNTIME_VERSION, packageVersion: version, location: root };
2922
3891
  } catch {
2923
3892
  return { ok: true, installed: false, installAvailable: true, package: EMBEDDING_RUNTIME_PACKAGE, requiredVersion: EMBEDDING_RUNTIME_VERSION, packageVersion: null, location: root };
@@ -2926,10 +3895,10 @@ function embeddingRuntimeStatus(options = {}) {
2926
3895
  function installEmbeddingRuntime(options = {}) {
2927
3896
  if (options.confirm !== true) throw new Error("Embedding runtime installation requires explicit confirmation.");
2928
3897
  const root = embeddingRuntimeRoot(options);
2929
- const lock = join11(root, ".installing");
2930
- if (existsSync12(lock)) throw new Error("Embedding runtime installation is already in progress.");
2931
- mkdirSync3(root, { recursive: true });
2932
- writeFileSync4(lock, `${process.pid}
3898
+ const lock = join15(root, ".installing");
3899
+ if (existsSync15(lock)) throw new Error("Embedding runtime installation is already in progress.");
3900
+ mkdirSync4(root, { recursive: true });
3901
+ writeFileSync5(lock, `${process.pid}
2933
3902
  `, { flag: "wx" });
2934
3903
  try {
2935
3904
  const spawn = options.spawnImpl ?? spawnSync2;
@@ -3029,13 +3998,13 @@ async function resolveEmbedder(root, options = {}) {
3029
3998
  }
3030
3999
 
3031
4000
  // packages/cli/src/query/store.mjs
3032
- import { createHash as createHash4 } from "node:crypto";
3033
- import { existsSync as existsSync13, mkdirSync as mkdirSync4, readFileSync as readFileSync12, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync5 } from "node:fs";
3034
- import { join as join12 } from "node:path";
4001
+ import { createHash as createHash6 } from "node:crypto";
4002
+ import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync15, renameSync as renameSync2, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
4003
+ import { join as join16 } from "node:path";
3035
4004
  var VECTOR_SCHEMA_VERSION = 2;
3036
4005
  var VECTOR_DIMENSIONS = 384;
3037
- function profileFingerprint(identity) {
3038
- return createHash4("sha256").update(JSON.stringify(identity)).digest("hex");
4006
+ function profileFingerprint(identity2) {
4007
+ return createHash6("sha256").update(JSON.stringify(identity2)).digest("hex");
3039
4008
  }
3040
4009
  function isValidNormalizedVectorValues(values, dimensions = VECTOR_DIMENSIONS) {
3041
4010
  if (!Number.isInteger(dimensions) || dimensions < 1 || !values || values.length % dimensions !== 0) return false;
@@ -3051,18 +4020,18 @@ function isValidNormalizedVectorValues(values, dimensions = VECTOR_DIMENSIONS) {
3051
4020
  return true;
3052
4021
  }
3053
4022
  function vectorCachePaths(root) {
3054
- const directory = join12(root, ".dotdotgod", "vectors");
3055
- return { directory, manifest: join12(directory, "manifest.json"), chunks: join12(directory, "chunks.jsonl"), embeddings: join12(directory, "embeddings.f32") };
4023
+ const directory = join16(root, ".dotdotgod", "vectors");
4024
+ return { directory, manifest: join16(directory, "manifest.json"), chunks: join16(directory, "chunks.jsonl"), embeddings: join16(directory, "embeddings.f32") };
3056
4025
  }
3057
- function readVectorCache(root, identity = null) {
4026
+ function readVectorCache(root, identity2 = null) {
3058
4027
  const paths = vectorCachePaths(root);
3059
- if (!existsSync13(paths.manifest) || !existsSync13(paths.chunks) || !existsSync13(paths.embeddings)) return null;
4028
+ if (!existsSync16(paths.manifest) || !existsSync16(paths.chunks) || !existsSync16(paths.embeddings)) return null;
3060
4029
  try {
3061
- const manifest = JSON.parse(readFileSync12(paths.manifest, "utf8"));
4030
+ const manifest = JSON.parse(readFileSync15(paths.manifest, "utf8"));
3062
4031
  if (manifest.schemaVersion !== VECTOR_SCHEMA_VERSION || !Number.isInteger(manifest.dimensions) || manifest.dimensions < 1) return null;
3063
- if (identity && manifest.profileFingerprint !== profileFingerprint(identity)) return null;
3064
- const chunks = readFileSync12(paths.chunks, "utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line));
3065
- const buffer = readFileSync12(paths.embeddings);
4032
+ if (identity2 && manifest.profileFingerprint !== profileFingerprint(identity2)) return null;
4033
+ const chunks = readFileSync15(paths.chunks, "utf8").split("\n").filter(Boolean).map((line) => JSON.parse(line));
4034
+ const buffer = readFileSync15(paths.embeddings);
3066
4035
  if (buffer.byteLength !== chunks.length * manifest.dimensions * 4) return null;
3067
4036
  const vectors = new Float32Array(buffer.buffer, buffer.byteOffset, buffer.byteLength / 4);
3068
4037
  if (!isValidNormalizedVectorValues(vectors, manifest.dimensions)) return null;
@@ -3073,14 +4042,14 @@ function readVectorCache(root, identity = null) {
3073
4042
  }
3074
4043
  function atomicWrite(path, data) {
3075
4044
  const temporary = `${path}.tmp-${process.pid}`;
3076
- writeFileSync5(temporary, data);
3077
- renameSync(temporary, path);
4045
+ writeFileSync6(temporary, data);
4046
+ renameSync2(temporary, path);
3078
4047
  }
3079
4048
  function writeVectorCache(root, chunks, vectors, metadata = {}) {
3080
4049
  const paths = vectorCachePaths(root);
3081
4050
  const dimensions = metadata.dimensions;
3082
4051
  if (!Number.isInteger(dimensions) || dimensions < 1) throw new Error("Vector dimensions must be a positive integer.");
3083
- mkdirSync4(paths.directory, { recursive: true });
4052
+ mkdirSync5(paths.directory, { recursive: true });
3084
4053
  if (vectors.length !== chunks.length * dimensions) throw new Error("Vector count does not match chunk metadata.");
3085
4054
  if (!isValidNormalizedVectorValues(vectors, dimensions)) throw new Error("Vectors must contain finite normalized values.");
3086
4055
  const manifest = { schemaVersion: VECTOR_SCHEMA_VERSION, chunks: chunks.length, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), ...metadata };
@@ -3092,17 +4061,85 @@ function writeVectorCache(root, chunks, vectors, metadata = {}) {
3092
4061
  return manifest;
3093
4062
  }
3094
4063
 
4064
+ // packages/cli/src/query/hybrid.mjs
4065
+ var resultKey = (chunk) => chunk.kind === "symbol" ? chunk.id : chunk.path;
4066
+ var terms = (text) => [...new Set(text.replace(/([a-z])([A-Z])/g, "$1 $2").toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(Boolean))];
4067
+ function keywordCandidates(query, chunks) {
4068
+ const words = terms(query);
4069
+ if (!words.length) return [];
4070
+ const literal = query.toLowerCase();
4071
+ return chunks.map((chunk) => {
4072
+ const names = `${chunk.qualifiedName ?? ""} ${chunk.heading ?? ""} ${chunk.path}`.toLowerCase();
4073
+ const body = `${chunk.signature ?? ""} ${chunk.documentation ?? chunk.text}`.toLowerCase();
4074
+ const haystack = new Set(terms(`${names} ${body}`));
4075
+ const matched = words.filter((word) => haystack.has(word)).length;
4076
+ const exact = chunk.qualifiedName?.toLowerCase() === literal || chunk.name?.toLowerCase() === literal;
4077
+ return { ...chunk, exactNameMatch: Boolean(exact), keywordScore: exact ? 3 : matched / words.length + (names.includes(literal) ? 0.5 : body.includes(literal) ? 0.25 : 0) };
4078
+ }).filter((chunk) => chunk.keywordScore > 0).sort((a, b) => b.keywordScore - a.keywordScore || a.id.localeCompare(b.id));
4079
+ }
4080
+ function fuseCandidates(keyword, vector, limit) {
4081
+ const candidates = /* @__PURE__ */ new Map();
4082
+ for (const [channel, rows] of [["keyword", keyword], ["vector", vector]]) {
4083
+ const seenIds = /* @__PURE__ */ new Set();
4084
+ let rank = 0;
4085
+ rows.forEach((chunk) => {
4086
+ if (seenIds.has(chunk.id)) return;
4087
+ seenIds.add(chunk.id);
4088
+ rank += 1;
4089
+ const existing = candidates.get(chunk.id) ?? { ...chunk, score: 0, retrieval: [] };
4090
+ existing.score += 1 / (60 + rank);
4091
+ existing.retrieval.push(channel);
4092
+ if (chunk.vectorScore !== void 0) existing.vectorScore = chunk.vectorScore;
4093
+ if (chunk.keywordScore !== void 0) existing.keywordScore = chunk.keywordScore;
4094
+ candidates.set(chunk.id, existing);
4095
+ });
4096
+ }
4097
+ const seen = /* @__PURE__ */ new Set();
4098
+ return [...candidates.values()].sort((a, b) => Number(b.exactNameMatch ?? false) - Number(a.exactNameMatch ?? false) || b.score - a.score || a.id.localeCompare(b.id)).filter((chunk) => {
4099
+ const key = resultKey(chunk);
4100
+ if (seen.has(key)) return false;
4101
+ seen.add(key);
4102
+ return true;
4103
+ }).slice(0, limit);
4104
+ }
4105
+ async function projectCorpus(root, scope) {
4106
+ const config = readMemoryConfig(root);
4107
+ const docs = scope === "code" ? [] : collectDocumentationChunks(root, config.load?.documentationSummary?.exclude, config.documentation?.root ?? "docs").map((chunk) => ({ ...chunk, kind: "document" }));
4108
+ const outlines = scope === "docs" ? null : await buildOutlines(root);
4109
+ return { chunks: [...docs, ...outlines ? outlineChunks(outlines) : []], outlines };
4110
+ }
4111
+ function assertCodeEmbeddingConsent(root, options, chunks) {
4112
+ if (!chunks.some((chunk) => chunk.kind === "symbol")) return;
4113
+ const profile = options.profile ?? resolveEmbeddingProfile(root).profile;
4114
+ if (profile.provider !== "local" && options.allowCodeEmbedding !== true) throw new Error("Remote code-metadata embedding requires --allow-code-embedding; use --search keyword or --scope docs instead.");
4115
+ }
4116
+ function attachRelationships(results, outlines) {
4117
+ if (!outlines) return results;
4118
+ const graph = addOutlineGraph({ nodes: [], edges: [] }, outlines);
4119
+ return results.map((result) => {
4120
+ const relationships = graph.edges.filter((edge) => edge.source === result.id || edge.target === result.id || edge.source === `file:${result.path}` && edge.relation === "imports");
4121
+ return { ...result, relationshipCount: relationships.length, relationships: relationships.slice(0, 10) };
4122
+ });
4123
+ }
4124
+
3095
4125
  // packages/cli/src/commands/query.mjs
3096
4126
  var DEFAULT_LIMIT = 30;
3097
4127
  var MAX_LIMIT = 100;
3098
4128
  var EMBED_BATCH_SIZE = 16;
3099
4129
  function parseQueryOptions(argv) {
3100
- const options = { root: ".", query: "", limit: DEFAULT_LIMIT, json: false };
4130
+ const options = { root: ".", query: "", limit: DEFAULT_LIMIT, json: false, scope: "all", search: "hybrid", allowCodeEmbedding: false };
3101
4131
  let rootSet = false;
3102
4132
  const queryParts = [];
3103
4133
  for (let i = 0; i < argv.length; i += 1) {
3104
4134
  const arg = argv[i];
3105
4135
  if (arg === "--json") options.json = true;
4136
+ else if (arg === "--scope") {
4137
+ options.scope = argv[++i];
4138
+ if (!["docs", "code", "all"].includes(options.scope)) usage("--scope must be docs, code or all.", "query");
4139
+ } else if (arg === "--search") {
4140
+ options.search = argv[++i];
4141
+ if (!["hybrid", "keyword", "vector"].includes(options.search)) usage("--search must be hybrid, keyword or vector.", "query");
4142
+ } else if (arg === "--allow-code-embedding") options.allowCodeEmbedding = true;
3106
4143
  else if (arg === "--limit") {
3107
4144
  const value = Number(argv[++i]);
3108
4145
  if (!Number.isInteger(value) || value < 1 || value > MAX_LIMIT) usage(`--limit must be an integer from 1 to ${MAX_LIMIT}.`, "query");
@@ -3141,20 +4178,12 @@ function rankVectorFiles(queryVector, index) {
3141
4178
  }
3142
4179
  return results;
3143
4180
  }
3144
- function lexicalBoost(query, chunk) {
3145
- const terms = new Set(query.toLowerCase().split(/[^\p{L}\p{N}_.:/-]+/u).filter((term) => term.length > 1));
3146
- if (!terms.size) return 0;
3147
- const haystack = `${chunk.path} ${chunk.heading}`.toLowerCase();
3148
- let matches = 0;
3149
- for (const term of terms) if (haystack.includes(term)) matches += 1;
3150
- return Math.min(0.08, matches / terms.size * 0.08);
3151
- }
3152
4181
  async function buildVectorIndex(root, embedOrOptions = {}) {
3153
4182
  const options = typeof embedOrOptions === "function" ? { embed: embedOrOptions } : embedOrOptions;
3154
4183
  const resolved = await resolveEmbedder(root, options);
3155
4184
  const config = readMemoryConfig(root);
3156
4185
  const exclude = config.load?.documentationSummary?.exclude ?? ["docs/plan", "docs/archive"];
3157
- const chunks = collectDocumentationChunks(root, exclude, config.documentation?.root ?? "docs");
4186
+ const chunks = options.chunks ?? collectDocumentationChunks(root, exclude, config.documentation?.root ?? "docs");
3158
4187
  const cached = readVectorCache(root, resolved.identity);
3159
4188
  const cachedOffsets = new Map((cached?.chunks ?? []).map((chunk, index) => [chunk.fingerprint, index]));
3160
4189
  const rows = new Array(chunks.length);
@@ -3188,19 +4217,49 @@ async function buildVectorIndex(root, embedOrOptions = {}) {
3188
4217
  const manifest = writeVectorCache(root, storedChunks, vectors, { provider: resolved.profile.provider, model: resolved.profile.model, dimensions, profileFingerprint: profileFingerprint(resolved.identity), excluded: exclude, embedded: missing.length, reused });
3189
4218
  return { chunks: storedChunks, vectors, manifest, embedding: resolved };
3190
4219
  }
3191
- async function queryDocumentation(root, query, options = {}) {
4220
+ async function queryProject(root, query, options = {}) {
4221
+ const scope = options.scope ?? "all";
4222
+ const search = options.search ?? "hybrid";
3192
4223
  const limit = options.limit ?? DEFAULT_LIMIT;
3193
- const index = await buildVectorIndex(root, options);
3194
- const [queryVector] = await index.embedding.embed([`query: ${query}`]);
3195
- const dimensions = index.manifest.dimensions;
3196
- assertVector(queryVector, dimensions, "Query vector");
3197
- const results = rankVectorFiles(queryVector, index).map((chunk) => ({ ...chunk, score: Number((chunk.vectorScore + lexicalBoost(query, chunk)).toFixed(6)), vectorScore: Number(chunk.vectorScore.toFixed(6)) })).sort((a, b) => b.score - a.score || a.path.localeCompare(b.path)).slice(0, limit);
3198
- return { ok: true, command: "query", root, query, provider: index.embedding.profile.provider, model: index.embedding.profile.model, embeddingSource: index.embedding.source, dimensions, limit, index: index.manifest, results };
4224
+ const corpus = await projectCorpus(root, scope);
4225
+ const keyword = search === "vector" ? [] : keywordCandidates(query, corpus.chunks);
4226
+ let vector = [];
4227
+ let index;
4228
+ let vectorError;
4229
+ if (search !== "keyword" && corpus.chunks.length) {
4230
+ try {
4231
+ assertCodeEmbeddingConsent(root, options, corpus.chunks);
4232
+ index = await buildVectorIndex(root, { ...options, chunks: corpus.chunks });
4233
+ const [queryVector] = await index.embedding.embed([`query: ${query}`]);
4234
+ vector = index.chunks.map((chunk, position) => ({ ...chunk, vectorScore: cosineScore(queryVector, index.vectors, position * index.manifest.dimensions, index.manifest.dimensions) })).sort((a, b) => b.vectorScore - a.vectorScore || a.id.localeCompare(b.id));
4235
+ } catch (error) {
4236
+ if (search === "vector") throw error;
4237
+ vectorError = error instanceof Error ? error.message : String(error);
4238
+ }
4239
+ }
4240
+ const results = attachRelationships(fuseCandidates(keyword, vector, limit), corpus.outlines).map(({ passage, ...result }) => result);
4241
+ return {
4242
+ ok: true,
4243
+ command: "query",
4244
+ root,
4245
+ query,
4246
+ scope,
4247
+ search,
4248
+ limit,
4249
+ provider: index?.embedding.profile.provider ?? null,
4250
+ model: index?.embedding.profile.model ?? null,
4251
+ embeddingSource: index?.embedding.source ?? "not-used",
4252
+ dimensions: index?.manifest.dimensions ?? null,
4253
+ index: index?.manifest ?? null,
4254
+ outlines: corpus.outlines ? { refresh: corpus.outlines.refresh, indexedFiles: corpus.outlines.files.length, diagnostics: corpus.outlines.files.filter((file) => file.status !== "parsed").map(({ path, status }) => ({ path, status })) } : null,
4255
+ warnings: [...vectorError ? [`Vector retrieval unavailable; keyword results only: ${vectorError}`] : [], ...corpus.outlines?.files.filter((file) => file.status !== "parsed").map((file) => `Outline ${file.status}: ${file.path}`) ?? []],
4256
+ results
4257
+ };
3199
4258
  }
3200
4259
  function formatQueryResult(payload) {
3201
- const lines = [`dotdotgod query: ${payload.query}`, `- embedding: ${payload.provider}/${payload.model} (${payload.embeddingSource})`, `- results: ${payload.results.length}`];
4260
+ const lines = [`dotdotgod query: ${payload.query}`, `- embedding: ${payload.provider ? `${payload.provider}/${payload.model}` : "not used"} (${payload.embeddingSource})`, `- results: ${payload.results.length}`, ...payload.warnings ?? []];
3202
4261
  payload.results.forEach((result, index) => {
3203
- lines.push(`${index + 1}. ${result.path}${result.heading ? ` \u2014 ${result.heading}` : ""} (${result.score.toFixed(3)})`);
4262
+ lines.push(`${index + 1}. ${result.path}${result.startLine ? `:${result.startLine}\u2013${result.endLine}` : ""}${result.heading ? ` \u2014 ${result.heading}` : ""} (${result.score.toFixed(3)})`);
3204
4263
  const excerpt = result.text.replace(/\s+/g, " ").trim().slice(0, 240);
3205
4264
  if (excerpt) lines.push(` ${excerpt}${result.text.length > 240 ? "\u2026" : ""}`);
3206
4265
  });
@@ -3209,7 +4268,7 @@ function formatQueryResult(payload) {
3209
4268
  async function runQuery(argv) {
3210
4269
  const options = parseQueryOptions(argv);
3211
4270
  try {
3212
- const payload = await queryDocumentation(options.root, options.query, options);
4271
+ const payload = await queryProject(options.root, options.query, options);
3213
4272
  console.log(options.json ? JSON.stringify(payload, null, 2) : formatQueryResult(payload));
3214
4273
  } catch (error) {
3215
4274
  const message = `dotdotgod query failed: ${error instanceof Error ? error.message : String(error)}`;
@@ -3255,8 +4314,8 @@ function runEmbedding(argv, options = {}) {
3255
4314
  import { resolve as resolve13 } from "node:path";
3256
4315
 
3257
4316
  // packages/cli/src/memory/documentation-map.mjs
3258
- import { existsSync as existsSync14, readdirSync as readdirSync4, statSync as statSync7 } from "node:fs";
3259
- import { join as join13, resolve as resolve12 } from "node:path";
4317
+ import { existsSync as existsSync17, readdirSync as readdirSync4, statSync as statSync7 } from "node:fs";
4318
+ import { join as join17, resolve as resolve12 } from "node:path";
3260
4319
  var SKIPPED_DIRECTORIES2 = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", ".git", ".dotdotgod"]);
3261
4320
  function node() {
3262
4321
  return { directories: /* @__PURE__ */ new Map(), files: [] };
@@ -3319,7 +4378,7 @@ function discoverMarkdown(root, documentationRoot, exclude) {
3319
4378
  const walk = (relativeDirectory) => {
3320
4379
  let entries;
3321
4380
  try {
3322
- entries = readdirSync4(join13(root, relativeDirectory), { withFileTypes: true, encoding: "utf8" });
4381
+ entries = readdirSync4(join17(root, relativeDirectory), { withFileTypes: true, encoding: "utf8" });
3323
4382
  } catch {
3324
4383
  return;
3325
4384
  }
@@ -3337,7 +4396,7 @@ function discoverMarkdown(root, documentationRoot, exclude) {
3337
4396
  }
3338
4397
  function buildDocumentationMap(projectRoot = ".", { depth = 5 } = {}) {
3339
4398
  const root = resolve12(projectRoot);
3340
- if (!existsSync14(root)) return { ok: false, error: { code: "ROOT_NOT_FOUND", message: `Project root not found: ${root}` } };
4399
+ if (!existsSync17(root)) return { ok: false, error: { code: "ROOT_NOT_FOUND", message: `Project root not found: ${root}` } };
3341
4400
  try {
3342
4401
  if (!statSync7(root).isDirectory()) return { ok: false, error: { code: "ROOT_NOT_FOUND", message: `Project root not found: ${root}` } };
3343
4402
  } catch {
@@ -4768,8 +5827,8 @@ function compareImpactItems(seeds) {
4768
5827
  }
4769
5828
 
4770
5829
  // packages/cli/src/impact/references.mjs
4771
- import { readFileSync as readFileSync13, statSync as statSync8 } from "node:fs";
4772
- import { extname as extname5, resolve as resolve14 } from "node:path";
5830
+ import { readFileSync as readFileSync16, statSync as statSync8 } from "node:fs";
5831
+ import { extname as extname7, resolve as resolve14 } from "node:path";
4773
5832
  var WARNING_LIMIT = 20;
4774
5833
  var edgeKey = (source, target, relation) => `${source}\0${target}\0${relation}`;
4775
5834
  function prepareImpactReferences(index, root, changedPaths) {
@@ -4784,7 +5843,7 @@ function prepareImpactReferences(index, root, changedPaths) {
4784
5843
  const absolute = resolve14(root, path);
4785
5844
  const stat = statSync8(absolute);
4786
5845
  result = { status: "exists" };
4787
- if (stat.isFile() && extname5(absolute) === ".md") result.anchors = extractAnchors(readFileSync13(absolute, "utf8"));
5846
+ if (stat.isFile() && extname7(absolute) === ".md") result.anchors = extractAnchors(readFileSync16(absolute, "utf8"));
4788
5847
  } catch (error) {
4789
5848
  result = { status: ["ENOENT", "ENOTDIR"].includes(error.code) ? "missing" : "unavailable" };
4790
5849
  }
@@ -4846,8 +5905,8 @@ function prepareImpactReferences(index, root, changedPaths) {
4846
5905
  }
4847
5906
 
4848
5907
  // packages/cli/src/impact/vector-profile.mjs
4849
- import { lstatSync, readFileSync as readFileSync14, realpathSync } from "node:fs";
4850
- import { extname as extname6, relative as relative5, resolve as resolve15, sep } from "node:path";
5908
+ import { lstatSync as lstatSync3, readFileSync as readFileSync17, realpathSync as realpathSync2 } from "node:fs";
5909
+ import { extname as extname8, relative as relative6, resolve as resolve15, sep } from "node:path";
4851
5910
  var MAX_VECTOR_PROFILE_FILE_BYTES = 64 * 1024;
4852
5911
  var MAX_VECTOR_PROFILE_CHARS = 4e3;
4853
5912
  var MAX_VECTOR_PROFILE_METADATA_ITEMS = 20;
@@ -4869,13 +5928,13 @@ function canonicalizeChangedPath(root, path) {
4869
5928
  const absolute = resolve15(rootAbsolute, normalized);
4870
5929
  if (!insideRoot(rootAbsolute, absolute)) return normalized;
4871
5930
  try {
4872
- const canonicalRoot = realpathSync(rootAbsolute);
4873
- const canonicalAbsolute = realpathSync(absolute);
5931
+ const canonicalRoot = realpathSync2(rootAbsolute);
5932
+ const canonicalAbsolute = realpathSync2(absolute);
4874
5933
  if (!insideRoot(canonicalRoot, canonicalAbsolute)) return normalized;
4875
- const canonical = relative5(canonicalRoot, canonicalAbsolute).replaceAll("\\", "/");
5934
+ const canonical = relative6(canonicalRoot, canonicalAbsolute).replaceAll("\\", "/");
4876
5935
  return canonical || normalized;
4877
5936
  } catch {
4878
- return relative5(rootAbsolute, absolute).replaceAll("\\", "/") || normalized;
5937
+ return relative6(rootAbsolute, absolute).replaceAll("\\", "/") || normalized;
4879
5938
  }
4880
5939
  }
4881
5940
  function unsafeProfilePath(path) {
@@ -4895,17 +5954,17 @@ function graphMetadata(graph, seedId) {
4895
5954
  return values;
4896
5955
  }
4897
5956
  function safeTextPrefix(root, path) {
4898
- if (unsafeProfilePath(path) || !TEXT_EXTENSIONS.has(extname6(path).toLowerCase())) return "";
5957
+ if (unsafeProfilePath(path) || !TEXT_EXTENSIONS.has(extname8(path).toLowerCase())) return "";
4899
5958
  try {
4900
5959
  const absolute = resolve15(root, path);
4901
5960
  if (!insideRoot(root, absolute)) return "";
4902
- const canonicalAbsolute = realpathSync(absolute);
4903
- if (!insideRoot(realpathSync(root), canonicalAbsolute)) return "";
4904
- const canonicalPath = relative5(realpathSync(root), canonicalAbsolute).replaceAll("\\", "/");
4905
- if (canonicalPath !== path || unsafeProfilePath(canonicalPath) || !TEXT_EXTENSIONS.has(extname6(canonicalPath).toLowerCase())) return "";
4906
- const stats = lstatSync(canonicalAbsolute);
5961
+ const canonicalAbsolute = realpathSync2(absolute);
5962
+ if (!insideRoot(realpathSync2(root), canonicalAbsolute)) return "";
5963
+ const canonicalPath = relative6(realpathSync2(root), canonicalAbsolute).replaceAll("\\", "/");
5964
+ if (canonicalPath !== path || unsafeProfilePath(canonicalPath) || !TEXT_EXTENSIONS.has(extname8(canonicalPath).toLowerCase())) return "";
5965
+ const stats = lstatSync3(canonicalAbsolute);
4907
5966
  if (!stats.isFile() || stats.isSymbolicLink() || stats.size > MAX_VECTOR_PROFILE_FILE_BYTES) return "";
4908
- const buffer = readFileSync14(canonicalAbsolute);
5967
+ const buffer = readFileSync17(canonicalAbsolute);
4909
5968
  if (buffer.includes(0)) return "";
4910
5969
  const text = new TextDecoder("utf-8", { fatal: true }).decode(buffer);
4911
5970
  return text.slice(0, MAX_VECTOR_PROFILE_CHARS);
@@ -5125,7 +6184,7 @@ function buildCompactImpactReport(impact, limits = {}) {
5125
6184
  }
5126
6185
 
5127
6186
  // packages/cli/src/reference/extract.mjs
5128
- import { basename as basename8, dirname as dirname7, extname as extname7 } from "node:path";
6187
+ import { basename as basename8, dirname as dirname11, extname as extname9 } from "node:path";
5129
6188
  var DEFAULT_REFERENCE_LIMIT = 5;
5130
6189
  function extractBracketReferences(prompt = "") {
5131
6190
  const refs = [];
@@ -5170,13 +6229,13 @@ function isArchiveBodyPath(path = "", memoryConfig) {
5170
6229
  function aliasEntriesForPath(path = "") {
5171
6230
  const entries = [];
5172
6231
  const base = basename8(path);
5173
- const ext = extname7(base);
6232
+ const ext = extname9(base);
5174
6233
  const stem = ext ? base.slice(0, -ext.length) : base;
5175
6234
  const withoutMd = path.replace(/\.md$/i, "");
5176
6235
  const parts = withoutMd.split("/").filter(Boolean);
5177
6236
  for (let i = 0; i < parts.length; i += 1) entries.push({ alias: parts.slice(i).join("/"), kind: "path-suffix" });
5178
6237
  if (base === "README.md") {
5179
- const dir = dirname7(path).replace(/\\/g, "/");
6238
+ const dir = dirname11(path).replace(/\\/g, "/");
5180
6239
  entries.push({ alias: basename8(dir), kind: "path" });
5181
6240
  }
5182
6241
  for (const alias of [path, withoutMd, base, stem]) entries.push({ alias, kind: "path" });
@@ -5187,7 +6246,7 @@ function referenceCandidateAliases(node2) {
5187
6246
  const entries = [];
5188
6247
  if (node2.type === "file" && path) entries.push(...aliasEntriesForPath(path));
5189
6248
  if (node2.type === "heading") {
5190
- const fileStem = path ? basename8(path, extname7(path)) : "";
6249
+ const fileStem = path ? basename8(path, extname9(path)) : "";
5191
6250
  const anchor = typeof node2.id === "string" && node2.id.includes("#") ? node2.id.split("#").pop() : "";
5192
6251
  for (const alias of [node2.title, anchor, fileStem && node2.title ? `${fileStem}#${node2.title}` : "", fileStem && anchor ? `${fileStem}#${anchor}` : "", path && node2.title ? `${path}#${node2.title}` : "", path && anchor ? `${path}#${anchor}` : ""]) entries.push({ alias, kind: "heading" });
5193
6252
  }
@@ -5355,15 +6414,15 @@ function runExpand(argv) {
5355
6414
  }
5356
6415
 
5357
6416
  // packages/cli/src/commands/traceability.mjs
5358
- import { existsSync as existsSync15, readFileSync as readFileSync15, readdirSync as readdirSync5, writeFileSync as writeFileSync6 } from "node:fs";
5359
- import { join as join14, resolve as resolve17 } from "node:path";
6417
+ import { existsSync as existsSync18, readFileSync as readFileSync18, readdirSync as readdirSync5, writeFileSync as writeFileSync7 } from "node:fs";
6418
+ import { join as join18, resolve as resolve17 } from "node:path";
5360
6419
  function collectDocsMarkdownFiles(root) {
5361
- const docs = join14(root, "docs");
6420
+ const docs = join18(root, "docs");
5362
6421
  const files = [];
5363
6422
  const walk = (dir) => {
5364
- if (!existsSync15(dir)) return;
6423
+ if (!existsSync18(dir)) return;
5365
6424
  for (const entry of readdirSync5(dir, { withFileTypes: true })) {
5366
- const path = join14(dir, entry.name);
6425
+ const path = join18(dir, entry.name);
5367
6426
  if (entry.isDirectory()) walk(path);
5368
6427
  else if (entry.isFile() && entry.name.endsWith(".md")) files.push(path);
5369
6428
  }
@@ -5399,7 +6458,7 @@ function runTraceability(argv) {
5399
6458
  const errors = [...config.errors ?? []];
5400
6459
  for (const file of files) {
5401
6460
  if (options.write && errors.length > 0) break;
5402
- const content = readFileSync15(file, "utf8");
6461
+ const content = readFileSync18(file, "utf8");
5403
6462
  for (const error of validateTraceabilityLinksRegion(content, options.root, file)) errors.push(error);
5404
6463
  const blocks = extractDotdotgodTraceabilityBlocks(content).filter((block) => !block.error);
5405
6464
  if (blocks.length === 0) continue;
@@ -5408,7 +6467,7 @@ function runTraceability(argv) {
5408
6467
  for (const error of result.errors ?? []) errors.push(error);
5409
6468
  continue;
5410
6469
  }
5411
- if (options.write && result.changed) writeFileSync6(file, result.content);
6470
+ if (options.write && result.changed) writeFileSync7(file, result.content);
5412
6471
  if (result.changed) results.push({ file: rel(options.root, file), changed: true });
5413
6472
  }
5414
6473
  const ok = errors.length === 0 && (!options.check || results.length === 0);
@@ -5429,9 +6488,9 @@ function runTraceability(argv) {
5429
6488
 
5430
6489
  // packages/cli/src/graph-view/server.mjs
5431
6490
  import { createServer } from "node:http";
5432
- import { readFileSync as readFileSync16 } from "node:fs";
5433
- import { dirname as dirname8, extname as extname8, join as join15 } from "node:path";
5434
- import { fileURLToPath } from "node:url";
6491
+ import { readFileSync as readFileSync19 } from "node:fs";
6492
+ import { dirname as dirname12, extname as extname10, join as join19 } from "node:path";
6493
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
5435
6494
 
5436
6495
  // packages/cli/src/graph-view/payload.mjs
5437
6496
  function edgeKey2(edge) {
@@ -5542,7 +6601,7 @@ function buildImpactGraphPayload(index, impact) {
5542
6601
  }
5543
6602
 
5544
6603
  // packages/cli/src/graph-view/server.mjs
5545
- var ASSET_ROOT = join15(dirname8(fileURLToPath(import.meta.url)), "assets");
6604
+ var ASSET_ROOT = join19(dirname12(fileURLToPath2(import.meta.url)), "assets");
5546
6605
  var CONTENT_TYPES = { ".css": "text/css; charset=utf-8", ".html": "text/html; charset=utf-8", ".js": "text/javascript; charset=utf-8", ".map": "application/json; charset=utf-8" };
5547
6606
  var VENDOR_FILES = {
5548
6607
  "/vendor/sigma.js": new URL("../../node_modules/sigma/dist/sigma.min.js", import.meta.url),
@@ -5555,8 +6614,8 @@ function json(response, status, value) {
5555
6614
  }
5556
6615
  function asset(response, file) {
5557
6616
  try {
5558
- const body = readFileSync16(file);
5559
- response.writeHead(200, { "content-type": CONTENT_TYPES[extname8(file)] ?? "application/octet-stream", "cache-control": "no-store" });
6617
+ const body = readFileSync19(file);
6618
+ response.writeHead(200, { "content-type": CONTENT_TYPES[extname10(file)] ?? "application/octet-stream", "cache-control": "no-store" });
5560
6619
  response.end(body);
5561
6620
  } catch {
5562
6621
  response.writeHead(404);
@@ -5591,13 +6650,13 @@ async function startImpactGraphServer({ root, changed, host = "127.0.0.1", port
5591
6650
  return json(response, 500, { ok: false, error: { code: "GRAPH_SERVE_FAILED", message: error instanceof Error ? error.message : String(error) } });
5592
6651
  }
5593
6652
  }
5594
- if (VENDOR_FILES[url.pathname]) return asset(response, fileURLToPath(VENDOR_FILES[url.pathname]));
5595
- const relative6 = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
5596
- if (!["index.html", "app.css", "app.js"].includes(relative6)) {
6653
+ if (VENDOR_FILES[url.pathname]) return asset(response, fileURLToPath2(VENDOR_FILES[url.pathname]));
6654
+ const relative7 = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
6655
+ if (!["index.html", "app.css", "app.js"].includes(relative7)) {
5597
6656
  response.writeHead(404);
5598
6657
  return response.end("Not found");
5599
6658
  }
5600
- return asset(response, join15(ASSET_ROOT, relative6));
6659
+ return asset(response, join19(ASSET_ROOT, relative7));
5601
6660
  });
5602
6661
  await new Promise((resolve18, reject) => {
5603
6662
  server.once("error", reject);
@@ -5825,7 +6884,7 @@ async function runCli(argv = process.argv.slice(2)) {
5825
6884
  if (hasHelpToken(args)) usage("", helpCommandFromArgs([command, ...args]));
5826
6885
  if (command === "validate") runValidate(args);
5827
6886
  else if (command === "init") runInit(args, usage);
5828
- else if (command === "index") runIndex(args);
6887
+ else if (command === "index") await runIndex(args);
5829
6888
  else if (command === "config") runConfig(args);
5830
6889
  else if (command === "status") runStatus(args);
5831
6890
  else if (command === "query") await runQuery(args);