eminiar 0.6.2 → 0.8.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/.claude-plugin/plugin.json +1 -1
- package/dist/cli.js +1129 -229
- package/dist/server.js +405 -244
- package/dist/ui/app.global.js +62 -61
- package/dist/ui/styles.css +29 -0
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -26524,9 +26524,9 @@ var require_parse2 = __commonJS({
|
|
|
26524
26524
|
const idx = prev.value.lastIndexOf("[");
|
|
26525
26525
|
const pre = prev.value.slice(0, idx);
|
|
26526
26526
|
const rest3 = prev.value.slice(idx + 2);
|
|
26527
|
-
const
|
|
26528
|
-
if (
|
|
26529
|
-
prev.value = pre +
|
|
26527
|
+
const posix2 = POSIX_REGEX_SOURCE[rest3];
|
|
26528
|
+
if (posix2) {
|
|
26529
|
+
prev.value = pre + posix2;
|
|
26530
26530
|
state.backtrack = true;
|
|
26531
26531
|
advance();
|
|
26532
26532
|
if (!bos.output && tokens.indexOf(prev) === 1) {
|
|
@@ -27048,7 +27048,7 @@ var require_picomatch = __commonJS({
|
|
|
27048
27048
|
throw new TypeError("Expected pattern to be a non-empty string");
|
|
27049
27049
|
}
|
|
27050
27050
|
const opts = options || {};
|
|
27051
|
-
const
|
|
27051
|
+
const posix2 = utils.isWindows(options);
|
|
27052
27052
|
const regex = isState ? picomatch.compileRe(glob2, options) : picomatch.makeRe(glob2, options, false, true);
|
|
27053
27053
|
const state = regex.state;
|
|
27054
27054
|
delete regex.state;
|
|
@@ -27058,8 +27058,8 @@ var require_picomatch = __commonJS({
|
|
|
27058
27058
|
isIgnored = picomatch(opts.ignore, ignoreOpts, returnState);
|
|
27059
27059
|
}
|
|
27060
27060
|
const matcher = (input2, returnObject = false) => {
|
|
27061
|
-
const { isMatch, match, output: output2 } = picomatch.test(input2, regex, options, { glob: glob2, posix });
|
|
27062
|
-
const result = { glob: glob2, state, regex, posix, input: input2, output: output2, match, isMatch };
|
|
27061
|
+
const { isMatch, match, output: output2 } = picomatch.test(input2, regex, options, { glob: glob2, posix: posix2 });
|
|
27062
|
+
const result = { glob: glob2, state, regex, posix: posix2, input: input2, output: output2, match, isMatch };
|
|
27063
27063
|
if (typeof opts.onResult === "function") {
|
|
27064
27064
|
opts.onResult(result);
|
|
27065
27065
|
}
|
|
@@ -27084,7 +27084,7 @@ var require_picomatch = __commonJS({
|
|
|
27084
27084
|
}
|
|
27085
27085
|
return matcher;
|
|
27086
27086
|
};
|
|
27087
|
-
picomatch.test = (input2, regex, options, { glob: glob2, posix } = {}) => {
|
|
27087
|
+
picomatch.test = (input2, regex, options, { glob: glob2, posix: posix2 } = {}) => {
|
|
27088
27088
|
if (typeof input2 !== "string") {
|
|
27089
27089
|
throw new TypeError("Expected input to be a string");
|
|
27090
27090
|
}
|
|
@@ -27092,7 +27092,7 @@ var require_picomatch = __commonJS({
|
|
|
27092
27092
|
return { isMatch: false, output: "" };
|
|
27093
27093
|
}
|
|
27094
27094
|
const opts = options || {};
|
|
27095
|
-
const format = opts.format || (
|
|
27095
|
+
const format = opts.format || (posix2 ? utils.toPosixSlashes : null);
|
|
27096
27096
|
let match = input2 === glob2;
|
|
27097
27097
|
let output2 = match && format ? format(input2) : input2;
|
|
27098
27098
|
if (match === false) {
|
|
@@ -27101,14 +27101,14 @@ var require_picomatch = __commonJS({
|
|
|
27101
27101
|
}
|
|
27102
27102
|
if (match === false || opts.capture === true) {
|
|
27103
27103
|
if (opts.matchBase === true || opts.basename === true) {
|
|
27104
|
-
match = picomatch.matchBase(input2, regex, options,
|
|
27104
|
+
match = picomatch.matchBase(input2, regex, options, posix2);
|
|
27105
27105
|
} else {
|
|
27106
27106
|
match = regex.exec(output2);
|
|
27107
27107
|
}
|
|
27108
27108
|
}
|
|
27109
27109
|
return { isMatch: Boolean(match), match, output: output2 };
|
|
27110
27110
|
};
|
|
27111
|
-
picomatch.matchBase = (input2, glob2, options,
|
|
27111
|
+
picomatch.matchBase = (input2, glob2, options, posix2 = utils.isWindows(options)) => {
|
|
27112
27112
|
const regex = glob2 instanceof RegExp ? glob2 : picomatch.makeRe(glob2, options);
|
|
27113
27113
|
return regex.test(path3.basename(input2));
|
|
27114
27114
|
};
|
|
@@ -27299,9 +27299,9 @@ var require_micromatch = __commonJS({
|
|
|
27299
27299
|
return [].concat(patterns).every((p) => picomatch(p, options)(str2));
|
|
27300
27300
|
};
|
|
27301
27301
|
micromatch.capture = (glob2, input2, options) => {
|
|
27302
|
-
let
|
|
27302
|
+
let posix2 = utils.isWindows(options);
|
|
27303
27303
|
let regex = picomatch.makeRe(String(glob2), { ...options, capture: true });
|
|
27304
|
-
let match = regex.exec(
|
|
27304
|
+
let match = regex.exec(posix2 ? utils.toPosixSlashes(input2) : input2);
|
|
27305
27305
|
if (match) {
|
|
27306
27306
|
return match.slice(1).map((v) => v === void 0 ? "" : v);
|
|
27307
27307
|
}
|
|
@@ -27481,15 +27481,15 @@ var require_pattern = __commonJS({
|
|
|
27481
27481
|
exports.removeDuplicateSlashes = removeDuplicateSlashes;
|
|
27482
27482
|
function partitionAbsoluteAndRelative(patterns) {
|
|
27483
27483
|
const absolute = [];
|
|
27484
|
-
const
|
|
27484
|
+
const relative11 = [];
|
|
27485
27485
|
for (const pattern2 of patterns) {
|
|
27486
27486
|
if (isAbsolute(pattern2)) {
|
|
27487
27487
|
absolute.push(pattern2);
|
|
27488
27488
|
} else {
|
|
27489
|
-
|
|
27489
|
+
relative11.push(pattern2);
|
|
27490
27490
|
}
|
|
27491
27491
|
}
|
|
27492
|
-
return [absolute,
|
|
27492
|
+
return [absolute, relative11];
|
|
27493
27493
|
}
|
|
27494
27494
|
exports.partitionAbsoluteAndRelative = partitionAbsoluteAndRelative;
|
|
27495
27495
|
function isAbsolute(pattern2) {
|
|
@@ -27920,11 +27920,11 @@ var require_out = __commonJS({
|
|
|
27920
27920
|
async.read(path3, getSettings(optionsOrSettingsOrCallback), callback);
|
|
27921
27921
|
}
|
|
27922
27922
|
exports.stat = stat;
|
|
27923
|
-
function
|
|
27923
|
+
function statSync12(path3, optionsOrSettings) {
|
|
27924
27924
|
const settings = getSettings(optionsOrSettings);
|
|
27925
27925
|
return sync.read(path3, settings);
|
|
27926
27926
|
}
|
|
27927
|
-
exports.statSync =
|
|
27927
|
+
exports.statSync = statSync12;
|
|
27928
27928
|
function getSettings(settingsOrOptions = {}) {
|
|
27929
27929
|
if (settingsOrOptions instanceof settings_1.default) {
|
|
27930
27930
|
return settingsOrOptions;
|
|
@@ -29804,19 +29804,19 @@ var require_out4 = __commonJS({
|
|
|
29804
29804
|
return utils.path.convertPathToPattern(source);
|
|
29805
29805
|
}
|
|
29806
29806
|
FastGlob2.convertPathToPattern = convertPathToPattern;
|
|
29807
|
-
let
|
|
29808
|
-
(function(
|
|
29807
|
+
let posix2;
|
|
29808
|
+
(function(posix3) {
|
|
29809
29809
|
function escapePath2(source) {
|
|
29810
29810
|
assertPatternsInput(source);
|
|
29811
29811
|
return utils.path.escapePosixPath(source);
|
|
29812
29812
|
}
|
|
29813
|
-
|
|
29813
|
+
posix3.escapePath = escapePath2;
|
|
29814
29814
|
function convertPathToPattern2(source) {
|
|
29815
29815
|
assertPatternsInput(source);
|
|
29816
29816
|
return utils.path.convertPosixPathToPattern(source);
|
|
29817
29817
|
}
|
|
29818
|
-
|
|
29819
|
-
})(
|
|
29818
|
+
posix3.convertPathToPattern = convertPathToPattern2;
|
|
29819
|
+
})(posix2 = FastGlob2.posix || (FastGlob2.posix = {}));
|
|
29820
29820
|
let win32;
|
|
29821
29821
|
(function(win322) {
|
|
29822
29822
|
function escapePath2(source) {
|
|
@@ -30776,13 +30776,14 @@ function fileOf(id) {
|
|
|
30776
30776
|
if (kind === "sym") return rest2.split("#")[0];
|
|
30777
30777
|
return void 0;
|
|
30778
30778
|
}
|
|
30779
|
-
var fileId, memoryId, symId, pkgId, personId, conceptId;
|
|
30779
|
+
var fileId, memoryId, docId, symId, pkgId, personId, conceptId;
|
|
30780
30780
|
var init_ids = __esm({
|
|
30781
30781
|
"src/graph/ids.ts"() {
|
|
30782
30782
|
"use strict";
|
|
30783
30783
|
init_esm_shims();
|
|
30784
30784
|
fileId = (rel) => `file:${rel}`;
|
|
30785
30785
|
memoryId = (name) => `memory:${name}`;
|
|
30786
|
+
docId = (name) => `doc:${name}`;
|
|
30786
30787
|
symId = (rel, name) => `sym:${rel}#${name}`;
|
|
30787
30788
|
pkgId = (name) => `pkg:${name}`;
|
|
30788
30789
|
personId = (name) => `person:${name}`;
|
|
@@ -31152,9 +31153,10 @@ function impact(db, ref, opts = {}) {
|
|
|
31152
31153
|
frontier = next;
|
|
31153
31154
|
if (!frontier.length) break;
|
|
31154
31155
|
}
|
|
31155
|
-
for (const e of db.graphEdgesTo(start, ["mentions"])) {
|
|
31156
|
+
for (const e of db.graphEdgesTo(start, ["mentions", "documents"])) {
|
|
31156
31157
|
const via = e.dst === root.id ? root.name : e.dst.replace(/^sym:[^#]*#/, "");
|
|
31157
|
-
|
|
31158
|
+
const who = e.kind === "documents" ? "doc documents" : "memory mentions";
|
|
31159
|
+
add(e.src, 0.5 * e.confidence, 1, `${who} ${via}`);
|
|
31158
31160
|
}
|
|
31159
31161
|
if (opts.includeGit !== false) {
|
|
31160
31162
|
const file3 = toFile(root.id);
|
|
@@ -35514,10 +35516,13 @@ __export(store_exports, {
|
|
|
35514
35516
|
isValidName: () => isValidName,
|
|
35515
35517
|
list: () => list,
|
|
35516
35518
|
memoriesDir: () => memoriesDir,
|
|
35519
|
+
memoriesRel: () => memoriesRel,
|
|
35520
|
+
memoryLinks: () => memoryLinks,
|
|
35517
35521
|
normalizeRelated: () => normalizeRelated,
|
|
35518
35522
|
read: () => read,
|
|
35519
35523
|
reconcile: () => reconcile,
|
|
35520
35524
|
relOf: () => relOf,
|
|
35525
|
+
relatedTarget: () => relatedTarget,
|
|
35521
35526
|
remove: () => remove,
|
|
35522
35527
|
rename: () => rename,
|
|
35523
35528
|
setMeta: () => setMeta,
|
|
@@ -35593,6 +35598,17 @@ function normalizeRelated(ids) {
|
|
|
35593
35598
|
}
|
|
35594
35599
|
return [...out];
|
|
35595
35600
|
}
|
|
35601
|
+
function relatedTarget(id) {
|
|
35602
|
+
return id.startsWith("mem:") ? memoryId(id.slice(4)) : id;
|
|
35603
|
+
}
|
|
35604
|
+
function memoryLinks(body, self) {
|
|
35605
|
+
const out = /* @__PURE__ */ new Set();
|
|
35606
|
+
for (const m of body.matchAll(MEM_LINK_RE)) {
|
|
35607
|
+
const name = m[1].replace(/[.-]+$/, "");
|
|
35608
|
+
if (name && name !== self && isValidName(name)) out.add(name);
|
|
35609
|
+
}
|
|
35610
|
+
return [...out];
|
|
35611
|
+
}
|
|
35596
35612
|
function writeMemoryFile(ctx, name, meta4, body) {
|
|
35597
35613
|
const abs = absOf(ctx, name);
|
|
35598
35614
|
mkdirSync7(dirname8(abs), { recursive: true });
|
|
@@ -35600,36 +35616,30 @@ function writeMemoryFile(ctx, name, meta4, body) {
|
|
|
35600
35616
|
${body.trim()}
|
|
35601
35617
|
`));
|
|
35602
35618
|
}
|
|
35603
|
-
function syncGraph(db, name, rel,
|
|
35619
|
+
function syncGraph(db, name, rel, g) {
|
|
35620
|
+
const self = memoryId(name);
|
|
35604
35621
|
db.graphDeleteForPath("curated", rel);
|
|
35605
35622
|
db.graphUpsertNodes([
|
|
35606
35623
|
{
|
|
35607
|
-
id:
|
|
35624
|
+
id: self,
|
|
35608
35625
|
kind: "memory",
|
|
35609
35626
|
name,
|
|
35610
35627
|
path: rel,
|
|
35611
35628
|
source: "curated",
|
|
35612
|
-
meta: { kind, tags }
|
|
35629
|
+
meta: { kind: g.kind, tags: g.tags, ...g.status ? { status: g.status } : {} }
|
|
35613
35630
|
}
|
|
35614
35631
|
]);
|
|
35615
|
-
const edges =
|
|
35616
|
-
|
|
35617
|
-
|
|
35618
|
-
kind
|
|
35619
|
-
|
|
35620
|
-
|
|
35621
|
-
weight: 1
|
|
35622
|
-
}
|
|
35623
|
-
|
|
35624
|
-
|
|
35625
|
-
|
|
35626
|
-
dst: memoryId(supersedes),
|
|
35627
|
-
kind: "supersedes",
|
|
35628
|
-
source: "curated",
|
|
35629
|
-
confidence: 1,
|
|
35630
|
-
weight: 1
|
|
35631
|
-
});
|
|
35632
|
-
}
|
|
35632
|
+
const edges = [];
|
|
35633
|
+
const seen = /* @__PURE__ */ new Set();
|
|
35634
|
+
const push = (dst, kind) => {
|
|
35635
|
+
const key = `${kind}|${dst}`;
|
|
35636
|
+
if (dst === self || seen.has(key)) return;
|
|
35637
|
+
seen.add(key);
|
|
35638
|
+
edges.push({ src: self, dst, kind, source: "curated", confidence: 1, weight: 1 });
|
|
35639
|
+
};
|
|
35640
|
+
for (const id of g.related) push(relatedTarget(id), "mentions");
|
|
35641
|
+
for (const other of memoryLinks(g.body, name)) push(memoryId(other), "links");
|
|
35642
|
+
if (g.supersedes) push(memoryId(g.supersedes.replace(/^mem(?:ory)?:/, "")), "supersedes");
|
|
35633
35643
|
if (edges.length) db.graphUpsertEdges(edges);
|
|
35634
35644
|
}
|
|
35635
35645
|
function forgetInDb(db, name, rel) {
|
|
@@ -35716,7 +35726,7 @@ tags: ${tags.join(", ")}` : ""}`,
|
|
|
35716
35726
|
status,
|
|
35717
35727
|
supersedes
|
|
35718
35728
|
});
|
|
35719
|
-
syncGraph(db, name, rel, kind, tags, related, supersedes);
|
|
35729
|
+
syncGraph(db, name, rel, { kind, tags, related, status, supersedes, body });
|
|
35720
35730
|
return { changed: true, row: db.memoryGetByName(name) };
|
|
35721
35731
|
}
|
|
35722
35732
|
function setMeta(ctx, db, name, patch) {
|
|
@@ -35919,7 +35929,7 @@ function gitignoreHint(ctx) {
|
|
|
35919
35929
|
const ignored = readFileSync16(file3, "utf8").split(/\r?\n/).map((l) => l.trim().replace(/^\//, "").replace(/\/$/, "")).some((l) => l === top || l === dir || l === `${top}/**` || l === `${dir}/**`);
|
|
35920
35930
|
return ignored ? `note: .gitignore ignores ${dir}, so memories stay local to this machine (remove that line to share them with the team)` : void 0;
|
|
35921
35931
|
}
|
|
35922
|
-
var import_fast_glob3, MEMORY_KINDS, SEGMENT, NAME_RE, hinted;
|
|
35932
|
+
var import_fast_glob3, MEMORY_KINDS, SEGMENT, NAME_RE, MEM_LINK_RE, hinted;
|
|
35923
35933
|
var init_store = __esm({
|
|
35924
35934
|
"src/memory/store.ts"() {
|
|
35925
35935
|
"use strict";
|
|
@@ -35941,6 +35951,7 @@ var init_store = __esm({
|
|
|
35941
35951
|
];
|
|
35942
35952
|
SEGMENT = "[a-z0-9][a-z0-9._-]*";
|
|
35943
35953
|
NAME_RE = new RegExp(`^(?:${SEGMENT}/)?${SEGMENT}$`);
|
|
35954
|
+
MEM_LINK_RE = /(?<![A-Za-z0-9_./-])mem:([a-z0-9][a-z0-9._-]*(?:\/[a-z0-9][a-z0-9._-]*)?)/g;
|
|
35944
35955
|
hinted = /* @__PURE__ */ new Set();
|
|
35945
35956
|
}
|
|
35946
35957
|
});
|
|
@@ -36626,7 +36637,7 @@ function architecture(ctx, db, aspects = ASPECTS) {
|
|
|
36626
36637
|
for (const l of languages2.values()) langCounts[l] = (langCounts[l] ?? 0) + 1;
|
|
36627
36638
|
const counts = db.graphCounts();
|
|
36628
36639
|
const nodeCounts = db.graphNodeCounts();
|
|
36629
|
-
const symbols = Object.entries(nodeCounts).filter(([k]) => !["file", "package", "person", "concept", "memory"].includes(k)).reduce((a, [, n]) => a + n, 0);
|
|
36640
|
+
const symbols = Object.entries(nodeCounts).filter(([k]) => !["file", "package", "person", "concept", "memory", "doc"].includes(k)).reduce((a, [, n]) => a + n, 0);
|
|
36630
36641
|
const out = {
|
|
36631
36642
|
project: {
|
|
36632
36643
|
name: typeof pkg.name === "string" ? pkg.name : void 0,
|
|
@@ -39030,6 +39041,341 @@ var init_discover = __esm({
|
|
|
39030
39041
|
}
|
|
39031
39042
|
});
|
|
39032
39043
|
|
|
39044
|
+
// src/docs/category.ts
|
|
39045
|
+
function categoryInfo(id) {
|
|
39046
|
+
return BY_ID.get(id) ?? BY_ID.get("local");
|
|
39047
|
+
}
|
|
39048
|
+
function localCategory(s) {
|
|
39049
|
+
const title = s.title ?? "";
|
|
39050
|
+
for (const [re, cat] of TITLE_RULES) if (re.test(title)) return cat;
|
|
39051
|
+
for (const [re, cat] of NAME_RULES) if (re.test(s.name)) return cat;
|
|
39052
|
+
const path3 = s.path ?? "";
|
|
39053
|
+
if (/(^|\/)(adrs?|decisions)$/i.test(path3)) return "adr";
|
|
39054
|
+
const globs = s.glob ?? [];
|
|
39055
|
+
const hits = /* @__PURE__ */ new Map();
|
|
39056
|
+
for (const g of globs)
|
|
39057
|
+
for (const [re, cat] of GLOB_RULES)
|
|
39058
|
+
if (re.test(g)) {
|
|
39059
|
+
hits.set(cat, (hits.get(cat) ?? 0) + 1);
|
|
39060
|
+
break;
|
|
39061
|
+
}
|
|
39062
|
+
if (hits.size) return [...hits.entries()].sort((a, b) => b[1] - a[1])[0][0];
|
|
39063
|
+
if (/(^|\/)(docs?|documentation)$/i.test(path3)) return "guides";
|
|
39064
|
+
if (/README|CONTRIBUTING|CHANGELOG/i.test(path3)) return "readme";
|
|
39065
|
+
return "local";
|
|
39066
|
+
}
|
|
39067
|
+
function docCategory(s, entry) {
|
|
39068
|
+
if (s.kind === "path") return localCategory(s);
|
|
39069
|
+
if (entry) {
|
|
39070
|
+
const tier = entry.tier ?? "library";
|
|
39071
|
+
return tier === "service" ? "service" : tier === "infra" ? "infra" : "library";
|
|
39072
|
+
}
|
|
39073
|
+
const provider = s.provider ?? "";
|
|
39074
|
+
const i = provider.indexOf(":");
|
|
39075
|
+
const kind = i === -1 ? provider : provider.slice(0, i);
|
|
39076
|
+
switch (kind) {
|
|
39077
|
+
case "api":
|
|
39078
|
+
return "api";
|
|
39079
|
+
case "env":
|
|
39080
|
+
return "service";
|
|
39081
|
+
case "image":
|
|
39082
|
+
case "files":
|
|
39083
|
+
return "infra";
|
|
39084
|
+
case "npm":
|
|
39085
|
+
case "pypi":
|
|
39086
|
+
case "go":
|
|
39087
|
+
return "library";
|
|
39088
|
+
default:
|
|
39089
|
+
return "web";
|
|
39090
|
+
}
|
|
39091
|
+
}
|
|
39092
|
+
function categoryRank(id) {
|
|
39093
|
+
const i = DOC_CATEGORIES.findIndex((c) => c.id === id);
|
|
39094
|
+
return i === -1 ? DOC_CATEGORIES.length : i;
|
|
39095
|
+
}
|
|
39096
|
+
var DOC_CATEGORIES, BY_ID, GROUP_LABELS, TITLE_RULES, NAME_RULES, GLOB_RULES;
|
|
39097
|
+
var init_category = __esm({
|
|
39098
|
+
"src/docs/category.ts"() {
|
|
39099
|
+
"use strict";
|
|
39100
|
+
init_esm_shims();
|
|
39101
|
+
DOC_CATEGORIES = [
|
|
39102
|
+
{
|
|
39103
|
+
id: "readme",
|
|
39104
|
+
group: "project",
|
|
39105
|
+
label: "Readme & guides",
|
|
39106
|
+
blurb: "README, CONTRIBUTING, CHANGELOG and other top-level guides"
|
|
39107
|
+
},
|
|
39108
|
+
{
|
|
39109
|
+
id: "guides",
|
|
39110
|
+
group: "project",
|
|
39111
|
+
label: "Docs folders",
|
|
39112
|
+
blurb: "docs/ directories: handbooks, how-tos, references"
|
|
39113
|
+
},
|
|
39114
|
+
{
|
|
39115
|
+
id: "adr",
|
|
39116
|
+
group: "project",
|
|
39117
|
+
label: "Decision records",
|
|
39118
|
+
blurb: "architecture decision records"
|
|
39119
|
+
},
|
|
39120
|
+
{
|
|
39121
|
+
id: "agent-instructions",
|
|
39122
|
+
group: "project",
|
|
39123
|
+
label: "Agent instructions",
|
|
39124
|
+
blurb: "Copilot, Claude and Cursor instructions, agents and skills"
|
|
39125
|
+
},
|
|
39126
|
+
{
|
|
39127
|
+
id: "api-specs",
|
|
39128
|
+
group: "project",
|
|
39129
|
+
label: "API specs",
|
|
39130
|
+
blurb: "OpenAPI, Postman, GraphQL and protobuf definitions"
|
|
39131
|
+
},
|
|
39132
|
+
{
|
|
39133
|
+
id: "env-reference",
|
|
39134
|
+
group: "project",
|
|
39135
|
+
label: "Environment reference",
|
|
39136
|
+
blurb: ".env examples: every variable the project reads"
|
|
39137
|
+
},
|
|
39138
|
+
{
|
|
39139
|
+
id: "deploy-config",
|
|
39140
|
+
group: "project",
|
|
39141
|
+
label: "Deploy config",
|
|
39142
|
+
blurb: "Terraform, Kubernetes, Helm, Compose and CI workflows"
|
|
39143
|
+
},
|
|
39144
|
+
{ id: "local", group: "project", label: "Other local files", blurb: "hand-registered paths" },
|
|
39145
|
+
{
|
|
39146
|
+
id: "service",
|
|
39147
|
+
group: "external",
|
|
39148
|
+
label: "Services",
|
|
39149
|
+
blurb: "third-party products and APIs the project integrates"
|
|
39150
|
+
},
|
|
39151
|
+
{
|
|
39152
|
+
id: "api",
|
|
39153
|
+
group: "external",
|
|
39154
|
+
label: "External APIs",
|
|
39155
|
+
blurb: "hosts the code calls, found by probing their docs"
|
|
39156
|
+
},
|
|
39157
|
+
{
|
|
39158
|
+
id: "infra",
|
|
39159
|
+
group: "external",
|
|
39160
|
+
label: "Infrastructure",
|
|
39161
|
+
blurb: "platforms the project runs on"
|
|
39162
|
+
},
|
|
39163
|
+
{ id: "library", group: "external", label: "Libraries", blurb: "frameworks and packages" },
|
|
39164
|
+
{ id: "web", group: "external", label: "Web pages", blurb: "hand-registered URLs" }
|
|
39165
|
+
];
|
|
39166
|
+
BY_ID = new Map(DOC_CATEGORIES.map((c) => [c.id, c]));
|
|
39167
|
+
GROUP_LABELS = {
|
|
39168
|
+
project: "Project documentation",
|
|
39169
|
+
external: "External documentation"
|
|
39170
|
+
};
|
|
39171
|
+
TITLE_RULES = [
|
|
39172
|
+
[/^project readme/i, "readme"],
|
|
39173
|
+
[/^(docs|doc|documentation)\/ directory$/i, "guides"],
|
|
39174
|
+
[/decision records/i, "adr"],
|
|
39175
|
+
[/copilot|claude|cursor/i, "agent-instructions"],
|
|
39176
|
+
[/openapi|postman|graphql|protobuf/i, "api-specs"],
|
|
39177
|
+
[/environment variable/i, "env-reference"],
|
|
39178
|
+
[/terraform|kubernetes|helm|compose|workflow/i, "deploy-config"]
|
|
39179
|
+
];
|
|
39180
|
+
NAME_RULES = [
|
|
39181
|
+
[/(^|-)readme$/, "readme"],
|
|
39182
|
+
[/(^|-)docs$/, "guides"],
|
|
39183
|
+
[/(^|-)adrs?$/, "adr"],
|
|
39184
|
+
[/(^|-)agent-instructions$/, "agent-instructions"],
|
|
39185
|
+
[/(^|-)api-specs$/, "api-specs"],
|
|
39186
|
+
[/(^|-)env-example$/, "env-reference"],
|
|
39187
|
+
[/(^|-)deploy-config$/, "deploy-config"]
|
|
39188
|
+
];
|
|
39189
|
+
GLOB_RULES = [
|
|
39190
|
+
[/openapi|swagger|postman_collection|asyncapi|\.graphql$|\.proto$/i, "api-specs"],
|
|
39191
|
+
[/^\.env\./, "env-reference"],
|
|
39192
|
+
[
|
|
39193
|
+
/\.tf$|tfvars|^k8s\/|^helm\/|^charts\/|^manifests\/|compose.*\.ya?ml|\.github\/workflows/i,
|
|
39194
|
+
"deploy-config"
|
|
39195
|
+
],
|
|
39196
|
+
[
|
|
39197
|
+
/copilot-instructions|\.github\/(instructions|agents|skills)|\.claude\/(agents|skills)|\.cursor\/rules|^(AGENTS|CLAUDE)\.md$/i,
|
|
39198
|
+
"agent-instructions"
|
|
39199
|
+
],
|
|
39200
|
+
[/^(README|CONTRIBUTING|CHANGELOG|SECURITY)(\.md)?$/i, "readme"],
|
|
39201
|
+
[/^\*\*\/\*\.(md|mdx|rst|txt)$/i, "guides"]
|
|
39202
|
+
];
|
|
39203
|
+
}
|
|
39204
|
+
});
|
|
39205
|
+
|
|
39206
|
+
// src/docs/graph.ts
|
|
39207
|
+
import { existsSync as existsSync21, statSync as statSync9 } from "fs";
|
|
39208
|
+
import { dirname as dirname10, posix, relative as relative8, sep as sep2 } from "path";
|
|
39209
|
+
function manifestRel(ctx) {
|
|
39210
|
+
return relative8(ctx.root, manifestPath(ctx)).split(sep2).join("/");
|
|
39211
|
+
}
|
|
39212
|
+
function packageCandidates(provider) {
|
|
39213
|
+
if (!provider) return [];
|
|
39214
|
+
const i = provider.indexOf(":");
|
|
39215
|
+
if (i === -1) return [];
|
|
39216
|
+
const kind = provider.slice(0, i);
|
|
39217
|
+
const name = provider.slice(i + 1).trim();
|
|
39218
|
+
if (!name) return [];
|
|
39219
|
+
if (kind === "npm" || kind === "go") return [pkgId(name)];
|
|
39220
|
+
if (kind === "pypi") {
|
|
39221
|
+
const lower = name.toLowerCase();
|
|
39222
|
+
return [.../* @__PURE__ */ new Set([pkgId(lower.replace(/-/g, "_")), pkgId(lower), pkgId(name)])];
|
|
39223
|
+
}
|
|
39224
|
+
return [];
|
|
39225
|
+
}
|
|
39226
|
+
function pathsIn(text3, pageRel, files) {
|
|
39227
|
+
const out = /* @__PURE__ */ new Set();
|
|
39228
|
+
for (const m of text3.matchAll(PATH_RE)) {
|
|
39229
|
+
const raw = m[1];
|
|
39230
|
+
const tries = [raw.replace(/^(\.\/)+/, "")];
|
|
39231
|
+
if (pageRel && raw.startsWith("."))
|
|
39232
|
+
tries.unshift(posix.normalize(posix.join(dirname10(pageRel), raw)));
|
|
39233
|
+
for (const t of tries) {
|
|
39234
|
+
if (files.has(t)) {
|
|
39235
|
+
out.add(t);
|
|
39236
|
+
break;
|
|
39237
|
+
}
|
|
39238
|
+
}
|
|
39239
|
+
}
|
|
39240
|
+
return [...out];
|
|
39241
|
+
}
|
|
39242
|
+
function pageRelOf(ctx, uri) {
|
|
39243
|
+
if (!uri?.startsWith("file://")) return void 0;
|
|
39244
|
+
const rel = relative8(ctx.root, uri.slice("file://".length)).split(sep2).join("/");
|
|
39245
|
+
return rel.startsWith("..") ? void 0 : rel;
|
|
39246
|
+
}
|
|
39247
|
+
function localEdges(ctx, db, src, files) {
|
|
39248
|
+
const id = docId(src.name);
|
|
39249
|
+
const hits = /* @__PURE__ */ new Map();
|
|
39250
|
+
for (const page of db.documentsBySource(docsTag(src.name), MAX_PAGES)) {
|
|
39251
|
+
const rel = pageRelOf(ctx, page.uri);
|
|
39252
|
+
if (rel && files.has(rel)) hits.set(rel, { via: "covers", count: 1 });
|
|
39253
|
+
const text3 = db.chunksOfDocument(page.id).map((c) => c.body).join("\n");
|
|
39254
|
+
for (const f of pathsIn(text3, rel, files)) {
|
|
39255
|
+
const h = hits.get(f);
|
|
39256
|
+
if (h) h.count++;
|
|
39257
|
+
else hits.set(f, { via: "mentions", count: 1 });
|
|
39258
|
+
}
|
|
39259
|
+
if (hits.size >= MAX_EDGES) break;
|
|
39260
|
+
}
|
|
39261
|
+
return [...hits].sort((a, b) => b[1].count - a[1].count).slice(0, MAX_EDGES).map(([rel, h]) => ({
|
|
39262
|
+
src: id,
|
|
39263
|
+
dst: `file:${rel}`,
|
|
39264
|
+
kind: DOCUMENTS_EDGE,
|
|
39265
|
+
source: "curated",
|
|
39266
|
+
confidence: h.via === "covers" ? 1 : 0.8,
|
|
39267
|
+
weight: Math.min(1, 0.5 + h.count / 10),
|
|
39268
|
+
meta: { via: h.via, count: h.count }
|
|
39269
|
+
}));
|
|
39270
|
+
}
|
|
39271
|
+
function packageEdges(src, packages) {
|
|
39272
|
+
const id = docId(src.name);
|
|
39273
|
+
const cands = packageCandidates(src.provider);
|
|
39274
|
+
if (!cands.length) return [];
|
|
39275
|
+
let targets = cands.filter((c) => packages.has(c));
|
|
39276
|
+
if (src.provider?.startsWith("go:"))
|
|
39277
|
+
targets = [...targets, ...[...packages].filter((p) => p.startsWith(`${cands[0]}/`))];
|
|
39278
|
+
if (!targets.length) targets = [cands[0]];
|
|
39279
|
+
return [...new Set(targets)].map((dst) => ({
|
|
39280
|
+
src: id,
|
|
39281
|
+
dst,
|
|
39282
|
+
kind: DOCUMENTS_EDGE,
|
|
39283
|
+
source: "curated",
|
|
39284
|
+
confidence: 1,
|
|
39285
|
+
weight: 1,
|
|
39286
|
+
meta: { via: "provider" }
|
|
39287
|
+
}));
|
|
39288
|
+
}
|
|
39289
|
+
function localDocPages(ctx, db) {
|
|
39290
|
+
const out = /* @__PURE__ */ new Map();
|
|
39291
|
+
for (const s of readManifest(ctx).sources) {
|
|
39292
|
+
if (s.kind !== "path") continue;
|
|
39293
|
+
for (const page of db.documentsBySource(docsTag(s.name), MAX_PAGES)) {
|
|
39294
|
+
const rel = pageRelOf(ctx, page.uri);
|
|
39295
|
+
if (!rel) continue;
|
|
39296
|
+
const names = out.get(rel) ?? [];
|
|
39297
|
+
if (!names.includes(s.name)) names.push(s.name);
|
|
39298
|
+
out.set(rel, names);
|
|
39299
|
+
}
|
|
39300
|
+
}
|
|
39301
|
+
return out;
|
|
39302
|
+
}
|
|
39303
|
+
function stampOf(ctx, db) {
|
|
39304
|
+
const file3 = manifestPath(ctx);
|
|
39305
|
+
const st = existsSync21(file3) ? statSync9(file3) : void 0;
|
|
39306
|
+
const counts = db.graphNodeCounts();
|
|
39307
|
+
const syncs = readManifest(ctx).sources.map((s) => syncState(db, s.name)?.at ?? 0).join(",");
|
|
39308
|
+
return `${st ? `${Math.floor(st.mtimeMs)}:${st.size}` : "none"}|${counts.file ?? 0}|${counts.package ?? 0}|${syncs}`;
|
|
39309
|
+
}
|
|
39310
|
+
function syncDocsGraph(ctx, db) {
|
|
39311
|
+
const rel = manifestRel(ctx);
|
|
39312
|
+
const m = readManifest(ctx);
|
|
39313
|
+
const catalog = mergedCatalog(ctx.config.docs.catalog);
|
|
39314
|
+
const files = new Set(
|
|
39315
|
+
db.graphFileNodes().filter((n) => n.source === "tree-sitter" && n.path).map((n) => n.path)
|
|
39316
|
+
);
|
|
39317
|
+
const packages = new Set(db.graphNodesByKind(["package"]).map((n) => n.id));
|
|
39318
|
+
const nodes = [];
|
|
39319
|
+
const edges = [];
|
|
39320
|
+
for (const s of m.sources) {
|
|
39321
|
+
const category = docCategory(
|
|
39322
|
+
s,
|
|
39323
|
+
s.provider ? catalogForProvider(s.provider, catalog) : void 0
|
|
39324
|
+
);
|
|
39325
|
+
nodes.push({
|
|
39326
|
+
id: docId(s.name),
|
|
39327
|
+
kind: "doc",
|
|
39328
|
+
name: s.name,
|
|
39329
|
+
path: rel,
|
|
39330
|
+
source: "curated",
|
|
39331
|
+
meta: {
|
|
39332
|
+
category,
|
|
39333
|
+
group: categoryInfo(category).group,
|
|
39334
|
+
sourceKind: s.kind,
|
|
39335
|
+
...s.provider ? { provider: s.provider } : {},
|
|
39336
|
+
...s.title ? { title: s.title } : {},
|
|
39337
|
+
...s.tags?.length ? { tags: s.tags } : {},
|
|
39338
|
+
...s.kind === "path" ? { target: s.path } : { url: s.urls?.[0] },
|
|
39339
|
+
documents: db.countDocuments(docsTag(s.name))
|
|
39340
|
+
}
|
|
39341
|
+
});
|
|
39342
|
+
edges.push(...packageEdges(s, packages));
|
|
39343
|
+
if (s.kind === "path") edges.push(...localEdges(ctx, db, s, files));
|
|
39344
|
+
}
|
|
39345
|
+
db.graphDeleteForPath("curated", rel, { keepFile: true });
|
|
39346
|
+
if (nodes.length) db.graphUpsertNodes(nodes);
|
|
39347
|
+
if (edges.length) db.graphUpsertEdges(edges);
|
|
39348
|
+
db.metaSet(STAMP_KEY, stampOf(ctx, db));
|
|
39349
|
+
return { sources: nodes.length, edges: edges.length };
|
|
39350
|
+
}
|
|
39351
|
+
function ensureDocsGraph(ctx, db) {
|
|
39352
|
+
if (db.metaGet(STAMP_KEY) === stampOf(ctx, db)) return false;
|
|
39353
|
+
syncDocsGraph(ctx, db);
|
|
39354
|
+
return true;
|
|
39355
|
+
}
|
|
39356
|
+
function syncDocsGraphQuietly(ctx, db) {
|
|
39357
|
+
try {
|
|
39358
|
+
syncDocsGraph(ctx, db);
|
|
39359
|
+
} catch {
|
|
39360
|
+
}
|
|
39361
|
+
}
|
|
39362
|
+
var DOCUMENTS_EDGE, MAX_PAGES, MAX_EDGES, PATH_RE, STAMP_KEY;
|
|
39363
|
+
var init_graph = __esm({
|
|
39364
|
+
"src/docs/graph.ts"() {
|
|
39365
|
+
"use strict";
|
|
39366
|
+
init_esm_shims();
|
|
39367
|
+
init_ids();
|
|
39368
|
+
init_catalog();
|
|
39369
|
+
init_category();
|
|
39370
|
+
init_manifest();
|
|
39371
|
+
DOCUMENTS_EDGE = "documents";
|
|
39372
|
+
MAX_PAGES = 500;
|
|
39373
|
+
MAX_EDGES = 400;
|
|
39374
|
+
PATH_RE = /(?:^|[\s(`'"[<])((?:\.{1,2}\/)*(?:[\w@.-]+\/)+[\w@.-]+\.[A-Za-z0-9]{1,8})(?=$|[\s)`'"\]>:#,;!?]|\.(?:\s|$))/gm;
|
|
39375
|
+
STAMP_KEY = "docs.graph.stamp";
|
|
39376
|
+
}
|
|
39377
|
+
});
|
|
39378
|
+
|
|
39033
39379
|
// src/docs/resolve.ts
|
|
39034
39380
|
async function getJson(url2, opts) {
|
|
39035
39381
|
const f = opts.fetchImpl ?? fetch;
|
|
@@ -39914,6 +40260,7 @@ async function syncSources(ctx, db, names, opts = {}) {
|
|
|
39914
40260
|
});
|
|
39915
40261
|
}
|
|
39916
40262
|
for (const src of wanted) out.push(await syncSource(ctx, db, src, opts));
|
|
40263
|
+
syncDocsGraphQuietly(ctx, db);
|
|
39917
40264
|
return out;
|
|
39918
40265
|
}
|
|
39919
40266
|
function formatSync(results) {
|
|
@@ -39943,6 +40290,7 @@ var init_sync = __esm({
|
|
|
39943
40290
|
init_queue();
|
|
39944
40291
|
init_fetcher();
|
|
39945
40292
|
init_indexer();
|
|
40293
|
+
init_graph();
|
|
39946
40294
|
init_manifest();
|
|
39947
40295
|
init_specs();
|
|
39948
40296
|
SKIP_EXT2 = /\.(png|jpe?g|gif|svg|webp|ico|css|js|mjs|map|zip|gz|tgz|tar|pdf|woff2?|ttf|otf|eot|mp4|mp3|webm|xml|rss|atom|wasm)$/i;
|
|
@@ -40086,12 +40434,14 @@ async function addDocSources(ctx, db, inputs, opts = {}) {
|
|
|
40086
40434
|
const names = [...out.added, ...out.updated];
|
|
40087
40435
|
if (opts.sync !== false && names.length)
|
|
40088
40436
|
out.sync = await syncSources(ctx, db, names, { force: opts.force });
|
|
40437
|
+
else if (names.length) syncDocsGraphQuietly(ctx, db);
|
|
40089
40438
|
return out;
|
|
40090
40439
|
}
|
|
40091
40440
|
function removeDocSource(ctx, db, name) {
|
|
40092
40441
|
const removed = removeSource(ctx, name);
|
|
40093
40442
|
const documents = db.deleteDocumentsBySource(docsTag(name));
|
|
40094
40443
|
clearSyncState(db, name);
|
|
40444
|
+
if (removed) syncDocsGraphQuietly(ctx, db);
|
|
40095
40445
|
return { removed, documents };
|
|
40096
40446
|
}
|
|
40097
40447
|
function formatAdd(o) {
|
|
@@ -40125,6 +40475,7 @@ var init_add = __esm({
|
|
|
40125
40475
|
init_esm_shims();
|
|
40126
40476
|
init_catalog();
|
|
40127
40477
|
init_discover();
|
|
40478
|
+
init_graph();
|
|
40128
40479
|
init_manifest();
|
|
40129
40480
|
init_resolve2();
|
|
40130
40481
|
init_sync();
|
|
@@ -40141,168 +40492,6 @@ var init_add = __esm({
|
|
|
40141
40492
|
}
|
|
40142
40493
|
});
|
|
40143
40494
|
|
|
40144
|
-
// src/docs/category.ts
|
|
40145
|
-
function categoryInfo(id) {
|
|
40146
|
-
return BY_ID.get(id) ?? BY_ID.get("local");
|
|
40147
|
-
}
|
|
40148
|
-
function localCategory(s) {
|
|
40149
|
-
const title = s.title ?? "";
|
|
40150
|
-
for (const [re, cat] of TITLE_RULES) if (re.test(title)) return cat;
|
|
40151
|
-
for (const [re, cat] of NAME_RULES) if (re.test(s.name)) return cat;
|
|
40152
|
-
const path3 = s.path ?? "";
|
|
40153
|
-
if (/(^|\/)(adrs?|decisions)$/i.test(path3)) return "adr";
|
|
40154
|
-
const globs = s.glob ?? [];
|
|
40155
|
-
const hits = /* @__PURE__ */ new Map();
|
|
40156
|
-
for (const g of globs)
|
|
40157
|
-
for (const [re, cat] of GLOB_RULES)
|
|
40158
|
-
if (re.test(g)) {
|
|
40159
|
-
hits.set(cat, (hits.get(cat) ?? 0) + 1);
|
|
40160
|
-
break;
|
|
40161
|
-
}
|
|
40162
|
-
if (hits.size) return [...hits.entries()].sort((a, b) => b[1] - a[1])[0][0];
|
|
40163
|
-
if (/(^|\/)(docs?|documentation)$/i.test(path3)) return "guides";
|
|
40164
|
-
if (/README|CONTRIBUTING|CHANGELOG/i.test(path3)) return "readme";
|
|
40165
|
-
return "local";
|
|
40166
|
-
}
|
|
40167
|
-
function docCategory(s, entry) {
|
|
40168
|
-
if (s.kind === "path") return localCategory(s);
|
|
40169
|
-
if (entry) {
|
|
40170
|
-
const tier = entry.tier ?? "library";
|
|
40171
|
-
return tier === "service" ? "service" : tier === "infra" ? "infra" : "library";
|
|
40172
|
-
}
|
|
40173
|
-
const provider = s.provider ?? "";
|
|
40174
|
-
const i = provider.indexOf(":");
|
|
40175
|
-
const kind = i === -1 ? provider : provider.slice(0, i);
|
|
40176
|
-
switch (kind) {
|
|
40177
|
-
case "api":
|
|
40178
|
-
return "api";
|
|
40179
|
-
case "env":
|
|
40180
|
-
return "service";
|
|
40181
|
-
case "image":
|
|
40182
|
-
case "files":
|
|
40183
|
-
return "infra";
|
|
40184
|
-
case "npm":
|
|
40185
|
-
case "pypi":
|
|
40186
|
-
case "go":
|
|
40187
|
-
return "library";
|
|
40188
|
-
default:
|
|
40189
|
-
return "web";
|
|
40190
|
-
}
|
|
40191
|
-
}
|
|
40192
|
-
function categoryRank(id) {
|
|
40193
|
-
const i = DOC_CATEGORIES.findIndex((c) => c.id === id);
|
|
40194
|
-
return i === -1 ? DOC_CATEGORIES.length : i;
|
|
40195
|
-
}
|
|
40196
|
-
var DOC_CATEGORIES, BY_ID, GROUP_LABELS, TITLE_RULES, NAME_RULES, GLOB_RULES;
|
|
40197
|
-
var init_category = __esm({
|
|
40198
|
-
"src/docs/category.ts"() {
|
|
40199
|
-
"use strict";
|
|
40200
|
-
init_esm_shims();
|
|
40201
|
-
DOC_CATEGORIES = [
|
|
40202
|
-
{
|
|
40203
|
-
id: "readme",
|
|
40204
|
-
group: "project",
|
|
40205
|
-
label: "Readme & guides",
|
|
40206
|
-
blurb: "README, CONTRIBUTING, CHANGELOG and other top-level guides"
|
|
40207
|
-
},
|
|
40208
|
-
{
|
|
40209
|
-
id: "guides",
|
|
40210
|
-
group: "project",
|
|
40211
|
-
label: "Docs folders",
|
|
40212
|
-
blurb: "docs/ directories: handbooks, how-tos, references"
|
|
40213
|
-
},
|
|
40214
|
-
{
|
|
40215
|
-
id: "adr",
|
|
40216
|
-
group: "project",
|
|
40217
|
-
label: "Decision records",
|
|
40218
|
-
blurb: "architecture decision records"
|
|
40219
|
-
},
|
|
40220
|
-
{
|
|
40221
|
-
id: "agent-instructions",
|
|
40222
|
-
group: "project",
|
|
40223
|
-
label: "Agent instructions",
|
|
40224
|
-
blurb: "Copilot, Claude and Cursor instructions, agents and skills"
|
|
40225
|
-
},
|
|
40226
|
-
{
|
|
40227
|
-
id: "api-specs",
|
|
40228
|
-
group: "project",
|
|
40229
|
-
label: "API specs",
|
|
40230
|
-
blurb: "OpenAPI, Postman, GraphQL and protobuf definitions"
|
|
40231
|
-
},
|
|
40232
|
-
{
|
|
40233
|
-
id: "env-reference",
|
|
40234
|
-
group: "project",
|
|
40235
|
-
label: "Environment reference",
|
|
40236
|
-
blurb: ".env examples: every variable the project reads"
|
|
40237
|
-
},
|
|
40238
|
-
{
|
|
40239
|
-
id: "deploy-config",
|
|
40240
|
-
group: "project",
|
|
40241
|
-
label: "Deploy config",
|
|
40242
|
-
blurb: "Terraform, Kubernetes, Helm, Compose and CI workflows"
|
|
40243
|
-
},
|
|
40244
|
-
{ id: "local", group: "project", label: "Other local files", blurb: "hand-registered paths" },
|
|
40245
|
-
{
|
|
40246
|
-
id: "service",
|
|
40247
|
-
group: "external",
|
|
40248
|
-
label: "Services",
|
|
40249
|
-
blurb: "third-party products and APIs the project integrates"
|
|
40250
|
-
},
|
|
40251
|
-
{
|
|
40252
|
-
id: "api",
|
|
40253
|
-
group: "external",
|
|
40254
|
-
label: "External APIs",
|
|
40255
|
-
blurb: "hosts the code calls, found by probing their docs"
|
|
40256
|
-
},
|
|
40257
|
-
{
|
|
40258
|
-
id: "infra",
|
|
40259
|
-
group: "external",
|
|
40260
|
-
label: "Infrastructure",
|
|
40261
|
-
blurb: "platforms the project runs on"
|
|
40262
|
-
},
|
|
40263
|
-
{ id: "library", group: "external", label: "Libraries", blurb: "frameworks and packages" },
|
|
40264
|
-
{ id: "web", group: "external", label: "Web pages", blurb: "hand-registered URLs" }
|
|
40265
|
-
];
|
|
40266
|
-
BY_ID = new Map(DOC_CATEGORIES.map((c) => [c.id, c]));
|
|
40267
|
-
GROUP_LABELS = {
|
|
40268
|
-
project: "Project documentation",
|
|
40269
|
-
external: "External documentation"
|
|
40270
|
-
};
|
|
40271
|
-
TITLE_RULES = [
|
|
40272
|
-
[/^project readme/i, "readme"],
|
|
40273
|
-
[/^(docs|doc|documentation)\/ directory$/i, "guides"],
|
|
40274
|
-
[/decision records/i, "adr"],
|
|
40275
|
-
[/copilot|claude|cursor/i, "agent-instructions"],
|
|
40276
|
-
[/openapi|postman|graphql|protobuf/i, "api-specs"],
|
|
40277
|
-
[/environment variable/i, "env-reference"],
|
|
40278
|
-
[/terraform|kubernetes|helm|compose|workflow/i, "deploy-config"]
|
|
40279
|
-
];
|
|
40280
|
-
NAME_RULES = [
|
|
40281
|
-
[/(^|-)readme$/, "readme"],
|
|
40282
|
-
[/(^|-)docs$/, "guides"],
|
|
40283
|
-
[/(^|-)adrs?$/, "adr"],
|
|
40284
|
-
[/(^|-)agent-instructions$/, "agent-instructions"],
|
|
40285
|
-
[/(^|-)api-specs$/, "api-specs"],
|
|
40286
|
-
[/(^|-)env-example$/, "env-reference"],
|
|
40287
|
-
[/(^|-)deploy-config$/, "deploy-config"]
|
|
40288
|
-
];
|
|
40289
|
-
GLOB_RULES = [
|
|
40290
|
-
[/openapi|swagger|postman_collection|asyncapi|\.graphql$|\.proto$/i, "api-specs"],
|
|
40291
|
-
[/^\.env\./, "env-reference"],
|
|
40292
|
-
[
|
|
40293
|
-
/\.tf$|tfvars|^k8s\/|^helm\/|^charts\/|^manifests\/|compose.*\.ya?ml|\.github\/workflows/i,
|
|
40294
|
-
"deploy-config"
|
|
40295
|
-
],
|
|
40296
|
-
[
|
|
40297
|
-
/copilot-instructions|\.github\/(instructions|agents|skills)|\.claude\/(agents|skills)|\.cursor\/rules|^(AGENTS|CLAUDE)\.md$/i,
|
|
40298
|
-
"agent-instructions"
|
|
40299
|
-
],
|
|
40300
|
-
[/^(README|CONTRIBUTING|CHANGELOG|SECURITY)(\.md)?$/i, "readme"],
|
|
40301
|
-
[/^\*\*\/\*\.(md|mdx|rst|txt)$/i, "guides"]
|
|
40302
|
-
];
|
|
40303
|
-
}
|
|
40304
|
-
});
|
|
40305
|
-
|
|
40306
40495
|
// src/tools/em_docs.ts
|
|
40307
40496
|
var em_docs_exports = {};
|
|
40308
40497
|
__export(em_docs_exports, {
|
|
@@ -41003,8 +41192,8 @@ var init_em_search = __esm({
|
|
|
41003
41192
|
});
|
|
41004
41193
|
|
|
41005
41194
|
// src/ui/api.ts
|
|
41006
|
-
import { existsSync as
|
|
41007
|
-
import { join as join26, relative as
|
|
41195
|
+
import { existsSync as existsSync22, readdirSync as readdirSync8 } from "fs";
|
|
41196
|
+
import { join as join26, relative as relative9 } from "path";
|
|
41008
41197
|
function listProjects() {
|
|
41009
41198
|
const dir = paths().projects;
|
|
41010
41199
|
const out = [];
|
|
@@ -41037,7 +41226,7 @@ function statsPayload(ctx) {
|
|
|
41037
41226
|
const sessionId = getCurrentSession(ctx);
|
|
41038
41227
|
const g = globalDb();
|
|
41039
41228
|
const onboardingDir = join26(memoriesDir(ctx), "onboarding");
|
|
41040
|
-
const onboarding =
|
|
41229
|
+
const onboarding = existsSync22(onboardingDir) ? readdirSync8(onboardingDir).filter((f) => f.endsWith(".md")).map((f) => f.slice(0, -3)) : [];
|
|
41041
41230
|
return {
|
|
41042
41231
|
project: ctx.root,
|
|
41043
41232
|
projectId: ctx.id,
|
|
@@ -41229,7 +41418,7 @@ function docsPayload(ctx) {
|
|
|
41229
41418
|
const known = new Set(m.sources.map((s) => docsTag(s.name)));
|
|
41230
41419
|
const orphans2 = [...stats.values()].filter((s) => !known.has(s.source)).map((s) => ({ source: s.source, documents: s.documents, chunks: s.chunks }));
|
|
41231
41420
|
return {
|
|
41232
|
-
manifest:
|
|
41421
|
+
manifest: relative9(ctx.root, manifestPath(ctx)),
|
|
41233
41422
|
sources,
|
|
41234
41423
|
orphans: orphans2,
|
|
41235
41424
|
totals: {
|
|
@@ -41575,7 +41764,7 @@ var init_namepath = __esm({
|
|
|
41575
41764
|
});
|
|
41576
41765
|
|
|
41577
41766
|
// src/code/symbols.ts
|
|
41578
|
-
import { existsSync as
|
|
41767
|
+
import { existsSync as existsSync23, readFileSync as readFileSync20 } from "fs";
|
|
41579
41768
|
import { join as join27 } from "path";
|
|
41580
41769
|
function isExported2(n) {
|
|
41581
41770
|
if (!n.meta) return false;
|
|
@@ -41612,8 +41801,8 @@ var init_symbols2 = __esm({
|
|
|
41612
41801
|
});
|
|
41613
41802
|
|
|
41614
41803
|
// src/ui/graph-api.ts
|
|
41615
|
-
import { readFileSync as readFileSync21, realpathSync as realpathSync2, statSync as
|
|
41616
|
-
import { relative as
|
|
41804
|
+
import { readFileSync as readFileSync21, realpathSync as realpathSync2, statSync as statSync10 } from "fs";
|
|
41805
|
+
import { relative as relative10, resolve as resolve7 } from "path";
|
|
41617
41806
|
function metaOf2(row) {
|
|
41618
41807
|
if (!row?.meta) return {};
|
|
41619
41808
|
try {
|
|
@@ -41675,16 +41864,16 @@ function treePayload(ctx) {
|
|
|
41675
41864
|
function filePayload(ctx, rel) {
|
|
41676
41865
|
const clean = rel.replace(/^\.\//, "");
|
|
41677
41866
|
const abs = resolve7(ctx.root, clean);
|
|
41678
|
-
if (
|
|
41867
|
+
if (relative10(ctx.root, abs).startsWith("..")) throw new Error("path outside the project");
|
|
41679
41868
|
let real = abs;
|
|
41680
41869
|
try {
|
|
41681
41870
|
real = realpathSync2(abs);
|
|
41682
41871
|
} catch {
|
|
41683
41872
|
return { error: "not found", path: clean };
|
|
41684
41873
|
}
|
|
41685
|
-
if (
|
|
41874
|
+
if (relative10(realpathSync2(ctx.root), real).startsWith(".."))
|
|
41686
41875
|
throw new Error("path outside the project");
|
|
41687
|
-
const st =
|
|
41876
|
+
const st = statSync10(real);
|
|
41688
41877
|
if (!st.isFile()) return { error: "not a file", path: clean };
|
|
41689
41878
|
const db = getStorage(ctx);
|
|
41690
41879
|
const row = db.fileIndexGet(clean);
|
|
@@ -43269,6 +43458,693 @@ var init_history_api = __esm({
|
|
|
43269
43458
|
}
|
|
43270
43459
|
});
|
|
43271
43460
|
|
|
43461
|
+
// src/history/lineage.ts
|
|
43462
|
+
function mergedBranchName(subject) {
|
|
43463
|
+
const pr = PR_MERGE.exec(subject);
|
|
43464
|
+
if (pr) return { name: pr[2], pr: Number(pr[1]) };
|
|
43465
|
+
const br = BRANCH_MERGE.exec(subject);
|
|
43466
|
+
if (br) return { name: br[1].replace(/^origin\//, "") };
|
|
43467
|
+
return void 0;
|
|
43468
|
+
}
|
|
43469
|
+
function firstParents(m, sha, stop, max) {
|
|
43470
|
+
const out = [];
|
|
43471
|
+
let cur = sha ? m.bySha.get(sha) : void 0;
|
|
43472
|
+
while (cur && !stop.has(cur.sha) && out.length < max) {
|
|
43473
|
+
out.push(cur);
|
|
43474
|
+
cur = cur.parents[0] ? m.bySha.get(cur.parents[0]) : void 0;
|
|
43475
|
+
}
|
|
43476
|
+
return out;
|
|
43477
|
+
}
|
|
43478
|
+
function branchLineage(m, opts = {}) {
|
|
43479
|
+
const max = opts.maxPerBranch ?? 500;
|
|
43480
|
+
const def = m.branches.find((b) => b.isDefault && !b.remote) ?? m.branches.find((b) => b.isDefault);
|
|
43481
|
+
const defaultName = def ? def.remote ? def.name.replace(/^[^/]+\//, "") : def.name : m.base ?? "main";
|
|
43482
|
+
const mainline = firstParents(m, def?.sha ?? m.head, /* @__PURE__ */ new Set(), 1e5);
|
|
43483
|
+
const onMain = new Set(mainline.map((c) => c.sha));
|
|
43484
|
+
const byName = /* @__PURE__ */ new Map();
|
|
43485
|
+
const entry = (name) => {
|
|
43486
|
+
let b = byName.get(name);
|
|
43487
|
+
if (!b) {
|
|
43488
|
+
b = {
|
|
43489
|
+
name,
|
|
43490
|
+
commits: [],
|
|
43491
|
+
tipAt: 0,
|
|
43492
|
+
isDefault: name === defaultName,
|
|
43493
|
+
live: false,
|
|
43494
|
+
merged: false,
|
|
43495
|
+
prs: [],
|
|
43496
|
+
merges: []
|
|
43497
|
+
};
|
|
43498
|
+
byName.set(name, b);
|
|
43499
|
+
}
|
|
43500
|
+
return b;
|
|
43501
|
+
};
|
|
43502
|
+
const own2 = /* @__PURE__ */ new Map();
|
|
43503
|
+
const add = (name, cs) => {
|
|
43504
|
+
const set2 = own2.get(name) ?? /* @__PURE__ */ new Set();
|
|
43505
|
+
for (const c of cs) if (!c.merge) set2.add(c.sha);
|
|
43506
|
+
own2.set(name, set2);
|
|
43507
|
+
const b = entry(name);
|
|
43508
|
+
for (const c of cs) b.tipAt = Math.max(b.tipAt, c.at);
|
|
43509
|
+
};
|
|
43510
|
+
add(defaultName, mainline);
|
|
43511
|
+
entry(defaultName).live = !!def;
|
|
43512
|
+
for (const c of mainline) {
|
|
43513
|
+
if (!c.merge || c.parents.length < 2) continue;
|
|
43514
|
+
const found = mergedBranchName(c.subject);
|
|
43515
|
+
const name = found?.name ?? (c.pr ? `PR #${c.pr}` : `merge ${c.short}`);
|
|
43516
|
+
if (name === defaultName) continue;
|
|
43517
|
+
add(name, firstParents(m, c.parents[1], onMain, max));
|
|
43518
|
+
const b = entry(name);
|
|
43519
|
+
b.merged = true;
|
|
43520
|
+
b.merges.push(c.sha);
|
|
43521
|
+
const pr = found?.pr ?? c.pr;
|
|
43522
|
+
if (pr && !b.prs.includes(pr)) b.prs.push(pr);
|
|
43523
|
+
}
|
|
43524
|
+
const locals = new Set(m.branches.filter((b) => !b.remote).map((b) => b.name));
|
|
43525
|
+
for (const ref of [...m.branches].sort((a, b) => Number(a.remote) - Number(b.remote))) {
|
|
43526
|
+
if (ref.isDefault) continue;
|
|
43527
|
+
const name = ref.remote ? ref.name.replace(/^[^/]+\//, "") : ref.name;
|
|
43528
|
+
if (ref.remote && (locals.has(name) || name === "HEAD" || !name)) continue;
|
|
43529
|
+
if (name === defaultName) continue;
|
|
43530
|
+
add(name, firstParents(m, ref.sha, onMain, max));
|
|
43531
|
+
const b = entry(name);
|
|
43532
|
+
b.live = true;
|
|
43533
|
+
b.merged ||= ref.merged;
|
|
43534
|
+
if (!b.tipAt) b.tipAt = ref.at;
|
|
43535
|
+
}
|
|
43536
|
+
for (const [name, set2] of own2) {
|
|
43537
|
+
entry(name).commits = [...set2].map((s) => m.bySha.get(s)).sort((a, b) => b.at - a.at).map((c) => c.sha);
|
|
43538
|
+
}
|
|
43539
|
+
const branches = [...byName.values()];
|
|
43540
|
+
const ownerOf = /* @__PURE__ */ new Map();
|
|
43541
|
+
for (const b of branches)
|
|
43542
|
+
for (const s of b.commits) ownerOf.set(s, [...ownerOf.get(s) ?? [], b.name]);
|
|
43543
|
+
return { defaultName, branches, ownerOf };
|
|
43544
|
+
}
|
|
43545
|
+
var PR_MERGE, BRANCH_MERGE;
|
|
43546
|
+
var init_lineage = __esm({
|
|
43547
|
+
"src/history/lineage.ts"() {
|
|
43548
|
+
"use strict";
|
|
43549
|
+
init_esm_shims();
|
|
43550
|
+
PR_MERGE = /^Merge pull request #(\d+) from [^/\s]+\/(\S+)/;
|
|
43551
|
+
BRANCH_MERGE = /^Merge (?:remote-tracking )?branch '([^']+)'/;
|
|
43552
|
+
}
|
|
43553
|
+
});
|
|
43554
|
+
|
|
43555
|
+
// src/ui/history-graph-api.ts
|
|
43556
|
+
import { existsSync as existsSync24 } from "fs";
|
|
43557
|
+
import { join as join28 } from "path";
|
|
43558
|
+
function parseList(raw) {
|
|
43559
|
+
if (!raw) return [];
|
|
43560
|
+
try {
|
|
43561
|
+
const v = JSON.parse(raw);
|
|
43562
|
+
return Array.isArray(v) ? v.filter((x) => typeof x === "string") : [];
|
|
43563
|
+
} catch {
|
|
43564
|
+
return [];
|
|
43565
|
+
}
|
|
43566
|
+
}
|
|
43567
|
+
function metaOf3(raw) {
|
|
43568
|
+
if (!raw) return {};
|
|
43569
|
+
try {
|
|
43570
|
+
return JSON.parse(raw);
|
|
43571
|
+
} catch {
|
|
43572
|
+
return {};
|
|
43573
|
+
}
|
|
43574
|
+
}
|
|
43575
|
+
function historyGraphKnowledgePayload(ctx, opts = {}) {
|
|
43576
|
+
const db = getStorage(ctx);
|
|
43577
|
+
const m = getHistory(ctx, db);
|
|
43578
|
+
if (!m) return NO_GIT2;
|
|
43579
|
+
reconcile(ctx, db);
|
|
43580
|
+
try {
|
|
43581
|
+
ensureDocsGraph(ctx, db);
|
|
43582
|
+
} catch {
|
|
43583
|
+
}
|
|
43584
|
+
return buildHistoryGraph(ctx, db, m, opts);
|
|
43585
|
+
}
|
|
43586
|
+
function buildHistoryGraph(ctx, db, m, opts = {}) {
|
|
43587
|
+
const nBranches = Math.max(1, Math.min(50, opts.branches ?? 8));
|
|
43588
|
+
const perBranch = Math.max(1, Math.min(200, opts.commitsPerBranch ?? 25));
|
|
43589
|
+
const perCommit = Math.max(1, Math.min(200, opts.filesPerCommit ?? 12));
|
|
43590
|
+
const limit = Math.max(20, Math.min(2e3, opts.limit ?? 400));
|
|
43591
|
+
const focus = opts.focus?.trim() || null;
|
|
43592
|
+
const { defaultName, branches, ownerOf } = branchLineage(m);
|
|
43593
|
+
const branchBy = new Map(branches.map((b) => [b.name, b]));
|
|
43594
|
+
const memDir = `${memoriesRel(ctx)}/`;
|
|
43595
|
+
const memoryOfPath = (p) => p.startsWith(memDir) && p.endsWith(".md") ? p.slice(memDir.length, -3) : void 0;
|
|
43596
|
+
const memRows = new Map(
|
|
43597
|
+
db.memoryList({ limit: 1e5 }).filter((r) => r.name).map((r) => [r.name, r])
|
|
43598
|
+
);
|
|
43599
|
+
const memFiles = /* @__PURE__ */ new Map();
|
|
43600
|
+
for (const [name, row] of memRows) {
|
|
43601
|
+
const paths2 = /* @__PURE__ */ new Set();
|
|
43602
|
+
for (const id of parseList(row.node_ids)) {
|
|
43603
|
+
const f = fileOf(id);
|
|
43604
|
+
if (f) paths2.add(f);
|
|
43605
|
+
}
|
|
43606
|
+
if (paths2.size) memFiles.set(name, [...paths2]);
|
|
43607
|
+
}
|
|
43608
|
+
const nonMerge = (cs) => cs.filter((c) => !c.merge);
|
|
43609
|
+
const touching = (paths2, cap) => {
|
|
43610
|
+
const out = /* @__PURE__ */ new Map();
|
|
43611
|
+
for (const p of paths2) for (const c of nonMerge(commitsForFile(m, p))) out.set(c.sha, c);
|
|
43612
|
+
return [...out.values()].sort((a, b) => b.at - a.at).slice(0, cap);
|
|
43613
|
+
};
|
|
43614
|
+
const commitSet = /* @__PURE__ */ new Map();
|
|
43615
|
+
const branchSet = /* @__PURE__ */ new Set();
|
|
43616
|
+
let onlyFiles = null;
|
|
43617
|
+
const addCommits = (cs) => {
|
|
43618
|
+
for (const c of cs) commitSet.set(c.sha, c);
|
|
43619
|
+
};
|
|
43620
|
+
let focusId = null;
|
|
43621
|
+
let followOwners = true;
|
|
43622
|
+
const kind = focus ? parseId(focus).kind : null;
|
|
43623
|
+
if (!focus) {
|
|
43624
|
+
followOwners = false;
|
|
43625
|
+
const feature = branches.filter((b) => !b.isDefault && b.commits.length).sort((a, b) => b.tipAt - a.tipAt).slice(0, nBranches);
|
|
43626
|
+
for (const b of feature) {
|
|
43627
|
+
branchSet.add(b.name);
|
|
43628
|
+
addCommits(b.commits.slice(0, perBranch).map((s) => m.bySha.get(s)));
|
|
43629
|
+
}
|
|
43630
|
+
const def = branchBy.get(defaultName);
|
|
43631
|
+
if (def) {
|
|
43632
|
+
branchSet.add(def.name);
|
|
43633
|
+
const oldest = Math.min(...feature.map((b) => b.tipAt), Date.now());
|
|
43634
|
+
addCommits(
|
|
43635
|
+
def.commits.map((s) => m.bySha.get(s)).filter((c, i) => i < perBranch && (feature.length < nBranches || c.at >= oldest))
|
|
43636
|
+
);
|
|
43637
|
+
}
|
|
43638
|
+
} else if (kind === "branch") {
|
|
43639
|
+
const name = focus.slice("branch:".length);
|
|
43640
|
+
const b = branchBy.get(name) ?? branches.find((x) => x.name.endsWith(`/${name}`));
|
|
43641
|
+
if (b) {
|
|
43642
|
+
focusId = branchId(b.name);
|
|
43643
|
+
branchSet.add(b.name);
|
|
43644
|
+
if (b.merged) branchSet.add(defaultName);
|
|
43645
|
+
addCommits(b.commits.slice(0, 150).map((s) => m.bySha.get(s)));
|
|
43646
|
+
}
|
|
43647
|
+
followOwners = false;
|
|
43648
|
+
} else if (kind === "commit") {
|
|
43649
|
+
const c = resolveCommit(m, focus.slice("commit:".length));
|
|
43650
|
+
if (c) {
|
|
43651
|
+
focusId = commitId(c.sha);
|
|
43652
|
+
addCommits([c]);
|
|
43653
|
+
addCommits([...c.origins, ...c.blamedBy].flatMap((s) => m.bySha.get(s) ?? []));
|
|
43654
|
+
}
|
|
43655
|
+
} else if (kind === "memory") {
|
|
43656
|
+
const name = focus.slice("memory:".length);
|
|
43657
|
+
focusId = memoryId(name);
|
|
43658
|
+
const paths2 = memFiles.get(name) ?? [];
|
|
43659
|
+
onlyFiles = new Set(paths2);
|
|
43660
|
+
const own2 = `${memDir}${name}.md`;
|
|
43661
|
+
addCommits([...touching(paths2, 60), ...touching([own2], 20)]);
|
|
43662
|
+
} else if (kind === "doc") {
|
|
43663
|
+
focusId = focus;
|
|
43664
|
+
const paths2 = db.graphEdgesFrom([focus], ["documents"]).map((e) => fileOf(e.dst)).filter((p) => !!p);
|
|
43665
|
+
onlyFiles = new Set(paths2);
|
|
43666
|
+
addCommits(touching(paths2, 60));
|
|
43667
|
+
} else {
|
|
43668
|
+
const path3 = fileOf(focus) ?? focus.replace(/^file:/, "");
|
|
43669
|
+
focusId = fileId(path3);
|
|
43670
|
+
onlyFiles = /* @__PURE__ */ new Set([path3]);
|
|
43671
|
+
addCommits(touching([path3], 60));
|
|
43672
|
+
}
|
|
43673
|
+
if (followOwners)
|
|
43674
|
+
for (const sha of commitSet.keys())
|
|
43675
|
+
for (const name of ownerOf.get(sha) ?? []) branchSet.add(name);
|
|
43676
|
+
const edges = /* @__PURE__ */ new Map();
|
|
43677
|
+
const addEdge = (src, dst, kind2, weight = 1) => {
|
|
43678
|
+
if (src === dst) return;
|
|
43679
|
+
const key = `${src}|${dst}|${kind2}`;
|
|
43680
|
+
if (!edges.has(key)) edges.set(key, { src, dst, kind: kind2, weight });
|
|
43681
|
+
};
|
|
43682
|
+
for (const name of branchSet) {
|
|
43683
|
+
const b = branchBy.get(name);
|
|
43684
|
+
if (!b) continue;
|
|
43685
|
+
for (const sha of b.commits)
|
|
43686
|
+
if (commitSet.has(sha)) addEdge(branchId(name), commitId(sha), "contains");
|
|
43687
|
+
if (b.merged && !b.isDefault && branchSet.has(defaultName))
|
|
43688
|
+
addEdge(branchId(name), branchId(defaultName), "merged_into");
|
|
43689
|
+
}
|
|
43690
|
+
const touchCount = /* @__PURE__ */ new Map();
|
|
43691
|
+
const commitFiles = /* @__PURE__ */ new Map();
|
|
43692
|
+
const recorded = /* @__PURE__ */ new Map();
|
|
43693
|
+
for (const c of commitSet.values()) {
|
|
43694
|
+
const mems = [];
|
|
43695
|
+
const paths2 = [];
|
|
43696
|
+
for (const f of [...c.files].sort((a, b) => b.added + b.deleted - (a.added + a.deleted))) {
|
|
43697
|
+
const mem = memoryOfPath(f.path);
|
|
43698
|
+
if (mem) {
|
|
43699
|
+
mems.push(mem);
|
|
43700
|
+
continue;
|
|
43701
|
+
}
|
|
43702
|
+
if (LOCKFILE.test(f.path)) continue;
|
|
43703
|
+
if (onlyFiles && !onlyFiles.has(f.path)) continue;
|
|
43704
|
+
paths2.push(f.path);
|
|
43705
|
+
}
|
|
43706
|
+
const kept2 = paths2.slice(0, perCommit);
|
|
43707
|
+
commitFiles.set(c.sha, kept2);
|
|
43708
|
+
for (const p of kept2) touchCount.set(p, (touchCount.get(p) ?? 0) + 1);
|
|
43709
|
+
if (mems.length) recorded.set(c.sha, mems);
|
|
43710
|
+
}
|
|
43711
|
+
for (const p of onlyFiles ?? []) if (!touchCount.has(p)) touchCount.set(p, 0);
|
|
43712
|
+
const fileBudget = Math.max(10, limit - branchSet.size - commitSet.size - 40);
|
|
43713
|
+
const files = new Set(
|
|
43714
|
+
[...touchCount.entries()].sort(
|
|
43715
|
+
(a, b) => Number(onlyFiles?.has(b[0]) ?? false) - Number(onlyFiles?.has(a[0]) ?? false) || b[1] - a[1] || (m.files.get(b[0])?.risk ?? 0) - (m.files.get(a[0])?.risk ?? 0) || a[0].localeCompare(b[0])
|
|
43716
|
+
).slice(0, fileBudget).map(([p]) => p)
|
|
43717
|
+
);
|
|
43718
|
+
for (const [sha, paths2] of commitFiles)
|
|
43719
|
+
for (const p of paths2) if (files.has(p)) addEdge(commitId(sha), fileId(p), "touches");
|
|
43720
|
+
const memories = /* @__PURE__ */ new Set();
|
|
43721
|
+
for (const [sha, mems] of recorded)
|
|
43722
|
+
for (const name of mems) {
|
|
43723
|
+
memories.add(name);
|
|
43724
|
+
addEdge(commitId(sha), memoryId(name), "records");
|
|
43725
|
+
}
|
|
43726
|
+
for (const [name, paths2] of memFiles)
|
|
43727
|
+
for (const p of paths2)
|
|
43728
|
+
if (files.has(p)) {
|
|
43729
|
+
memories.add(name);
|
|
43730
|
+
addEdge(memoryId(name), fileId(p), "mentions");
|
|
43731
|
+
}
|
|
43732
|
+
if (kind === "memory" && focus) memories.add(focus.slice("memory:".length));
|
|
43733
|
+
const docs = /* @__PURE__ */ new Set();
|
|
43734
|
+
if (files.size)
|
|
43735
|
+
for (const e of db.graphEdgesTo(
|
|
43736
|
+
[...files].map((p) => fileId(p)),
|
|
43737
|
+
["documents"]
|
|
43738
|
+
)) {
|
|
43739
|
+
docs.add(e.src);
|
|
43740
|
+
addEdge(e.src, e.dst, "documents");
|
|
43741
|
+
}
|
|
43742
|
+
if (kind === "doc" && focus) docs.add(focus);
|
|
43743
|
+
for (const l of m.links)
|
|
43744
|
+
if (commitSet.has(l.fix) && commitSet.has(l.origin))
|
|
43745
|
+
addEdge(commitId(l.fix), commitId(l.origin), "fixes", Math.min(3, 1 + l.lines / 20));
|
|
43746
|
+
const nodes = [];
|
|
43747
|
+
for (const name of branchSet) {
|
|
43748
|
+
const b = branchBy.get(name);
|
|
43749
|
+
if (!b) continue;
|
|
43750
|
+
const id = branchId(name);
|
|
43751
|
+
nodes.push({
|
|
43752
|
+
id,
|
|
43753
|
+
label: name,
|
|
43754
|
+
kind: "branch",
|
|
43755
|
+
group: "branch",
|
|
43756
|
+
path: null,
|
|
43757
|
+
line: null,
|
|
43758
|
+
root: id === focusId,
|
|
43759
|
+
at: b.tipAt,
|
|
43760
|
+
commits: b.commits.length,
|
|
43761
|
+
merged: b.merged,
|
|
43762
|
+
live: b.live,
|
|
43763
|
+
isDefault: b.isDefault,
|
|
43764
|
+
prs: b.prs.length ? b.prs : void 0
|
|
43765
|
+
});
|
|
43766
|
+
}
|
|
43767
|
+
for (const c of commitSet.values()) {
|
|
43768
|
+
const id = commitId(c.sha);
|
|
43769
|
+
nodes.push({
|
|
43770
|
+
id,
|
|
43771
|
+
label: c.subject,
|
|
43772
|
+
kind: "commit",
|
|
43773
|
+
group: "commit",
|
|
43774
|
+
path: null,
|
|
43775
|
+
line: null,
|
|
43776
|
+
root: id === focusId,
|
|
43777
|
+
sub: c.kind,
|
|
43778
|
+
bucket: c.bucket,
|
|
43779
|
+
at: c.at,
|
|
43780
|
+
author: c.author,
|
|
43781
|
+
short: c.short,
|
|
43782
|
+
risk: c.risk
|
|
43783
|
+
});
|
|
43784
|
+
}
|
|
43785
|
+
for (const p of files) {
|
|
43786
|
+
const id = fileId(p);
|
|
43787
|
+
const h = m.files.get(p);
|
|
43788
|
+
nodes.push({
|
|
43789
|
+
id,
|
|
43790
|
+
label: p,
|
|
43791
|
+
kind: "file",
|
|
43792
|
+
group: "file",
|
|
43793
|
+
path: p,
|
|
43794
|
+
line: null,
|
|
43795
|
+
root: id === focusId,
|
|
43796
|
+
missing: existsSync24(join28(ctx.root, p)) ? void 0 : true,
|
|
43797
|
+
risk: h?.risk,
|
|
43798
|
+
fixes: h?.fixes
|
|
43799
|
+
});
|
|
43800
|
+
}
|
|
43801
|
+
for (const name of memories) {
|
|
43802
|
+
const id = memoryId(name);
|
|
43803
|
+
const row = memRows.get(name);
|
|
43804
|
+
nodes.push({
|
|
43805
|
+
id,
|
|
43806
|
+
label: name,
|
|
43807
|
+
kind: "memory",
|
|
43808
|
+
group: "memory",
|
|
43809
|
+
path: row?.path ?? null,
|
|
43810
|
+
line: null,
|
|
43811
|
+
root: id === focusId,
|
|
43812
|
+
missing: row ? void 0 : true,
|
|
43813
|
+
sub: row?.kind,
|
|
43814
|
+
status: row?.status ?? (row?.kind === "decision" ? "accepted" : void 0),
|
|
43815
|
+
detail: name.includes("/") ? `${name.split("/")[0]}/` : void 0
|
|
43816
|
+
});
|
|
43817
|
+
}
|
|
43818
|
+
const docRows = new Map(db.graphGetNodes([...docs]).map((n) => [n.id, n]));
|
|
43819
|
+
for (const id of docs) {
|
|
43820
|
+
const row = docRows.get(id);
|
|
43821
|
+
const meta4 = metaOf3(row?.meta);
|
|
43822
|
+
const cat = typeof meta4.category === "string" ? meta4.category : void 0;
|
|
43823
|
+
nodes.push({
|
|
43824
|
+
id,
|
|
43825
|
+
label: row?.name ?? id.slice("doc:".length),
|
|
43826
|
+
kind: "doc",
|
|
43827
|
+
group: "doc",
|
|
43828
|
+
path: row?.path ?? null,
|
|
43829
|
+
line: null,
|
|
43830
|
+
root: id === focusId,
|
|
43831
|
+
missing: row ? void 0 : true,
|
|
43832
|
+
sub: cat,
|
|
43833
|
+
detail: cat ? categoryInfo(cat).label : void 0
|
|
43834
|
+
});
|
|
43835
|
+
}
|
|
43836
|
+
if (focusId && !nodes.some((n) => n.id === focusId)) {
|
|
43837
|
+
const p = parseId(focusId);
|
|
43838
|
+
nodes.push({
|
|
43839
|
+
id: focusId,
|
|
43840
|
+
label: p.rest,
|
|
43841
|
+
kind: p.kind,
|
|
43842
|
+
group: ["branch", "commit", "memory", "doc"].includes(p.kind) ? p.kind : "file",
|
|
43843
|
+
path: p.kind === "file" ? p.rest : null,
|
|
43844
|
+
line: null,
|
|
43845
|
+
root: true,
|
|
43846
|
+
missing: true
|
|
43847
|
+
});
|
|
43848
|
+
}
|
|
43849
|
+
for (const n of nodes) if (n.root) n.seed = true;
|
|
43850
|
+
const deg = /* @__PURE__ */ new Map();
|
|
43851
|
+
for (const e of edges.values()) {
|
|
43852
|
+
deg.set(e.src, (deg.get(e.src) ?? 0) + 1);
|
|
43853
|
+
deg.set(e.dst, (deg.get(e.dst) ?? 0) + 1);
|
|
43854
|
+
}
|
|
43855
|
+
const RANK = {
|
|
43856
|
+
branch: 1,
|
|
43857
|
+
commit: 2,
|
|
43858
|
+
memory: 3,
|
|
43859
|
+
doc: 3,
|
|
43860
|
+
file: 4
|
|
43861
|
+
};
|
|
43862
|
+
nodes.sort(
|
|
43863
|
+
(a, b) => (a.root ? 0 : RANK[a.group]) - (b.root ? 0 : RANK[b.group]) || (b.at ?? 0) - (a.at ?? 0) || (deg.get(b.id) ?? 0) - (deg.get(a.id) ?? 0) || a.id.localeCompare(b.id)
|
|
43864
|
+
);
|
|
43865
|
+
const total = nodes.length;
|
|
43866
|
+
const kept = nodes.slice(0, limit);
|
|
43867
|
+
const keep = new Set(kept.map((n) => n.id));
|
|
43868
|
+
const keptEdges = [...edges.values()].filter((e) => keep.has(e.src) && keep.has(e.dst));
|
|
43869
|
+
return {
|
|
43870
|
+
git: true,
|
|
43871
|
+
focus: focusId ?? focus,
|
|
43872
|
+
defaultBranch: defaultName,
|
|
43873
|
+
nodes: kept,
|
|
43874
|
+
edges: keptEdges,
|
|
43875
|
+
truncated: total > kept.length,
|
|
43876
|
+
total,
|
|
43877
|
+
/** Every branch with own commits, newest first, for the focus picker. */
|
|
43878
|
+
branches: branches.filter((b) => b.commits.length).sort((a, b) => Number(b.isDefault) - Number(a.isDefault) || b.tipAt - a.tipAt).map((b) => ({
|
|
43879
|
+
name: b.name,
|
|
43880
|
+
commits: b.commits.length,
|
|
43881
|
+
tipAt: b.tipAt,
|
|
43882
|
+
merged: b.merged,
|
|
43883
|
+
live: b.live,
|
|
43884
|
+
isDefault: b.isDefault
|
|
43885
|
+
}))
|
|
43886
|
+
};
|
|
43887
|
+
}
|
|
43888
|
+
var LOCKFILE, branchId, commitId, NO_GIT2;
|
|
43889
|
+
var init_history_graph_api = __esm({
|
|
43890
|
+
"src/ui/history-graph-api.ts"() {
|
|
43891
|
+
"use strict";
|
|
43892
|
+
init_esm_shims();
|
|
43893
|
+
init_category();
|
|
43894
|
+
init_graph();
|
|
43895
|
+
init_ids();
|
|
43896
|
+
init_lineage();
|
|
43897
|
+
init_model();
|
|
43898
|
+
init_store();
|
|
43899
|
+
init_storage();
|
|
43900
|
+
LOCKFILE = /(^|\/)(package-lock\.json|npm-shrinkwrap\.json|pnpm-lock\.yaml|yarn\.lock|bun\.lockb?|Cargo\.lock|go\.sum|poetry\.lock|uv\.lock|composer\.lock|Gemfile\.lock)$/;
|
|
43901
|
+
branchId = (name) => `branch:${name}`;
|
|
43902
|
+
commitId = (sha) => `commit:${sha}`;
|
|
43903
|
+
NO_GIT2 = { git: false, error: "not a git repository (or no commits yet)" };
|
|
43904
|
+
}
|
|
43905
|
+
});
|
|
43906
|
+
|
|
43907
|
+
// src/ui/knowledge-api.ts
|
|
43908
|
+
function metaOf4(row) {
|
|
43909
|
+
if (!row?.meta) return {};
|
|
43910
|
+
try {
|
|
43911
|
+
return JSON.parse(row.meta);
|
|
43912
|
+
} catch {
|
|
43913
|
+
return {};
|
|
43914
|
+
}
|
|
43915
|
+
}
|
|
43916
|
+
function kindOfMissing(id) {
|
|
43917
|
+
const k = parseId(id).kind;
|
|
43918
|
+
return k === "pkg" ? "package" : k === "sym" ? "symbol" : k === "doc" ? "doc" : k;
|
|
43919
|
+
}
|
|
43920
|
+
function scopeOf2(raw) {
|
|
43921
|
+
return raw === "memory" || raw === "docs" ? raw : "all";
|
|
43922
|
+
}
|
|
43923
|
+
function knowledgePayload(ctx, opts = {}) {
|
|
43924
|
+
const db = getStorage(ctx);
|
|
43925
|
+
reconcile(ctx, db);
|
|
43926
|
+
try {
|
|
43927
|
+
ensureDocsGraph(ctx, db);
|
|
43928
|
+
} catch {
|
|
43929
|
+
}
|
|
43930
|
+
return buildKnowledge(ctx, db, opts);
|
|
43931
|
+
}
|
|
43932
|
+
function buildKnowledge(ctx, db, opts = {}) {
|
|
43933
|
+
const scope = opts.scope ?? "all";
|
|
43934
|
+
const limit = Math.max(10, Math.min(2e3, opts.limit ?? 300));
|
|
43935
|
+
const focus = opts.focus?.trim() || null;
|
|
43936
|
+
const memRows = new Map(
|
|
43937
|
+
db.memoryList({ limit: 1e5 }).filter((r) => r.name).map((r) => [r.name, r])
|
|
43938
|
+
);
|
|
43939
|
+
const memNodes = db.graphNodesByKind(["memory"]);
|
|
43940
|
+
const docNodes = db.graphNodesByKind(["doc"]);
|
|
43941
|
+
const rows = /* @__PURE__ */ new Map();
|
|
43942
|
+
for (const n of [...memNodes, ...docNodes]) rows.set(n.id, n);
|
|
43943
|
+
const hubsOf = (id) => {
|
|
43944
|
+
const n = rows.get(id);
|
|
43945
|
+
if (!n) return [];
|
|
43946
|
+
const m = metaOf4(n);
|
|
43947
|
+
const out = [];
|
|
43948
|
+
if (n.kind === "memory") {
|
|
43949
|
+
const row = memRows.get(n.name);
|
|
43950
|
+
const tags = row?.tags ? JSON.parse(row.tags) : m.tags ?? [];
|
|
43951
|
+
for (const t of tags) out.push({ id: tagId(t), kind: "tag", label: `#${t}` });
|
|
43952
|
+
if (n.name.includes("/")) {
|
|
43953
|
+
const topic = n.name.split("/")[0];
|
|
43954
|
+
out.push({ id: topicId(topic), kind: "topic", label: `${topic}/` });
|
|
43955
|
+
}
|
|
43956
|
+
} else if (n.kind === "doc") {
|
|
43957
|
+
for (const t of m.tags ?? [])
|
|
43958
|
+
out.push({ id: tagId(t), kind: "tag", label: `#${t}` });
|
|
43959
|
+
const cat = typeof m.category === "string" ? m.category : "local";
|
|
43960
|
+
out.push({ id: topicId(`docs/${cat}`), kind: "topic", label: categoryInfo(cat).label });
|
|
43961
|
+
}
|
|
43962
|
+
return out;
|
|
43963
|
+
};
|
|
43964
|
+
const scoped = [
|
|
43965
|
+
...scope !== "docs" ? memNodes : [],
|
|
43966
|
+
...scope !== "memory" ? docNodes : []
|
|
43967
|
+
].map((n) => n.id);
|
|
43968
|
+
let seeds;
|
|
43969
|
+
if (!focus) seeds = scoped;
|
|
43970
|
+
else if (focus.startsWith("tag:") || focus.startsWith("topic:"))
|
|
43971
|
+
seeds = [...memNodes, ...docNodes].map((n) => n.id).filter((id) => hubsOf(id).some((h) => h.id === focus));
|
|
43972
|
+
else seeds = [focus];
|
|
43973
|
+
const edges = /* @__PURE__ */ new Map();
|
|
43974
|
+
const addEdge = (e) => {
|
|
43975
|
+
if (e.src === e.dst) return;
|
|
43976
|
+
const key = `${e.src}|${e.dst}|${e.kind}`;
|
|
43977
|
+
if (!edges.has(key)) edges.set(key, e);
|
|
43978
|
+
};
|
|
43979
|
+
const stored = (e) => addEdge({ src: e.src, dst: e.dst, kind: e.kind, weight: e.weight });
|
|
43980
|
+
const hop = (ids2) => {
|
|
43981
|
+
if (!ids2.length) return [];
|
|
43982
|
+
const found = /* @__PURE__ */ new Set();
|
|
43983
|
+
for (const e of db.graphEdgesFrom(ids2, ALL_KINDS)) {
|
|
43984
|
+
stored(e);
|
|
43985
|
+
found.add(e.dst);
|
|
43986
|
+
}
|
|
43987
|
+
for (const e of db.graphEdgesTo(ids2, ALL_KINDS)) {
|
|
43988
|
+
stored(e);
|
|
43989
|
+
found.add(e.src);
|
|
43990
|
+
}
|
|
43991
|
+
return [...found];
|
|
43992
|
+
};
|
|
43993
|
+
const first = hop(seeds);
|
|
43994
|
+
if (focus && !focus.startsWith("tag:") && !focus.startsWith("topic:")) {
|
|
43995
|
+
const fresh = first.filter((id) => !seeds.includes(id));
|
|
43996
|
+
hop(fresh);
|
|
43997
|
+
}
|
|
43998
|
+
const pages = localDocPages(ctx, db);
|
|
43999
|
+
const knowledgeIds = () => new Set([...edges.values()].flatMap((e) => [e.src, e.dst]).concat(seeds));
|
|
44000
|
+
if (pages.size) {
|
|
44001
|
+
for (const id of knowledgeIds()) {
|
|
44002
|
+
if (!id.startsWith("memory:")) continue;
|
|
44003
|
+
const row = memRows.get(id.slice("memory:".length));
|
|
44004
|
+
const related = row?.node_ids ? JSON.parse(row.node_ids) : [];
|
|
44005
|
+
for (const r of related) {
|
|
44006
|
+
const f = fileOf(r);
|
|
44007
|
+
for (const name of f && pages.get(f) || [])
|
|
44008
|
+
addEdge({ src: id, dst: docId(name), kind: "about", weight: 1, derived: true });
|
|
44009
|
+
}
|
|
44010
|
+
}
|
|
44011
|
+
const docsInView = [...knowledgeIds()].filter((id) => id.startsWith("doc:"));
|
|
44012
|
+
if (docsInView.length) {
|
|
44013
|
+
for (const [name, row] of memRows) {
|
|
44014
|
+
const related = row.node_ids ? JSON.parse(row.node_ids) : [];
|
|
44015
|
+
for (const r of related) {
|
|
44016
|
+
const f = fileOf(r);
|
|
44017
|
+
for (const doc of f && pages.get(f) || [])
|
|
44018
|
+
if (docsInView.includes(docId(doc)))
|
|
44019
|
+
addEdge({
|
|
44020
|
+
src: memoryId(name),
|
|
44021
|
+
dst: docId(doc),
|
|
44022
|
+
kind: "about",
|
|
44023
|
+
weight: 1,
|
|
44024
|
+
derived: true
|
|
44025
|
+
});
|
|
44026
|
+
}
|
|
44027
|
+
}
|
|
44028
|
+
}
|
|
44029
|
+
}
|
|
44030
|
+
const hubNodes = /* @__PURE__ */ new Map();
|
|
44031
|
+
for (const id of knowledgeIds()) {
|
|
44032
|
+
for (const h of hubsOf(id)) {
|
|
44033
|
+
if (!hubNodes.has(h.id))
|
|
44034
|
+
hubNodes.set(h.id, {
|
|
44035
|
+
id: h.id,
|
|
44036
|
+
label: h.label,
|
|
44037
|
+
kind: h.kind,
|
|
44038
|
+
path: null,
|
|
44039
|
+
line: null,
|
|
44040
|
+
group: "hub"
|
|
44041
|
+
});
|
|
44042
|
+
addEdge({
|
|
44043
|
+
src: id,
|
|
44044
|
+
dst: h.id,
|
|
44045
|
+
kind: h.kind === "tag" ? "tagged" : "in_topic",
|
|
44046
|
+
weight: 0.5,
|
|
44047
|
+
derived: true
|
|
44048
|
+
});
|
|
44049
|
+
}
|
|
44050
|
+
}
|
|
44051
|
+
if (focus && (focus.startsWith("tag:") || focus.startsWith("topic:")) && !hubNodes.has(focus))
|
|
44052
|
+
hubNodes.set(focus, {
|
|
44053
|
+
id: focus,
|
|
44054
|
+
label: focus.startsWith("tag:") ? `#${focus.slice(4)}` : `${focus.slice(6)}/`,
|
|
44055
|
+
kind: focus.startsWith("tag:") ? "tag" : "topic",
|
|
44056
|
+
path: null,
|
|
44057
|
+
line: null,
|
|
44058
|
+
group: "hub"
|
|
44059
|
+
});
|
|
44060
|
+
const ids = knowledgeIds();
|
|
44061
|
+
for (const h of hubNodes.keys()) ids.add(h);
|
|
44062
|
+
const graphRows = new Map(
|
|
44063
|
+
db.graphGetNodes([...ids].filter((id) => !hubNodes.has(id))).map((n) => [n.id, n])
|
|
44064
|
+
);
|
|
44065
|
+
const seedSet = new Set(seeds);
|
|
44066
|
+
if (focus) seedSet.add(focus);
|
|
44067
|
+
const nodes = [];
|
|
44068
|
+
for (const id of ids) {
|
|
44069
|
+
const hub = hubNodes.get(id);
|
|
44070
|
+
if (hub) {
|
|
44071
|
+
nodes.push({ ...hub, seed: seedSet.has(id) || void 0, root: id === focus });
|
|
44072
|
+
continue;
|
|
44073
|
+
}
|
|
44074
|
+
const row = graphRows.get(id);
|
|
44075
|
+
const base = vis(row, id, id === focus);
|
|
44076
|
+
const m = metaOf4(row);
|
|
44077
|
+
const node2 = {
|
|
44078
|
+
...base,
|
|
44079
|
+
kind: row ? row.kind : kindOfMissing(id),
|
|
44080
|
+
group: "code",
|
|
44081
|
+
seed: seedSet.has(id) || void 0,
|
|
44082
|
+
missing: row ? void 0 : true
|
|
44083
|
+
};
|
|
44084
|
+
if (node2.kind === "memory") {
|
|
44085
|
+
const name = row?.name ?? id.slice("memory:".length);
|
|
44086
|
+
const mr = memRows.get(name);
|
|
44087
|
+
node2.group = "memory";
|
|
44088
|
+
node2.label = name;
|
|
44089
|
+
node2.sub = mr?.kind ?? m.kind;
|
|
44090
|
+
node2.status = mr?.status ?? m.status ?? (node2.sub === "decision" ? "accepted" : void 0);
|
|
44091
|
+
node2.tags = mr?.tags ? JSON.parse(mr.tags) : m.tags;
|
|
44092
|
+
node2.detail = name.includes("/") ? `${name.split("/")[0]}/` : void 0;
|
|
44093
|
+
} else if (node2.kind === "doc") {
|
|
44094
|
+
node2.group = "doc";
|
|
44095
|
+
node2.label = row?.name ?? id.slice("doc:".length);
|
|
44096
|
+
node2.sub = typeof m.category === "string" ? m.category : void 0;
|
|
44097
|
+
node2.detail = node2.sub ? categoryInfo(node2.sub).label : void 0;
|
|
44098
|
+
node2.provider = typeof m.provider === "string" ? m.provider : void 0;
|
|
44099
|
+
node2.tags = m.tags;
|
|
44100
|
+
}
|
|
44101
|
+
nodes.push(node2);
|
|
44102
|
+
}
|
|
44103
|
+
const deg = /* @__PURE__ */ new Map();
|
|
44104
|
+
for (const e of edges.values()) {
|
|
44105
|
+
deg.set(e.src, (deg.get(e.src) ?? 0) + 1);
|
|
44106
|
+
deg.set(e.dst, (deg.get(e.dst) ?? 0) + 1);
|
|
44107
|
+
}
|
|
44108
|
+
const updated = (n) => n.kind === "memory" ? memRows.get(n.label)?.updated_at ?? 0 : 0;
|
|
44109
|
+
const rank = (n) => n.root ? 0 : n.seed ? 1 : n.group === "memory" || n.group === "doc" ? 2 : n.group === "hub" ? 3 : 4;
|
|
44110
|
+
nodes.sort(
|
|
44111
|
+
(a, b) => rank(a) - rank(b) || updated(b) - updated(a) || (deg.get(b.id) ?? 0) - (deg.get(a.id) ?? 0) || a.id.localeCompare(b.id)
|
|
44112
|
+
);
|
|
44113
|
+
const total = nodes.length;
|
|
44114
|
+
const kept = nodes.slice(0, limit);
|
|
44115
|
+
const keep = new Set(kept.map((n) => n.id));
|
|
44116
|
+
const keptEdges = [...edges.values()].filter((e) => keep.has(e.src) && keep.has(e.dst));
|
|
44117
|
+
const counts = { nodes: {}, edges: {} };
|
|
44118
|
+
for (const n of kept) counts.nodes[n.kind] = (counts.nodes[n.kind] ?? 0) + 1;
|
|
44119
|
+
for (const e of keptEdges) counts.edges[e.kind] = (counts.edges[e.kind] ?? 0) + 1;
|
|
44120
|
+
return {
|
|
44121
|
+
scope,
|
|
44122
|
+
focus,
|
|
44123
|
+
nodes: kept,
|
|
44124
|
+
edges: keptEdges,
|
|
44125
|
+
truncated: total > kept.length,
|
|
44126
|
+
total,
|
|
44127
|
+
counts
|
|
44128
|
+
};
|
|
44129
|
+
}
|
|
44130
|
+
var KNOWLEDGE_EDGES, ALL_KINDS, tagId, topicId;
|
|
44131
|
+
var init_knowledge_api = __esm({
|
|
44132
|
+
"src/ui/knowledge-api.ts"() {
|
|
44133
|
+
"use strict";
|
|
44134
|
+
init_esm_shims();
|
|
44135
|
+
init_category();
|
|
44136
|
+
init_graph();
|
|
44137
|
+
init_ids();
|
|
44138
|
+
init_store();
|
|
44139
|
+
init_storage();
|
|
44140
|
+
init_graph_api();
|
|
44141
|
+
KNOWLEDGE_EDGES = ["mentions", "links", "supersedes", "documents"];
|
|
44142
|
+
ALL_KINDS = [...KNOWLEDGE_EDGES];
|
|
44143
|
+
tagId = (t) => `tag:${t}`;
|
|
44144
|
+
topicId = (t) => `topic:${t}`;
|
|
44145
|
+
}
|
|
44146
|
+
});
|
|
44147
|
+
|
|
43272
44148
|
// src/ui/server.ts
|
|
43273
44149
|
var server_exports = {};
|
|
43274
44150
|
__export(server_exports, {
|
|
@@ -43276,9 +44152,9 @@ __export(server_exports, {
|
|
|
43276
44152
|
uiDir: () => uiDir
|
|
43277
44153
|
});
|
|
43278
44154
|
import { spawn as spawn2 } from "child_process";
|
|
43279
|
-
import { existsSync as
|
|
44155
|
+
import { existsSync as existsSync25, readFileSync as readFileSync22, statSync as statSync11 } from "fs";
|
|
43280
44156
|
import { createServer } from "http";
|
|
43281
|
-
import { extname as extname3, join as
|
|
44157
|
+
import { extname as extname3, join as join29, normalize as normalize6 } from "path";
|
|
43282
44158
|
import { URL as URL2 } from "url";
|
|
43283
44159
|
function json2(res, status, body) {
|
|
43284
44160
|
res.writeHead(status, {
|
|
@@ -43300,7 +44176,7 @@ function strList(v) {
|
|
|
43300
44176
|
return void 0;
|
|
43301
44177
|
}
|
|
43302
44178
|
function uiDir() {
|
|
43303
|
-
return process.env.EMINIAR_UI_DIR ||
|
|
44179
|
+
return process.env.EMINIAR_UI_DIR || join29(pluginRoot(), "dist", "ui");
|
|
43304
44180
|
}
|
|
43305
44181
|
function originAllowed(req, port) {
|
|
43306
44182
|
const host = req.headers.host ?? "";
|
|
@@ -43324,14 +44200,14 @@ function originAllowed(req, port) {
|
|
|
43324
44200
|
function serveStatic(res, p) {
|
|
43325
44201
|
const dir = uiDir();
|
|
43326
44202
|
const rel = p === "/" ? "index.html" : p.replace(/^\/+/, "");
|
|
43327
|
-
const file3 = normalize6(
|
|
44203
|
+
const file3 = normalize6(join29(dir, rel));
|
|
43328
44204
|
if (!file3.startsWith(normalize6(dir))) return false;
|
|
43329
|
-
if (!
|
|
44205
|
+
if (!existsSync25(file3) || !statSync11(file3).isFile()) return false;
|
|
43330
44206
|
const type = MIME[extname3(file3).toLowerCase()] ?? "application/octet-stream";
|
|
43331
44207
|
if (rel === "index.html") {
|
|
43332
44208
|
const stamp = (name) => {
|
|
43333
44209
|
try {
|
|
43334
|
-
return `/${name}?v=${Math.floor(
|
|
44210
|
+
return `/${name}?v=${Math.floor(statSync11(join29(dir, name)).mtimeMs)}`;
|
|
43335
44211
|
} catch {
|
|
43336
44212
|
return `/${name}`;
|
|
43337
44213
|
}
|
|
@@ -43495,6 +44371,16 @@ async function serve(opts) {
|
|
|
43495
44371
|
if (!provider) return json2(res, 400, { error: "provider required" });
|
|
43496
44372
|
return json2(res, 200, await docsResolvePayload(ctx(), provider));
|
|
43497
44373
|
}
|
|
44374
|
+
if (p === "/api/knowledge")
|
|
44375
|
+
return json2(
|
|
44376
|
+
res,
|
|
44377
|
+
200,
|
|
44378
|
+
knowledgePayload(ctx(), {
|
|
44379
|
+
scope: scopeOf2(q.get("scope")),
|
|
44380
|
+
focus: str2("focus"),
|
|
44381
|
+
limit: num2("limit", 300)
|
|
44382
|
+
})
|
|
44383
|
+
);
|
|
43498
44384
|
if (p === "/api/graph") {
|
|
43499
44385
|
const mode = q.get("mode") ?? "impact";
|
|
43500
44386
|
const node2 = q.get("node") ?? "";
|
|
@@ -43651,6 +44537,18 @@ async function serve(opts) {
|
|
|
43651
44537
|
}
|
|
43652
44538
|
if (p === "/api/history/graph")
|
|
43653
44539
|
return json2(res, 200, historyGraphPayload(ctx(), num2("limit", 150)));
|
|
44540
|
+
if (p === "/api/history/knowledge")
|
|
44541
|
+
return json2(
|
|
44542
|
+
res,
|
|
44543
|
+
200,
|
|
44544
|
+
historyGraphKnowledgePayload(ctx(), {
|
|
44545
|
+
focus: str2("focus"),
|
|
44546
|
+
branches: num2("branches", 8),
|
|
44547
|
+
commitsPerBranch: num2("commits", 25),
|
|
44548
|
+
filesPerCommit: num2("files", 12),
|
|
44549
|
+
limit: num2("limit", 400)
|
|
44550
|
+
})
|
|
44551
|
+
);
|
|
43654
44552
|
if (p === "/api/history/risk")
|
|
43655
44553
|
return json2(res, 200, historyRiskPayload(ctx(), strList(q.get("paths"))));
|
|
43656
44554
|
if (p === "/api/history/symbol") {
|
|
@@ -43705,6 +44603,8 @@ var init_server = __esm({
|
|
|
43705
44603
|
init_api2();
|
|
43706
44604
|
init_graph_api();
|
|
43707
44605
|
init_history_api();
|
|
44606
|
+
init_history_graph_api();
|
|
44607
|
+
init_knowledge_api();
|
|
43708
44608
|
MIME = {
|
|
43709
44609
|
".html": "text/html; charset=utf-8",
|
|
43710
44610
|
".js": "text/javascript; charset=utf-8",
|
|
@@ -43727,7 +44627,7 @@ var statusline_exports = {};
|
|
|
43727
44627
|
__export(statusline_exports, {
|
|
43728
44628
|
renderStatusline: () => renderStatusline
|
|
43729
44629
|
});
|
|
43730
|
-
import { existsSync as
|
|
44630
|
+
import { existsSync as existsSync26, readFileSync as readFileSync23, writeFileSync as writeFileSync9 } from "fs";
|
|
43731
44631
|
function renderStatusline(raw) {
|
|
43732
44632
|
let input2 = {};
|
|
43733
44633
|
try {
|
|
@@ -43736,7 +44636,7 @@ function renderStatusline(raw) {
|
|
|
43736
44636
|
}
|
|
43737
44637
|
const cache3 = paths().statuslineCache;
|
|
43738
44638
|
try {
|
|
43739
|
-
if (
|
|
44639
|
+
if (existsSync26(cache3)) {
|
|
43740
44640
|
const c = JSON.parse(readFileSync23(cache3, "utf8"));
|
|
43741
44641
|
if (Date.now() - c.at < 5e3 && c.key === (input2.session_id ?? "")) return c.line;
|
|
43742
44642
|
}
|