eminiar 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -3912,49 +3912,49 @@ var require_fast_uri = __commonJS({
3912
3912
  schemelessOptions.skipEscape = true;
3913
3913
  return serialize(resolved, schemelessOptions);
3914
3914
  }
3915
- function resolveComponent(base, relative9, options, skipNormalization) {
3915
+ function resolveComponent(base, relative10, options, skipNormalization) {
3916
3916
  const target = {};
3917
3917
  if (!skipNormalization) {
3918
3918
  base = parse3(serialize(base, options), options);
3919
- relative9 = parse3(serialize(relative9, options), options);
3919
+ relative10 = parse3(serialize(relative10, options), options);
3920
3920
  }
3921
3921
  options = options || {};
3922
- if (!options.tolerant && relative9.scheme) {
3923
- target.scheme = relative9.scheme;
3924
- target.userinfo = relative9.userinfo;
3925
- target.host = relative9.host;
3926
- target.port = relative9.port;
3927
- target.path = removeDotSegments(relative9.path || "");
3928
- target.query = relative9.query;
3922
+ if (!options.tolerant && relative10.scheme) {
3923
+ target.scheme = relative10.scheme;
3924
+ target.userinfo = relative10.userinfo;
3925
+ target.host = relative10.host;
3926
+ target.port = relative10.port;
3927
+ target.path = removeDotSegments(relative10.path || "");
3928
+ target.query = relative10.query;
3929
3929
  } else {
3930
- if (relative9.userinfo !== void 0 || relative9.host !== void 0 || relative9.port !== void 0) {
3931
- target.userinfo = relative9.userinfo;
3932
- target.host = relative9.host;
3933
- target.port = relative9.port;
3934
- target.path = removeDotSegments(relative9.path || "");
3935
- target.query = relative9.query;
3930
+ if (relative10.userinfo !== void 0 || relative10.host !== void 0 || relative10.port !== void 0) {
3931
+ target.userinfo = relative10.userinfo;
3932
+ target.host = relative10.host;
3933
+ target.port = relative10.port;
3934
+ target.path = removeDotSegments(relative10.path || "");
3935
+ target.query = relative10.query;
3936
3936
  } else {
3937
- if (!relative9.path) {
3937
+ if (!relative10.path) {
3938
3938
  target.path = base.path;
3939
- if (relative9.query !== void 0) {
3940
- target.query = relative9.query;
3939
+ if (relative10.query !== void 0) {
3940
+ target.query = relative10.query;
3941
3941
  } else {
3942
3942
  target.query = base.query;
3943
3943
  }
3944
3944
  } else {
3945
- if (relative9.path[0] === "/") {
3946
- target.path = removeDotSegments(relative9.path);
3945
+ if (relative10.path[0] === "/") {
3946
+ target.path = removeDotSegments(relative10.path);
3947
3947
  } else {
3948
3948
  if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
3949
- target.path = "/" + relative9.path;
3949
+ target.path = "/" + relative10.path;
3950
3950
  } else if (!base.path) {
3951
- target.path = relative9.path;
3951
+ target.path = relative10.path;
3952
3952
  } else {
3953
- target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative9.path;
3953
+ target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative10.path;
3954
3954
  }
3955
3955
  target.path = removeDotSegments(target.path);
3956
3956
  }
3957
- target.query = relative9.query;
3957
+ target.query = relative10.query;
3958
3958
  }
3959
3959
  target.userinfo = base.userinfo;
3960
3960
  target.host = base.host;
@@ -3962,7 +3962,7 @@ var require_fast_uri = __commonJS({
3962
3962
  }
3963
3963
  target.scheme = base.scheme;
3964
3964
  }
3965
- target.fragment = relative9.fragment;
3965
+ target.fragment = relative10.fragment;
3966
3966
  return target;
3967
3967
  }
3968
3968
  function equal(uriA, uriB, options) {
@@ -10015,9 +10015,9 @@ var require_parse2 = __commonJS({
10015
10015
  const idx = prev.value.lastIndexOf("[");
10016
10016
  const pre = prev.value.slice(0, idx);
10017
10017
  const rest2 = prev.value.slice(idx + 2);
10018
- const posix = POSIX_REGEX_SOURCE[rest2];
10019
- if (posix) {
10020
- prev.value = pre + posix;
10018
+ const posix2 = POSIX_REGEX_SOURCE[rest2];
10019
+ if (posix2) {
10020
+ prev.value = pre + posix2;
10021
10021
  state.backtrack = true;
10022
10022
  advance();
10023
10023
  if (!bos.output && tokens.indexOf(prev) === 1) {
@@ -10539,7 +10539,7 @@ var require_picomatch = __commonJS({
10539
10539
  throw new TypeError("Expected pattern to be a non-empty string");
10540
10540
  }
10541
10541
  const opts = options || {};
10542
- const posix = utils.isWindows(options);
10542
+ const posix2 = utils.isWindows(options);
10543
10543
  const regex = isState ? picomatch.compileRe(glob2, options) : picomatch.makeRe(glob2, options, false, true);
10544
10544
  const state = regex.state;
10545
10545
  delete regex.state;
@@ -10549,8 +10549,8 @@ var require_picomatch = __commonJS({
10549
10549
  isIgnored = picomatch(opts.ignore, ignoreOpts, returnState);
10550
10550
  }
10551
10551
  const matcher = (input2, returnObject = false) => {
10552
- const { isMatch, match, output: output2 } = picomatch.test(input2, regex, options, { glob: glob2, posix });
10553
- const result = { glob: glob2, state, regex, posix, input: input2, output: output2, match, isMatch };
10552
+ const { isMatch, match, output: output2 } = picomatch.test(input2, regex, options, { glob: glob2, posix: posix2 });
10553
+ const result = { glob: glob2, state, regex, posix: posix2, input: input2, output: output2, match, isMatch };
10554
10554
  if (typeof opts.onResult === "function") {
10555
10555
  opts.onResult(result);
10556
10556
  }
@@ -10575,7 +10575,7 @@ var require_picomatch = __commonJS({
10575
10575
  }
10576
10576
  return matcher;
10577
10577
  };
10578
- picomatch.test = (input2, regex, options, { glob: glob2, posix } = {}) => {
10578
+ picomatch.test = (input2, regex, options, { glob: glob2, posix: posix2 } = {}) => {
10579
10579
  if (typeof input2 !== "string") {
10580
10580
  throw new TypeError("Expected input to be a string");
10581
10581
  }
@@ -10583,7 +10583,7 @@ var require_picomatch = __commonJS({
10583
10583
  return { isMatch: false, output: "" };
10584
10584
  }
10585
10585
  const opts = options || {};
10586
- const format = opts.format || (posix ? utils.toPosixSlashes : null);
10586
+ const format = opts.format || (posix2 ? utils.toPosixSlashes : null);
10587
10587
  let match = input2 === glob2;
10588
10588
  let output2 = match && format ? format(input2) : input2;
10589
10589
  if (match === false) {
@@ -10592,14 +10592,14 @@ var require_picomatch = __commonJS({
10592
10592
  }
10593
10593
  if (match === false || opts.capture === true) {
10594
10594
  if (opts.matchBase === true || opts.basename === true) {
10595
- match = picomatch.matchBase(input2, regex, options, posix);
10595
+ match = picomatch.matchBase(input2, regex, options, posix2);
10596
10596
  } else {
10597
10597
  match = regex.exec(output2);
10598
10598
  }
10599
10599
  }
10600
10600
  return { isMatch: Boolean(match), match, output: output2 };
10601
10601
  };
10602
- picomatch.matchBase = (input2, glob2, options, posix = utils.isWindows(options)) => {
10602
+ picomatch.matchBase = (input2, glob2, options, posix2 = utils.isWindows(options)) => {
10603
10603
  const regex = glob2 instanceof RegExp ? glob2 : picomatch.makeRe(glob2, options);
10604
10604
  return regex.test(path3.basename(input2));
10605
10605
  };
@@ -10790,9 +10790,9 @@ var require_micromatch = __commonJS({
10790
10790
  return [].concat(patterns).every((p) => picomatch(p, options)(str2));
10791
10791
  };
10792
10792
  micromatch.capture = (glob2, input2, options) => {
10793
- let posix = utils.isWindows(options);
10793
+ let posix2 = utils.isWindows(options);
10794
10794
  let regex = picomatch.makeRe(String(glob2), { ...options, capture: true });
10795
- let match = regex.exec(posix ? utils.toPosixSlashes(input2) : input2);
10795
+ let match = regex.exec(posix2 ? utils.toPosixSlashes(input2) : input2);
10796
10796
  if (match) {
10797
10797
  return match.slice(1).map((v) => v === void 0 ? "" : v);
10798
10798
  }
@@ -10972,15 +10972,15 @@ var require_pattern2 = __commonJS({
10972
10972
  exports.removeDuplicateSlashes = removeDuplicateSlashes;
10973
10973
  function partitionAbsoluteAndRelative(patterns) {
10974
10974
  const absolute = [];
10975
- const relative9 = [];
10975
+ const relative10 = [];
10976
10976
  for (const pattern2 of patterns) {
10977
10977
  if (isAbsolute2(pattern2)) {
10978
10978
  absolute.push(pattern2);
10979
10979
  } else {
10980
- relative9.push(pattern2);
10980
+ relative10.push(pattern2);
10981
10981
  }
10982
10982
  }
10983
- return [absolute, relative9];
10983
+ return [absolute, relative10];
10984
10984
  }
10985
10985
  exports.partitionAbsoluteAndRelative = partitionAbsoluteAndRelative;
10986
10986
  function isAbsolute2(pattern2) {
@@ -11411,11 +11411,11 @@ var require_out = __commonJS({
11411
11411
  async.read(path3, getSettings(optionsOrSettingsOrCallback), callback);
11412
11412
  }
11413
11413
  exports.stat = stat;
11414
- function statSync10(path3, optionsOrSettings) {
11414
+ function statSync11(path3, optionsOrSettings) {
11415
11415
  const settings = getSettings(optionsOrSettings);
11416
11416
  return sync.read(path3, settings);
11417
11417
  }
11418
- exports.statSync = statSync10;
11418
+ exports.statSync = statSync11;
11419
11419
  function getSettings(settingsOrOptions = {}) {
11420
11420
  if (settingsOrOptions instanceof settings_1.default) {
11421
11421
  return settingsOrOptions;
@@ -13295,19 +13295,19 @@ var require_out4 = __commonJS({
13295
13295
  return utils.path.convertPathToPattern(source);
13296
13296
  }
13297
13297
  FastGlob2.convertPathToPattern = convertPathToPattern;
13298
- let posix;
13299
- (function(posix2) {
13298
+ let posix2;
13299
+ (function(posix3) {
13300
13300
  function escapePath2(source) {
13301
13301
  assertPatternsInput(source);
13302
13302
  return utils.path.escapePosixPath(source);
13303
13303
  }
13304
- posix2.escapePath = escapePath2;
13304
+ posix3.escapePath = escapePath2;
13305
13305
  function convertPathToPattern2(source) {
13306
13306
  assertPatternsInput(source);
13307
13307
  return utils.path.convertPosixPathToPattern(source);
13308
13308
  }
13309
- posix2.convertPathToPattern = convertPathToPattern2;
13310
- })(posix = FastGlob2.posix || (FastGlob2.posix = {}));
13309
+ posix3.convertPathToPattern = convertPathToPattern2;
13310
+ })(posix2 = FastGlob2.posix || (FastGlob2.posix = {}));
13311
13311
  let win32;
13312
13312
  (function(win322) {
13313
13313
  function escapePath2(source) {
@@ -13790,13 +13790,14 @@ function fileOf(id) {
13790
13790
  if (kind === "sym") return rest.split("#")[0];
13791
13791
  return void 0;
13792
13792
  }
13793
- var fileId, memoryId, symId, pkgId, personId, conceptId;
13793
+ var fileId, memoryId, docId, symId, pkgId, personId, conceptId;
13794
13794
  var init_ids = __esm({
13795
13795
  "src/graph/ids.ts"() {
13796
13796
  "use strict";
13797
13797
  init_esm_shims();
13798
13798
  fileId = (rel) => `file:${rel}`;
13799
13799
  memoryId = (name) => `memory:${name}`;
13800
+ docId = (name) => `doc:${name}`;
13800
13801
  symId = (rel, name) => `sym:${rel}#${name}`;
13801
13802
  pkgId = (name) => `pkg:${name}`;
13802
13803
  personId = (name) => `person:${name}`;
@@ -18022,10 +18023,12 @@ __export(store_exports, {
18022
18023
  isValidName: () => isValidName,
18023
18024
  list: () => list,
18024
18025
  memoriesDir: () => memoriesDir,
18026
+ memoryLinks: () => memoryLinks,
18025
18027
  normalizeRelated: () => normalizeRelated,
18026
18028
  read: () => read,
18027
18029
  reconcile: () => reconcile,
18028
18030
  relOf: () => relOf,
18031
+ relatedTarget: () => relatedTarget,
18029
18032
  remove: () => remove,
18030
18033
  rename: () => rename,
18031
18034
  setMeta: () => setMeta,
@@ -18101,6 +18104,17 @@ function normalizeRelated(ids) {
18101
18104
  }
18102
18105
  return [...out];
18103
18106
  }
18107
+ function relatedTarget(id) {
18108
+ return id.startsWith("mem:") ? memoryId(id.slice(4)) : id;
18109
+ }
18110
+ function memoryLinks(body, self) {
18111
+ const out = /* @__PURE__ */ new Set();
18112
+ for (const m of body.matchAll(MEM_LINK_RE)) {
18113
+ const name = m[1].replace(/[.-]+$/, "");
18114
+ if (name && name !== self && isValidName(name)) out.add(name);
18115
+ }
18116
+ return [...out];
18117
+ }
18104
18118
  function writeMemoryFile(ctx, name, meta4, body) {
18105
18119
  const abs = absOf(ctx, name);
18106
18120
  mkdirSync3(dirname7(abs), { recursive: true });
@@ -18108,36 +18122,30 @@ function writeMemoryFile(ctx, name, meta4, body) {
18108
18122
  ${body.trim()}
18109
18123
  `));
18110
18124
  }
18111
- function syncGraph(db, name, rel, kind, tags, related, supersedes) {
18125
+ function syncGraph(db, name, rel, g) {
18126
+ const self = memoryId(name);
18112
18127
  db.graphDeleteForPath("curated", rel);
18113
18128
  db.graphUpsertNodes([
18114
18129
  {
18115
- id: memoryId(name),
18130
+ id: self,
18116
18131
  kind: "memory",
18117
18132
  name,
18118
18133
  path: rel,
18119
18134
  source: "curated",
18120
- meta: { kind, tags }
18135
+ meta: { kind: g.kind, tags: g.tags, ...g.status ? { status: g.status } : {} }
18121
18136
  }
18122
18137
  ]);
18123
- const edges = related.map((dst) => ({
18124
- src: memoryId(name),
18125
- dst,
18126
- kind: "mentions",
18127
- source: "curated",
18128
- confidence: 1,
18129
- weight: 1
18130
- }));
18131
- if (supersedes) {
18132
- edges.push({
18133
- src: memoryId(name),
18134
- dst: memoryId(supersedes),
18135
- kind: "supersedes",
18136
- source: "curated",
18137
- confidence: 1,
18138
- weight: 1
18139
- });
18140
- }
18138
+ const edges = [];
18139
+ const seen = /* @__PURE__ */ new Set();
18140
+ const push = (dst, kind) => {
18141
+ const key = `${kind}|${dst}`;
18142
+ if (dst === self || seen.has(key)) return;
18143
+ seen.add(key);
18144
+ edges.push({ src: self, dst, kind, source: "curated", confidence: 1, weight: 1 });
18145
+ };
18146
+ for (const id of g.related) push(relatedTarget(id), "mentions");
18147
+ for (const other of memoryLinks(g.body, name)) push(memoryId(other), "links");
18148
+ if (g.supersedes) push(memoryId(g.supersedes.replace(/^mem(?:ory)?:/, "")), "supersedes");
18141
18149
  if (edges.length) db.graphUpsertEdges(edges);
18142
18150
  }
18143
18151
  function forgetInDb(db, name, rel) {
@@ -18224,7 +18232,7 @@ tags: ${tags.join(", ")}` : ""}`,
18224
18232
  status,
18225
18233
  supersedes
18226
18234
  });
18227
- syncGraph(db, name, rel, kind, tags, related, supersedes);
18235
+ syncGraph(db, name, rel, { kind, tags, related, status, supersedes, body });
18228
18236
  return { changed: true, row: db.memoryGetByName(name) };
18229
18237
  }
18230
18238
  function setMeta(ctx, db, name, patch) {
@@ -18427,7 +18435,7 @@ function gitignoreHint(ctx) {
18427
18435
  const ignored = readFileSync8(file3, "utf8").split(/\r?\n/).map((l) => l.trim().replace(/^\//, "").replace(/\/$/, "")).some((l) => l === top || l === dir || l === `${top}/**` || l === `${dir}/**`);
18428
18436
  return ignored ? `note: .gitignore ignores ${dir}, so memories stay local to this machine (remove that line to share them with the team)` : void 0;
18429
18437
  }
18430
- var import_fast_glob3, MEMORY_KINDS, SEGMENT, NAME_RE, hinted;
18438
+ var import_fast_glob3, MEMORY_KINDS, SEGMENT, NAME_RE, MEM_LINK_RE, hinted;
18431
18439
  var init_store = __esm({
18432
18440
  "src/memory/store.ts"() {
18433
18441
  "use strict";
@@ -18449,6 +18457,7 @@ var init_store = __esm({
18449
18457
  ];
18450
18458
  SEGMENT = "[a-z0-9][a-z0-9._-]*";
18451
18459
  NAME_RE = new RegExp(`^(?:${SEGMENT}/)?${SEGMENT}$`);
18460
+ MEM_LINK_RE = /(?<![A-Za-z0-9_./-])mem:([a-z0-9][a-z0-9._-]*(?:\/[a-z0-9][a-z0-9._-]*)?)/g;
18452
18461
  hinted = /* @__PURE__ */ new Set();
18453
18462
  }
18454
18463
  });
@@ -50808,9 +50817,10 @@ function impact(db, ref, opts = {}) {
50808
50817
  frontier = next;
50809
50818
  if (!frontier.length) break;
50810
50819
  }
50811
- for (const e of db.graphEdgesTo(start, ["mentions"])) {
50820
+ for (const e of db.graphEdgesTo(start, ["mentions", "documents"])) {
50812
50821
  const via = e.dst === root.id ? root.name : e.dst.replace(/^sym:[^#]*#/, "");
50813
- add(e.src, 0.5 * e.confidence, 1, `memory mentions ${via}`);
50822
+ const who = e.kind === "documents" ? "doc documents" : "memory mentions";
50823
+ add(e.src, 0.5 * e.confidence, 1, `${who} ${via}`);
50814
50824
  }
50815
50825
  if (opts.includeGit !== false) {
50816
50826
  const file3 = toFile(root.id);
@@ -51231,7 +51241,7 @@ function architecture(ctx, db, aspects = ASPECTS) {
51231
51241
  for (const l of languages2.values()) langCounts[l] = (langCounts[l] ?? 0) + 1;
51232
51242
  const counts = db.graphCounts();
51233
51243
  const nodeCounts = db.graphNodeCounts();
51234
- const symbols = Object.entries(nodeCounts).filter(([k]) => !["file", "package", "person", "concept", "memory"].includes(k)).reduce((a, [, n]) => a + n, 0);
51244
+ const symbols = Object.entries(nodeCounts).filter(([k]) => !["file", "package", "person", "concept", "memory", "doc"].includes(k)).reduce((a, [, n]) => a + n, 0);
51235
51245
  const out = {
51236
51246
  project: {
51237
51247
  name: typeof pkg.name === "string" ? pkg.name : void 0,
@@ -55141,6 +55151,309 @@ function discoverDocs(ctx, opts = {}) {
55141
55151
  };
55142
55152
  }
55143
55153
 
55154
+ // src/docs/graph.ts
55155
+ init_esm_shims();
55156
+ init_ids();
55157
+ import { existsSync as existsSync14, statSync as statSync10 } from "fs";
55158
+ import { dirname as dirname9, posix, relative as relative8, sep as sep2 } from "path";
55159
+
55160
+ // src/docs/category.ts
55161
+ init_esm_shims();
55162
+ var DOC_CATEGORIES = [
55163
+ {
55164
+ id: "readme",
55165
+ group: "project",
55166
+ label: "Readme & guides",
55167
+ blurb: "README, CONTRIBUTING, CHANGELOG and other top-level guides"
55168
+ },
55169
+ {
55170
+ id: "guides",
55171
+ group: "project",
55172
+ label: "Docs folders",
55173
+ blurb: "docs/ directories: handbooks, how-tos, references"
55174
+ },
55175
+ {
55176
+ id: "adr",
55177
+ group: "project",
55178
+ label: "Decision records",
55179
+ blurb: "architecture decision records"
55180
+ },
55181
+ {
55182
+ id: "agent-instructions",
55183
+ group: "project",
55184
+ label: "Agent instructions",
55185
+ blurb: "Copilot, Claude and Cursor instructions, agents and skills"
55186
+ },
55187
+ {
55188
+ id: "api-specs",
55189
+ group: "project",
55190
+ label: "API specs",
55191
+ blurb: "OpenAPI, Postman, GraphQL and protobuf definitions"
55192
+ },
55193
+ {
55194
+ id: "env-reference",
55195
+ group: "project",
55196
+ label: "Environment reference",
55197
+ blurb: ".env examples: every variable the project reads"
55198
+ },
55199
+ {
55200
+ id: "deploy-config",
55201
+ group: "project",
55202
+ label: "Deploy config",
55203
+ blurb: "Terraform, Kubernetes, Helm, Compose and CI workflows"
55204
+ },
55205
+ { id: "local", group: "project", label: "Other local files", blurb: "hand-registered paths" },
55206
+ {
55207
+ id: "service",
55208
+ group: "external",
55209
+ label: "Services",
55210
+ blurb: "third-party products and APIs the project integrates"
55211
+ },
55212
+ {
55213
+ id: "api",
55214
+ group: "external",
55215
+ label: "External APIs",
55216
+ blurb: "hosts the code calls, found by probing their docs"
55217
+ },
55218
+ {
55219
+ id: "infra",
55220
+ group: "external",
55221
+ label: "Infrastructure",
55222
+ blurb: "platforms the project runs on"
55223
+ },
55224
+ { id: "library", group: "external", label: "Libraries", blurb: "frameworks and packages" },
55225
+ { id: "web", group: "external", label: "Web pages", blurb: "hand-registered URLs" }
55226
+ ];
55227
+ var BY_ID = new Map(DOC_CATEGORIES.map((c) => [c.id, c]));
55228
+ function categoryInfo(id) {
55229
+ return BY_ID.get(id) ?? BY_ID.get("local");
55230
+ }
55231
+ var GROUP_LABELS = {
55232
+ project: "Project documentation",
55233
+ external: "External documentation"
55234
+ };
55235
+ var TITLE_RULES = [
55236
+ [/^project readme/i, "readme"],
55237
+ [/^(docs|doc|documentation)\/ directory$/i, "guides"],
55238
+ [/decision records/i, "adr"],
55239
+ [/copilot|claude|cursor/i, "agent-instructions"],
55240
+ [/openapi|postman|graphql|protobuf/i, "api-specs"],
55241
+ [/environment variable/i, "env-reference"],
55242
+ [/terraform|kubernetes|helm|compose|workflow/i, "deploy-config"]
55243
+ ];
55244
+ var NAME_RULES = [
55245
+ [/(^|-)readme$/, "readme"],
55246
+ [/(^|-)docs$/, "guides"],
55247
+ [/(^|-)adrs?$/, "adr"],
55248
+ [/(^|-)agent-instructions$/, "agent-instructions"],
55249
+ [/(^|-)api-specs$/, "api-specs"],
55250
+ [/(^|-)env-example$/, "env-reference"],
55251
+ [/(^|-)deploy-config$/, "deploy-config"]
55252
+ ];
55253
+ var GLOB_RULES = [
55254
+ [/openapi|swagger|postman_collection|asyncapi|\.graphql$|\.proto$/i, "api-specs"],
55255
+ [/^\.env\./, "env-reference"],
55256
+ [
55257
+ /\.tf$|tfvars|^k8s\/|^helm\/|^charts\/|^manifests\/|compose.*\.ya?ml|\.github\/workflows/i,
55258
+ "deploy-config"
55259
+ ],
55260
+ [
55261
+ /copilot-instructions|\.github\/(instructions|agents|skills)|\.claude\/(agents|skills)|\.cursor\/rules|^(AGENTS|CLAUDE)\.md$/i,
55262
+ "agent-instructions"
55263
+ ],
55264
+ [/^(README|CONTRIBUTING|CHANGELOG|SECURITY)(\.md)?$/i, "readme"],
55265
+ [/^\*\*\/\*\.(md|mdx|rst|txt)$/i, "guides"]
55266
+ ];
55267
+ function localCategory(s) {
55268
+ const title = s.title ?? "";
55269
+ for (const [re, cat] of TITLE_RULES) if (re.test(title)) return cat;
55270
+ for (const [re, cat] of NAME_RULES) if (re.test(s.name)) return cat;
55271
+ const path3 = s.path ?? "";
55272
+ if (/(^|\/)(adrs?|decisions)$/i.test(path3)) return "adr";
55273
+ const globs = s.glob ?? [];
55274
+ const hits = /* @__PURE__ */ new Map();
55275
+ for (const g of globs)
55276
+ for (const [re, cat] of GLOB_RULES)
55277
+ if (re.test(g)) {
55278
+ hits.set(cat, (hits.get(cat) ?? 0) + 1);
55279
+ break;
55280
+ }
55281
+ if (hits.size) return [...hits.entries()].sort((a, b) => b[1] - a[1])[0][0];
55282
+ if (/(^|\/)(docs?|documentation)$/i.test(path3)) return "guides";
55283
+ if (/README|CONTRIBUTING|CHANGELOG/i.test(path3)) return "readme";
55284
+ return "local";
55285
+ }
55286
+ function docCategory(s, entry) {
55287
+ if (s.kind === "path") return localCategory(s);
55288
+ if (entry) {
55289
+ const tier = entry.tier ?? "library";
55290
+ return tier === "service" ? "service" : tier === "infra" ? "infra" : "library";
55291
+ }
55292
+ const provider = s.provider ?? "";
55293
+ const i = provider.indexOf(":");
55294
+ const kind = i === -1 ? provider : provider.slice(0, i);
55295
+ switch (kind) {
55296
+ case "api":
55297
+ return "api";
55298
+ case "env":
55299
+ return "service";
55300
+ case "image":
55301
+ case "files":
55302
+ return "infra";
55303
+ case "npm":
55304
+ case "pypi":
55305
+ case "go":
55306
+ return "library";
55307
+ default:
55308
+ return "web";
55309
+ }
55310
+ }
55311
+ function categoryRank(id) {
55312
+ const i = DOC_CATEGORIES.findIndex((c) => c.id === id);
55313
+ return i === -1 ? DOC_CATEGORIES.length : i;
55314
+ }
55315
+
55316
+ // src/docs/graph.ts
55317
+ var DOCUMENTS_EDGE = "documents";
55318
+ var MAX_PAGES = 500;
55319
+ var MAX_EDGES = 400;
55320
+ var PATH_RE = /(?:^|[\s(`'"[<])((?:\.{1,2}\/)*(?:[\w@.-]+\/)+[\w@.-]+\.[A-Za-z0-9]{1,8})(?=$|[\s)`'"\]>:#,;!?]|\.(?:\s|$))/gm;
55321
+ function manifestRel(ctx) {
55322
+ return relative8(ctx.root, manifestPath(ctx)).split(sep2).join("/");
55323
+ }
55324
+ function packageCandidates(provider) {
55325
+ if (!provider) return [];
55326
+ const i = provider.indexOf(":");
55327
+ if (i === -1) return [];
55328
+ const kind = provider.slice(0, i);
55329
+ const name = provider.slice(i + 1).trim();
55330
+ if (!name) return [];
55331
+ if (kind === "npm" || kind === "go") return [pkgId(name)];
55332
+ if (kind === "pypi") {
55333
+ const lower = name.toLowerCase();
55334
+ return [.../* @__PURE__ */ new Set([pkgId(lower.replace(/-/g, "_")), pkgId(lower), pkgId(name)])];
55335
+ }
55336
+ return [];
55337
+ }
55338
+ function pathsIn(text2, pageRel, files) {
55339
+ const out = /* @__PURE__ */ new Set();
55340
+ for (const m of text2.matchAll(PATH_RE)) {
55341
+ const raw = m[1];
55342
+ const tries = [raw.replace(/^(\.\/)+/, "")];
55343
+ if (pageRel && raw.startsWith("."))
55344
+ tries.unshift(posix.normalize(posix.join(dirname9(pageRel), raw)));
55345
+ for (const t of tries) {
55346
+ if (files.has(t)) {
55347
+ out.add(t);
55348
+ break;
55349
+ }
55350
+ }
55351
+ }
55352
+ return [...out];
55353
+ }
55354
+ function pageRelOf(ctx, uri) {
55355
+ if (!uri?.startsWith("file://")) return void 0;
55356
+ const rel = relative8(ctx.root, uri.slice("file://".length)).split(sep2).join("/");
55357
+ return rel.startsWith("..") ? void 0 : rel;
55358
+ }
55359
+ function localEdges(ctx, db, src, files) {
55360
+ const id = docId(src.name);
55361
+ const hits = /* @__PURE__ */ new Map();
55362
+ for (const page of db.documentsBySource(docsTag(src.name), MAX_PAGES)) {
55363
+ const rel = pageRelOf(ctx, page.uri);
55364
+ if (rel && files.has(rel)) hits.set(rel, { via: "covers", count: 1 });
55365
+ const text2 = db.chunksOfDocument(page.id).map((c) => c.body).join("\n");
55366
+ for (const f of pathsIn(text2, rel, files)) {
55367
+ const h = hits.get(f);
55368
+ if (h) h.count++;
55369
+ else hits.set(f, { via: "mentions", count: 1 });
55370
+ }
55371
+ if (hits.size >= MAX_EDGES) break;
55372
+ }
55373
+ return [...hits].sort((a, b) => b[1].count - a[1].count).slice(0, MAX_EDGES).map(([rel, h]) => ({
55374
+ src: id,
55375
+ dst: `file:${rel}`,
55376
+ kind: DOCUMENTS_EDGE,
55377
+ source: "curated",
55378
+ confidence: h.via === "covers" ? 1 : 0.8,
55379
+ weight: Math.min(1, 0.5 + h.count / 10),
55380
+ meta: { via: h.via, count: h.count }
55381
+ }));
55382
+ }
55383
+ function packageEdges(src, packages) {
55384
+ const id = docId(src.name);
55385
+ const cands = packageCandidates(src.provider);
55386
+ if (!cands.length) return [];
55387
+ let targets = cands.filter((c) => packages.has(c));
55388
+ if (src.provider?.startsWith("go:"))
55389
+ targets = [...targets, ...[...packages].filter((p) => p.startsWith(`${cands[0]}/`))];
55390
+ if (!targets.length) targets = [cands[0]];
55391
+ return [...new Set(targets)].map((dst) => ({
55392
+ src: id,
55393
+ dst,
55394
+ kind: DOCUMENTS_EDGE,
55395
+ source: "curated",
55396
+ confidence: 1,
55397
+ weight: 1,
55398
+ meta: { via: "provider" }
55399
+ }));
55400
+ }
55401
+ var STAMP_KEY = "docs.graph.stamp";
55402
+ function stampOf(ctx, db) {
55403
+ const file3 = manifestPath(ctx);
55404
+ const st = existsSync14(file3) ? statSync10(file3) : void 0;
55405
+ const counts = db.graphNodeCounts();
55406
+ const syncs = readManifest(ctx).sources.map((s) => syncState(db, s.name)?.at ?? 0).join(",");
55407
+ return `${st ? `${Math.floor(st.mtimeMs)}:${st.size}` : "none"}|${counts.file ?? 0}|${counts.package ?? 0}|${syncs}`;
55408
+ }
55409
+ function syncDocsGraph(ctx, db) {
55410
+ const rel = manifestRel(ctx);
55411
+ const m = readManifest(ctx);
55412
+ const catalog = mergedCatalog(ctx.config.docs.catalog);
55413
+ const files = new Set(
55414
+ db.graphFileNodes().filter((n) => n.source === "tree-sitter" && n.path).map((n) => n.path)
55415
+ );
55416
+ const packages = new Set(db.graphNodesByKind(["package"]).map((n) => n.id));
55417
+ const nodes = [];
55418
+ const edges = [];
55419
+ for (const s of m.sources) {
55420
+ const category = docCategory(
55421
+ s,
55422
+ s.provider ? catalogForProvider(s.provider, catalog) : void 0
55423
+ );
55424
+ nodes.push({
55425
+ id: docId(s.name),
55426
+ kind: "doc",
55427
+ name: s.name,
55428
+ path: rel,
55429
+ source: "curated",
55430
+ meta: {
55431
+ category,
55432
+ group: categoryInfo(category).group,
55433
+ sourceKind: s.kind,
55434
+ ...s.provider ? { provider: s.provider } : {},
55435
+ ...s.title ? { title: s.title } : {},
55436
+ ...s.tags?.length ? { tags: s.tags } : {},
55437
+ ...s.kind === "path" ? { target: s.path } : { url: s.urls?.[0] },
55438
+ documents: db.countDocuments(docsTag(s.name))
55439
+ }
55440
+ });
55441
+ edges.push(...packageEdges(s, packages));
55442
+ if (s.kind === "path") edges.push(...localEdges(ctx, db, s, files));
55443
+ }
55444
+ db.graphDeleteForPath("curated", rel, { keepFile: true });
55445
+ if (nodes.length) db.graphUpsertNodes(nodes);
55446
+ if (edges.length) db.graphUpsertEdges(edges);
55447
+ db.metaSet(STAMP_KEY, stampOf(ctx, db));
55448
+ return { sources: nodes.length, edges: edges.length };
55449
+ }
55450
+ function syncDocsGraphQuietly(ctx, db) {
55451
+ try {
55452
+ syncDocsGraph(ctx, db);
55453
+ } catch {
55454
+ }
55455
+ }
55456
+
55144
55457
  // src/docs/resolve.ts
55145
55458
  init_esm_shims();
55146
55459
  async function getJson(url2, opts) {
@@ -56009,6 +56322,7 @@ async function syncSources(ctx, db, names, opts = {}) {
56009
56322
  });
56010
56323
  }
56011
56324
  for (const src of wanted) out.push(await syncSource(ctx, db, src, opts));
56325
+ syncDocsGraphQuietly(ctx, db);
56012
56326
  return out;
56013
56327
  }
56014
56328
  function formatSync(results) {
@@ -56178,12 +56492,14 @@ async function addDocSources(ctx, db, inputs, opts = {}) {
56178
56492
  const names = [...out.added, ...out.updated];
56179
56493
  if (opts.sync !== false && names.length)
56180
56494
  out.sync = await syncSources(ctx, db, names, { force: opts.force });
56495
+ else if (names.length) syncDocsGraphQuietly(ctx, db);
56181
56496
  return out;
56182
56497
  }
56183
56498
  function removeDocSource(ctx, db, name) {
56184
56499
  const removed = removeSource(ctx, name);
56185
56500
  const documents = db.deleteDocumentsBySource(docsTag(name));
56186
56501
  clearSyncState(db, name);
56502
+ if (removed) syncDocsGraphQuietly(ctx, db);
56187
56503
  return { removed, documents };
56188
56504
  }
56189
56505
  function formatAdd(o) {
@@ -56211,162 +56527,6 @@ async function addPicked(ctx, db, pick2, opts) {
56211
56527
  );
56212
56528
  }
56213
56529
 
56214
- // src/docs/category.ts
56215
- init_esm_shims();
56216
- var DOC_CATEGORIES = [
56217
- {
56218
- id: "readme",
56219
- group: "project",
56220
- label: "Readme & guides",
56221
- blurb: "README, CONTRIBUTING, CHANGELOG and other top-level guides"
56222
- },
56223
- {
56224
- id: "guides",
56225
- group: "project",
56226
- label: "Docs folders",
56227
- blurb: "docs/ directories: handbooks, how-tos, references"
56228
- },
56229
- {
56230
- id: "adr",
56231
- group: "project",
56232
- label: "Decision records",
56233
- blurb: "architecture decision records"
56234
- },
56235
- {
56236
- id: "agent-instructions",
56237
- group: "project",
56238
- label: "Agent instructions",
56239
- blurb: "Copilot, Claude and Cursor instructions, agents and skills"
56240
- },
56241
- {
56242
- id: "api-specs",
56243
- group: "project",
56244
- label: "API specs",
56245
- blurb: "OpenAPI, Postman, GraphQL and protobuf definitions"
56246
- },
56247
- {
56248
- id: "env-reference",
56249
- group: "project",
56250
- label: "Environment reference",
56251
- blurb: ".env examples: every variable the project reads"
56252
- },
56253
- {
56254
- id: "deploy-config",
56255
- group: "project",
56256
- label: "Deploy config",
56257
- blurb: "Terraform, Kubernetes, Helm, Compose and CI workflows"
56258
- },
56259
- { id: "local", group: "project", label: "Other local files", blurb: "hand-registered paths" },
56260
- {
56261
- id: "service",
56262
- group: "external",
56263
- label: "Services",
56264
- blurb: "third-party products and APIs the project integrates"
56265
- },
56266
- {
56267
- id: "api",
56268
- group: "external",
56269
- label: "External APIs",
56270
- blurb: "hosts the code calls, found by probing their docs"
56271
- },
56272
- {
56273
- id: "infra",
56274
- group: "external",
56275
- label: "Infrastructure",
56276
- blurb: "platforms the project runs on"
56277
- },
56278
- { id: "library", group: "external", label: "Libraries", blurb: "frameworks and packages" },
56279
- { id: "web", group: "external", label: "Web pages", blurb: "hand-registered URLs" }
56280
- ];
56281
- var BY_ID = new Map(DOC_CATEGORIES.map((c) => [c.id, c]));
56282
- function categoryInfo(id) {
56283
- return BY_ID.get(id) ?? BY_ID.get("local");
56284
- }
56285
- var GROUP_LABELS = {
56286
- project: "Project documentation",
56287
- external: "External documentation"
56288
- };
56289
- var TITLE_RULES = [
56290
- [/^project readme/i, "readme"],
56291
- [/^(docs|doc|documentation)\/ directory$/i, "guides"],
56292
- [/decision records/i, "adr"],
56293
- [/copilot|claude|cursor/i, "agent-instructions"],
56294
- [/openapi|postman|graphql|protobuf/i, "api-specs"],
56295
- [/environment variable/i, "env-reference"],
56296
- [/terraform|kubernetes|helm|compose|workflow/i, "deploy-config"]
56297
- ];
56298
- var NAME_RULES = [
56299
- [/(^|-)readme$/, "readme"],
56300
- [/(^|-)docs$/, "guides"],
56301
- [/(^|-)adrs?$/, "adr"],
56302
- [/(^|-)agent-instructions$/, "agent-instructions"],
56303
- [/(^|-)api-specs$/, "api-specs"],
56304
- [/(^|-)env-example$/, "env-reference"],
56305
- [/(^|-)deploy-config$/, "deploy-config"]
56306
- ];
56307
- var GLOB_RULES = [
56308
- [/openapi|swagger|postman_collection|asyncapi|\.graphql$|\.proto$/i, "api-specs"],
56309
- [/^\.env\./, "env-reference"],
56310
- [
56311
- /\.tf$|tfvars|^k8s\/|^helm\/|^charts\/|^manifests\/|compose.*\.ya?ml|\.github\/workflows/i,
56312
- "deploy-config"
56313
- ],
56314
- [
56315
- /copilot-instructions|\.github\/(instructions|agents|skills)|\.claude\/(agents|skills)|\.cursor\/rules|^(AGENTS|CLAUDE)\.md$/i,
56316
- "agent-instructions"
56317
- ],
56318
- [/^(README|CONTRIBUTING|CHANGELOG|SECURITY)(\.md)?$/i, "readme"],
56319
- [/^\*\*\/\*\.(md|mdx|rst|txt)$/i, "guides"]
56320
- ];
56321
- function localCategory(s) {
56322
- const title = s.title ?? "";
56323
- for (const [re, cat] of TITLE_RULES) if (re.test(title)) return cat;
56324
- for (const [re, cat] of NAME_RULES) if (re.test(s.name)) return cat;
56325
- const path3 = s.path ?? "";
56326
- if (/(^|\/)(adrs?|decisions)$/i.test(path3)) return "adr";
56327
- const globs = s.glob ?? [];
56328
- const hits = /* @__PURE__ */ new Map();
56329
- for (const g of globs)
56330
- for (const [re, cat] of GLOB_RULES)
56331
- if (re.test(g)) {
56332
- hits.set(cat, (hits.get(cat) ?? 0) + 1);
56333
- break;
56334
- }
56335
- if (hits.size) return [...hits.entries()].sort((a, b) => b[1] - a[1])[0][0];
56336
- if (/(^|\/)(docs?|documentation)$/i.test(path3)) return "guides";
56337
- if (/README|CONTRIBUTING|CHANGELOG/i.test(path3)) return "readme";
56338
- return "local";
56339
- }
56340
- function docCategory(s, entry) {
56341
- if (s.kind === "path") return localCategory(s);
56342
- if (entry) {
56343
- const tier = entry.tier ?? "library";
56344
- return tier === "service" ? "service" : tier === "infra" ? "infra" : "library";
56345
- }
56346
- const provider = s.provider ?? "";
56347
- const i = provider.indexOf(":");
56348
- const kind = i === -1 ? provider : provider.slice(0, i);
56349
- switch (kind) {
56350
- case "api":
56351
- return "api";
56352
- case "env":
56353
- return "service";
56354
- case "image":
56355
- case "files":
56356
- return "infra";
56357
- case "npm":
56358
- case "pypi":
56359
- case "go":
56360
- return "library";
56361
- default:
56362
- return "web";
56363
- }
56364
- }
56365
- function categoryRank(id) {
56366
- const i = DOC_CATEGORIES.findIndex((c) => c.id === id);
56367
- return i === -1 ? DOC_CATEGORIES.length : i;
56368
- }
56369
-
56370
56530
  // src/tools/em_docs.ts
56371
56531
  init_queue();
56372
56532
  var sourceShape2 = {
@@ -56674,7 +56834,7 @@ init_esm_shims();
56674
56834
  init_esm_shims();
56675
56835
  init_paths();
56676
56836
  init_version();
56677
- import { accessSync as accessSync2, constants as constants2, existsSync as existsSync14, readFileSync as readFileSync14 } from "fs";
56837
+ import { accessSync as accessSync2, constants as constants2, existsSync as existsSync15, readFileSync as readFileSync14 } from "fs";
56678
56838
  import { createRequire as createRequire3 } from "module";
56679
56839
  import { join as join20 } from "path";
56680
56840
  var require4 = createRequire3(import.meta.url);
@@ -56705,7 +56865,7 @@ function runDoctor() {
56705
56865
  );
56706
56866
  checks.push(
56707
56867
  check2("node-path pin", () => {
56708
- if (!existsSync14(p.nodePathFile)) return "not pinned (run `eminiar install`); using PATH node";
56868
+ if (!existsSync15(p.nodePathFile)) return "not pinned (run `eminiar install`); using PATH node";
56709
56869
  const pinned = readFileSync14(p.nodePathFile, "utf8").trim();
56710
56870
  accessSync2(pinned, constants2.X_OK);
56711
56871
  return pinned;
@@ -56713,10 +56873,10 @@ function runDoctor() {
56713
56873
  );
56714
56874
  checks.push(
56715
56875
  check2("root-path pin", () => {
56716
- if (!existsSync14(p.rootPathFile))
56876
+ if (!existsSync15(p.rootPathFile))
56717
56877
  return "not pinned (run `eminiar install`); the bin/ shims run only from a directory that has node_modules";
56718
56878
  const root = readFileSync14(p.rootPathFile, "utf8").trim();
56719
- if (!existsSync14(join20(root, "dist", "server.js")))
56879
+ if (!existsSync15(join20(root, "dist", "server.js")))
56720
56880
  throw new Error(`${root} has no dist/server.js (run \`eminiar install\`)`);
56721
56881
  return root;
56722
56882
  })
@@ -56758,7 +56918,7 @@ function runDoctor() {
56758
56918
  checks.push(
56759
56919
  check2("embedding model", () => {
56760
56920
  const dir = paths().models;
56761
- return existsSync14(dir) ? `${dir} (downloaded lazily on first hybrid search)` : "not present";
56921
+ return existsSync15(dir) ? `${dir} (downloaded lazily on first hybrid search)` : "not present";
56762
56922
  })
56763
56923
  );
56764
56924
  return checks;
@@ -56783,18 +56943,18 @@ init_esm_shims();
56783
56943
  init_paths();
56784
56944
  import {
56785
56945
  copyFileSync,
56786
- existsSync as existsSync15,
56946
+ existsSync as existsSync16,
56787
56947
  mkdirSync as mkdirSync6,
56788
56948
  readdirSync as readdirSync5,
56789
56949
  readFileSync as readFileSync15,
56790
56950
  writeFileSync as writeFileSync6
56791
56951
  } from "fs";
56792
56952
  import { homedir as homedir3 } from "os";
56793
- import { dirname as dirname9, join as join21 } from "path";
56953
+ import { dirname as dirname10, join as join21 } from "path";
56794
56954
  var claudeDir = () => process.env.CLAUDE_CONFIG_DIR || join21(homedir3(), ".claude");
56795
56955
  var pluginsDir = () => join21(claudeDir(), "plugins");
56796
56956
  function readJson3(file3) {
56797
- if (!existsSync15(file3)) return {};
56957
+ if (!existsSync16(file3)) return {};
56798
56958
  try {
56799
56959
  return JSON.parse(readFileSync15(file3, "utf8"));
56800
56960
  } catch (err) {
@@ -56937,7 +57097,7 @@ init_esm_shims();
56937
57097
  // src/sandbox/file.ts
56938
57098
  init_esm_shims();
56939
57099
  import { realpathSync as realpathSync2 } from "fs";
56940
- import { isAbsolute, relative as relative8, resolve as resolve7 } from "path";
57100
+ import { isAbsolute, relative as relative9, resolve as resolve7 } from "path";
56941
57101
  function confinePath(root, target, rules) {
56942
57102
  const abs = resolve7(root, target);
56943
57103
  let real;
@@ -56946,7 +57106,7 @@ function confinePath(root, target, rules) {
56946
57106
  } catch {
56947
57107
  throw new Error(`file not found: ${target}`);
56948
57108
  }
56949
- const rel = relative8(root, real);
57109
+ const rel = relative9(root, real);
56950
57110
  const inside = rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
56951
57111
  if (inside) return real;
56952
57112
  if (rules.allow.some((r) => matchRead(r, real))) return real;
@@ -57303,7 +57463,7 @@ init_esm_shims();
57303
57463
 
57304
57464
  // src/code/symbols.ts
57305
57465
  init_esm_shims();
57306
- import { existsSync as existsSync16, readFileSync as readFileSync17 } from "fs";
57466
+ import { existsSync as existsSync17, readFileSync as readFileSync17 } from "fs";
57307
57467
  import { join as join23 } from "path";
57308
57468
 
57309
57469
  // src/code/namepath.ts
@@ -57374,7 +57534,7 @@ function children(db, parent, depth = 1) {
57374
57534
  function symbolBody(db, root, n) {
57375
57535
  if (!n.path || !n.start_line || !n.end_line) return void 0;
57376
57536
  const abs = join23(root, n.path);
57377
- if (existsSync16(abs)) {
57537
+ if (existsSync17(abs)) {
57378
57538
  try {
57379
57539
  return readFileSync17(abs, "utf8").split("\n").slice(n.start_line - 1, n.end_line).join("\n");
57380
57540
  } catch {
@@ -58386,13 +58546,13 @@ function mergeByTool(rows) {
58386
58546
  // src/tools/em_symbols.ts
58387
58547
  init_esm_shims();
58388
58548
  init_backend();
58389
- function fence(body, budget, docId) {
58549
+ function fence(body, budget, docId2) {
58390
58550
  const cut = body.length > budget;
58391
58551
  const shown = cut ? body.slice(0, budget) : body;
58392
58552
  return `\`\`\`
58393
58553
  ${shown}
58394
58554
  \`\`\`${cut ? `
58395
- \u2026 ${body.length - budget} more chars (raise max_tokens${docId ? ` or em_search {document_ids:[${docId}]}` : ""})` : ""}`;
58555
+ \u2026 ${body.length - budget} more chars (raise max_tokens${docId2 ? ` or em_search {document_ids:[${docId2}]}` : ""})` : ""}`;
58396
58556
  }
58397
58557
  function referrerLabel(db, id, nodes) {
58398
58558
  const n = nodes.get(id) ?? db.graphGetNode(id);