eminiar 0.10.0 → 0.10.1
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 +214 -50
- package/dist/hook.js +161 -41
- package/dist/server.js +247 -57
- package/package.json +1 -1
- package/skills/em-docs/SKILL.md +3 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "eminiar",
|
|
3
3
|
"displayName": "Eminiar",
|
|
4
|
-
"version": "0.10.
|
|
4
|
+
"version": "0.10.1",
|
|
5
5
|
"description": "Local-first context, memory and knowledge graph for Claude Code: sandboxed execution, FTS5 + vector search, session continuity, code graph.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Ryan Dick"
|
package/dist/cli.js
CHANGED
|
@@ -21270,6 +21270,14 @@ function findOnPath(bin) {
|
|
|
21270
21270
|
}
|
|
21271
21271
|
return void 0;
|
|
21272
21272
|
}
|
|
21273
|
+
function requirePreamble(code) {
|
|
21274
|
+
if (!/\brequire\s*\(/.test(code)) return void 0;
|
|
21275
|
+
if (/\b(?:const|let|var|function)\s+require\b/.test(code) || /\bcreateRequire\b/.test(code)) {
|
|
21276
|
+
return void 0;
|
|
21277
|
+
}
|
|
21278
|
+
return `${REQUIRE_PREAMBLE}
|
|
21279
|
+
`;
|
|
21280
|
+
}
|
|
21273
21281
|
function preferredShell() {
|
|
21274
21282
|
const sh = process.env.SHELL ?? "";
|
|
21275
21283
|
if (/\/(zsh|bash)$/.test(sh) && existsSync5(sh)) return sh;
|
|
@@ -21290,13 +21298,14 @@ function availableLanguages() {
|
|
|
21290
21298
|
return { id: l.id, bin };
|
|
21291
21299
|
});
|
|
21292
21300
|
}
|
|
21293
|
-
var require3, tsxPath, LANGUAGES;
|
|
21301
|
+
var require3, REQUIRE_PREAMBLE, tsxPath, LANGUAGES;
|
|
21294
21302
|
var init_languages = __esm({
|
|
21295
21303
|
"src/sandbox/languages.ts"() {
|
|
21296
21304
|
"use strict";
|
|
21297
21305
|
init_esm_shims();
|
|
21298
21306
|
init_paths();
|
|
21299
21307
|
require3 = createRequire2(import.meta.url);
|
|
21308
|
+
REQUIRE_PREAMBLE = 'import { createRequire } from "node:module"; const require = createRequire(process.env.EMINIAR_PROJECT_ROOT ? process.env.EMINIAR_PROJECT_ROOT + "/package.json" : import.meta.url);';
|
|
21300
21309
|
LANGUAGES = [
|
|
21301
21310
|
{
|
|
21302
21311
|
id: "shell",
|
|
@@ -21310,14 +21319,16 @@ var init_languages = __esm({
|
|
|
21310
21319
|
aliases: ["js", "node", "mjs"],
|
|
21311
21320
|
ext: ".mjs",
|
|
21312
21321
|
detect: () => process.execPath,
|
|
21313
|
-
argv: (bin, file3) => [bin, "--no-warnings", file3]
|
|
21322
|
+
argv: (bin, file3) => [bin, "--no-warnings", file3],
|
|
21323
|
+
preamble: requirePreamble
|
|
21314
21324
|
},
|
|
21315
21325
|
{
|
|
21316
21326
|
id: "typescript",
|
|
21317
21327
|
aliases: ["ts"],
|
|
21318
21328
|
ext: ".ts",
|
|
21319
21329
|
detect: () => process.execPath,
|
|
21320
|
-
argv: (bin, file3) => [bin, "--no-warnings", tsxCli(), file3]
|
|
21330
|
+
argv: (bin, file3) => [bin, "--no-warnings", tsxCli(), file3],
|
|
21331
|
+
preamble: requirePreamble
|
|
21321
21332
|
},
|
|
21322
21333
|
{
|
|
21323
21334
|
id: "python",
|
|
@@ -21524,6 +21535,10 @@ var init_terms = __esm({
|
|
|
21524
21535
|
});
|
|
21525
21536
|
|
|
21526
21537
|
// src/storage/sqlite/schema.ts
|
|
21538
|
+
function hasColumn(db, table, column) {
|
|
21539
|
+
const cols = db.pragma(`table_info(${table})`);
|
|
21540
|
+
return cols.some((c) => c.name === column);
|
|
21541
|
+
}
|
|
21527
21542
|
function vecTableSql(dims) {
|
|
21528
21543
|
return `CREATE VIRTUAL TABLE IF NOT EXISTS chunk_vec USING vec0(chunk_id INTEGER PRIMARY KEY, embedding float[${dims}])`;
|
|
21529
21544
|
}
|
|
@@ -21866,7 +21881,6 @@ CREATE TABLE IF NOT EXISTS transcript_files (
|
|
|
21866
21881
|
offset INTEGER NOT NULL DEFAULT 0,
|
|
21867
21882
|
size INTEGER NOT NULL DEFAULT 0,
|
|
21868
21883
|
mtime INTEGER NOT NULL DEFAULT 0,
|
|
21869
|
-
head_hash TEXT,
|
|
21870
21884
|
ingested_at INTEGER NOT NULL
|
|
21871
21885
|
);
|
|
21872
21886
|
|
|
@@ -21894,6 +21908,16 @@ CREATE TABLE IF NOT EXISTS statusline_snapshots (
|
|
|
21894
21908
|
CREATE INDEX IF NOT EXISTS statusline_snapshots_session_ts ON statusline_snapshots(session_id, ts);
|
|
21895
21909
|
CREATE INDEX IF NOT EXISTS statusline_snapshots_ts ON statusline_snapshots(ts);
|
|
21896
21910
|
`
|
|
21911
|
+
},
|
|
21912
|
+
{
|
|
21913
|
+
// transcript_files.head_hash (fingerprint of the file head, so a rewrite with the same
|
|
21914
|
+
// size is noticed) was first added to v5 after v5 had already run on real databases, so
|
|
21915
|
+
// version-5 databases exist both with and without the column. Add it only when missing.
|
|
21916
|
+
version: 6,
|
|
21917
|
+
up(db) {
|
|
21918
|
+
if (!hasColumn(db, "transcript_files", "head_hash"))
|
|
21919
|
+
db.exec("ALTER TABLE transcript_files ADD COLUMN head_hash TEXT");
|
|
21920
|
+
}
|
|
21897
21921
|
}
|
|
21898
21922
|
];
|
|
21899
21923
|
}
|
|
@@ -22013,7 +22037,8 @@ var init_adapter = __esm({
|
|
|
22013
22037
|
if (pending2.length) {
|
|
22014
22038
|
this.transaction(() => {
|
|
22015
22039
|
for (const m of pending2) {
|
|
22016
|
-
this.db.exec(m.sql);
|
|
22040
|
+
if (m.sql) this.db.exec(m.sql);
|
|
22041
|
+
m.up?.(this.db);
|
|
22017
22042
|
this.metaSet("schema_version", String(m.version));
|
|
22018
22043
|
}
|
|
22019
22044
|
});
|
|
@@ -22232,18 +22257,42 @@ var init_adapter = __esm({
|
|
|
22232
22257
|
}
|
|
22233
22258
|
vecSearch(embedding, k, filters) {
|
|
22234
22259
|
if (!this.vectors) return [];
|
|
22235
|
-
const raw = this.db.prepare(
|
|
22236
|
-
"SELECT chunk_id id, distance FROM chunk_vec WHERE embedding MATCH ? AND k = ? ORDER BY distance"
|
|
22237
|
-
).all(embedding, BigInt(filters ? k * 4 : k));
|
|
22238
|
-
if (!filters || !raw.length) return raw.map((r) => ({ chunkId: r.id, distance: r.distance }));
|
|
22239
22260
|
const { where, params } = filterSql(filters);
|
|
22240
|
-
|
|
22241
|
-
|
|
22242
|
-
|
|
22243
|
-
|
|
22244
|
-
|
|
22245
|
-
|
|
22246
|
-
|
|
22261
|
+
if (!where) return this.knn(embedding, k);
|
|
22262
|
+
try {
|
|
22263
|
+
return this.knn(
|
|
22264
|
+
embedding,
|
|
22265
|
+
k,
|
|
22266
|
+
`AND chunk_id IN (SELECT c.id FROM chunks c JOIN documents d ON d.id = c.document_id WHERE 1=1${where})`,
|
|
22267
|
+
params
|
|
22268
|
+
);
|
|
22269
|
+
} catch {
|
|
22270
|
+
return this.vecSearchWidening(embedding, k, where, params);
|
|
22271
|
+
}
|
|
22272
|
+
}
|
|
22273
|
+
knn(embedding, k, extra = "", params = []) {
|
|
22274
|
+
return this.db.prepare(
|
|
22275
|
+
`SELECT chunk_id id, distance FROM chunk_vec WHERE embedding MATCH ? AND k = ? ${extra} ORDER BY distance`
|
|
22276
|
+
).all(embedding, BigInt(k), ...params).map((r) => ({ chunkId: r.id, distance: r.distance }));
|
|
22277
|
+
}
|
|
22278
|
+
/** Fallback filtered KNN: fetch k*4, k*16, ... nearest until k pass the filter or all were seen. */
|
|
22279
|
+
vecSearchWidening(embedding, k, where, params) {
|
|
22280
|
+
const total = this.vecCount();
|
|
22281
|
+
for (let fetch2 = k * 4; ; fetch2 *= 4) {
|
|
22282
|
+
const raw = this.knn(embedding, Math.min(fetch2, total));
|
|
22283
|
+
if (!raw.length) return [];
|
|
22284
|
+
const allowed = /* @__PURE__ */ new Set();
|
|
22285
|
+
for (let i = 0; i < raw.length; i += 400) {
|
|
22286
|
+
const ids = raw.slice(i, i + 400).map((r) => r.chunkId);
|
|
22287
|
+
for (const r of this.db.prepare(
|
|
22288
|
+
`SELECT c.id FROM chunks c JOIN documents d ON d.id = c.document_id
|
|
22289
|
+
WHERE c.id IN (${ids.map(() => "?").join(",")})${where}`
|
|
22290
|
+
).all(...ids, ...params))
|
|
22291
|
+
allowed.add(r.id);
|
|
22292
|
+
}
|
|
22293
|
+
const hits = raw.filter((r) => allowed.has(r.chunkId)).slice(0, k);
|
|
22294
|
+
if (hits.length >= k || fetch2 >= total) return hits;
|
|
22295
|
+
}
|
|
22247
22296
|
}
|
|
22248
22297
|
unembeddedChunkIds(limit) {
|
|
22249
22298
|
return this.db.prepare("SELECT id FROM chunks WHERE embedded = 0 ORDER BY id LIMIT ?").all(limit).map((r) => r.id);
|
|
@@ -22522,6 +22571,26 @@ var init_adapter = __esm({
|
|
|
22522
22571
|
}
|
|
22523
22572
|
return out;
|
|
22524
22573
|
}
|
|
22574
|
+
graphDependentFileCounts(paths2, kinds) {
|
|
22575
|
+
const out = /* @__PURE__ */ new Map();
|
|
22576
|
+
if (!paths2.length || !kinds.length) return out;
|
|
22577
|
+
const kph = kinds.map(() => "?").join(",");
|
|
22578
|
+
for (let i = 0; i < paths2.length; i += 400) {
|
|
22579
|
+
const batch = paths2.slice(i, i + 400);
|
|
22580
|
+
const pph = batch.map(() => "?").join(",");
|
|
22581
|
+
for (const r of this.db.prepare(
|
|
22582
|
+
`SELECT d.path p, COUNT(DISTINCT s.path) n
|
|
22583
|
+
FROM graph_nodes d
|
|
22584
|
+
JOIN graph_edges e ON e.dst = d.id AND e.kind IN (${kph})
|
|
22585
|
+
JOIN graph_nodes s ON s.id = e.src
|
|
22586
|
+
WHERE d.path IN (${pph}) AND s.path IS NOT NULL AND s.path <> d.path
|
|
22587
|
+
GROUP BY d.path`
|
|
22588
|
+
).all(...kinds, ...batch)) {
|
|
22589
|
+
out.set(r.p, r.n);
|
|
22590
|
+
}
|
|
22591
|
+
}
|
|
22592
|
+
return out;
|
|
22593
|
+
}
|
|
22525
22594
|
edgesWhere(col, ids, kinds) {
|
|
22526
22595
|
if (!ids.length) return [];
|
|
22527
22596
|
const out = [];
|
|
@@ -30413,7 +30482,7 @@ function chunkMarkdown(text3, baseTitle, opts = DEFAULT_CHUNKER) {
|
|
|
30413
30482
|
if (/^\s*(```|~~~)/.test(line)) inFence = !inFence;
|
|
30414
30483
|
const m = !inFence && /^(#{1,6})\s+(.+?)\s*#*\s*$/.exec(line);
|
|
30415
30484
|
if (m) {
|
|
30416
|
-
|
|
30485
|
+
sections.push(current);
|
|
30417
30486
|
const level = m[1].length;
|
|
30418
30487
|
while (stack.length && stack[stack.length - 1].level >= level) stack.pop();
|
|
30419
30488
|
stack.push({ level, text: m[2] });
|
|
@@ -30422,14 +30491,23 @@ function chunkMarkdown(text3, baseTitle, opts = DEFAULT_CHUNKER) {
|
|
|
30422
30491
|
}
|
|
30423
30492
|
current.lines.push(line);
|
|
30424
30493
|
});
|
|
30425
|
-
|
|
30494
|
+
sections.push(current);
|
|
30426
30495
|
const out = [];
|
|
30427
|
-
|
|
30496
|
+
sections.forEach((s, k) => {
|
|
30428
30497
|
const title = s.path.length ? `${baseTitle} > ${s.path.join(" > ")}` : baseTitle;
|
|
30429
30498
|
const body = s.lines.join("\n");
|
|
30499
|
+
if (!body.trim()) {
|
|
30500
|
+
const next = sections[k + 1];
|
|
30501
|
+
const isParent = next !== void 0 && next.path.length > s.path.length && s.path.every((p, j) => next.path[j] === p);
|
|
30502
|
+
if (s.path.length && !isParent) {
|
|
30503
|
+
const heading = s.path[s.path.length - 1];
|
|
30504
|
+
out.push({ title, body: heading, startLine: s.startLine, endLine: s.startLine });
|
|
30505
|
+
}
|
|
30506
|
+
return;
|
|
30507
|
+
}
|
|
30430
30508
|
if (body.length <= opts.maxChars) {
|
|
30431
30509
|
out.push({ title, body, startLine: s.startLine, endLine: s.startLine + s.lines.length - 1 });
|
|
30432
|
-
|
|
30510
|
+
return;
|
|
30433
30511
|
}
|
|
30434
30512
|
let fence = false;
|
|
30435
30513
|
const fenceState = s.lines.map((l) => {
|
|
@@ -30445,7 +30523,7 @@ function chunkMarkdown(text3, baseTitle, opts = DEFAULT_CHUNKER) {
|
|
|
30445
30523
|
s.startLine - 1
|
|
30446
30524
|
)
|
|
30447
30525
|
);
|
|
30448
|
-
}
|
|
30526
|
+
});
|
|
30449
30527
|
return out;
|
|
30450
30528
|
}
|
|
30451
30529
|
var init_markdown = __esm({
|
|
@@ -30557,7 +30635,20 @@ import { readFileSync as readFileSync10, statSync } from "fs";
|
|
|
30557
30635
|
import { relative, resolve as resolve2 } from "path";
|
|
30558
30636
|
function indexContent(db, input2) {
|
|
30559
30637
|
const kind = input2.kind ?? (input2.contentType === "code" ? "code" : input2.contentType === "json" ? "json" : void 0);
|
|
30560
|
-
const
|
|
30638
|
+
const cut = input2.chunks ? { chunks: finalize2(input2.chunks, input2.contentType ?? "code"), kind: kind ?? "code" } : chunk(input2.content, input2.title, kind, input2.uri);
|
|
30639
|
+
const k = cut.kind;
|
|
30640
|
+
const fallback = cut.chunks.length === 0 && input2.content.trim().length > 0;
|
|
30641
|
+
const chunks = fallback ? finalize2(
|
|
30642
|
+
[
|
|
30643
|
+
{
|
|
30644
|
+
title: input2.title,
|
|
30645
|
+
body: input2.content,
|
|
30646
|
+
startLine: 1,
|
|
30647
|
+
endLine: input2.content.split("\n").length
|
|
30648
|
+
}
|
|
30649
|
+
],
|
|
30650
|
+
kindToContentType(k)
|
|
30651
|
+
) : cut.chunks;
|
|
30561
30652
|
const contentHash = hashText(input2.content);
|
|
30562
30653
|
const existing = input2.uri ? db.findDocumentByUri(input2.uri) : void 0;
|
|
30563
30654
|
if (existing && existing.content_hash === contentHash) {
|
|
@@ -30567,7 +30658,8 @@ function indexContent(db, input2) {
|
|
|
30567
30658
|
created: false,
|
|
30568
30659
|
chunks: chunks.length,
|
|
30569
30660
|
chars: input2.content.length,
|
|
30570
|
-
kind: k
|
|
30661
|
+
kind: k,
|
|
30662
|
+
fallback
|
|
30571
30663
|
};
|
|
30572
30664
|
}
|
|
30573
30665
|
db.deleteDocument(existing.id);
|
|
@@ -30584,11 +30676,25 @@ function indexContent(db, input2) {
|
|
|
30584
30676
|
},
|
|
30585
30677
|
chunks
|
|
30586
30678
|
);
|
|
30587
|
-
return {
|
|
30679
|
+
return {
|
|
30680
|
+
documentId: id,
|
|
30681
|
+
created,
|
|
30682
|
+
chunks: chunks.length,
|
|
30683
|
+
chars: input2.content.length,
|
|
30684
|
+
kind: k,
|
|
30685
|
+
fallback
|
|
30686
|
+
};
|
|
30588
30687
|
}
|
|
30589
30688
|
function indexPath(db, root, target, opts = {}) {
|
|
30590
30689
|
const abs = resolve2(root, target);
|
|
30591
|
-
const summary = {
|
|
30690
|
+
const summary = {
|
|
30691
|
+
files: 0,
|
|
30692
|
+
indexed: 0,
|
|
30693
|
+
unchanged: 0,
|
|
30694
|
+
skipped: [],
|
|
30695
|
+
chunks: 0,
|
|
30696
|
+
fallbacks: 0
|
|
30697
|
+
};
|
|
30592
30698
|
const st = statSync(abs);
|
|
30593
30699
|
const files = st.isDirectory() ? import_fast_glob.default.sync(opts.globs ?? ["**/*.md"], {
|
|
30594
30700
|
cwd: abs,
|
|
@@ -30623,6 +30729,7 @@ function indexPath(db, root, target, opts = {}) {
|
|
|
30623
30729
|
if (r.created) summary.indexed++;
|
|
30624
30730
|
else summary.unchanged++;
|
|
30625
30731
|
summary.chunks += r.chunks;
|
|
30732
|
+
if (r.fallback) summary.fallbacks++;
|
|
30626
30733
|
} catch (err) {
|
|
30627
30734
|
summary.skipped.push({ path: rel, reason: err.message });
|
|
30628
30735
|
}
|
|
@@ -30698,7 +30805,7 @@ var init_em_index = __esm({
|
|
|
30698
30805
|
});
|
|
30699
30806
|
kickEmbedding(ctx, db);
|
|
30700
30807
|
return text(
|
|
30701
|
-
`${r.created ? "indexed" : "unchanged"} "${args.title}" as doc ${r.documentId}: ${r.chunks} chunks, ${r.chars} chars (${r.kind})`
|
|
30808
|
+
`${r.created ? "indexed" : "unchanged"} "${args.title}" as doc ${r.documentId}: ${r.chunks} chunks, ${r.chars} chars (${r.kind})${r.fallback ? " (no sections found; indexed as one chunk)" : ""}`
|
|
30702
30809
|
);
|
|
30703
30810
|
}
|
|
30704
30811
|
if (!args.path) return errorResult("provide `path` or `content`");
|
|
@@ -30708,7 +30815,7 @@ var init_em_index = __esm({
|
|
|
30708
30815
|
recursive: args.recursive ?? true
|
|
30709
30816
|
});
|
|
30710
30817
|
const lines = [
|
|
30711
|
-
`${args.path}: ${s.files} file(s), ${s.indexed} indexed, ${s.unchanged} unchanged, ${s.chunks} chunks`
|
|
30818
|
+
`${args.path}: ${s.files} file(s), ${s.indexed} indexed, ${s.unchanged} unchanged, ${s.chunks} chunks${s.fallbacks ? ` (${s.fallbacks} with no sections found; indexed as one chunk)` : ""}`
|
|
30712
30819
|
];
|
|
30713
30820
|
for (const sk of s.skipped.slice(0, 10)) lines.push(` skipped ${sk.path}: ${sk.reason}`);
|
|
30714
30821
|
if (s.skipped.length > 10) lines.push(` \u2026 ${s.skipped.length - 10} more skipped`);
|
|
@@ -38324,6 +38431,9 @@ import { dirname as dirname9, join as join24 } from "path";
|
|
|
38324
38431
|
function docsTag(name) {
|
|
38325
38432
|
return `${DOCS_PREFIX}${name}`;
|
|
38326
38433
|
}
|
|
38434
|
+
function nameFromTag(tag) {
|
|
38435
|
+
return tag.startsWith(DOCS_PREFIX) ? tag.slice(DOCS_PREFIX.length) : void 0;
|
|
38436
|
+
}
|
|
38327
38437
|
function isValidName2(name) {
|
|
38328
38438
|
return NAME_RE2.test(name);
|
|
38329
38439
|
}
|
|
@@ -40848,7 +40958,7 @@ function removeDocSource(ctx, db, name) {
|
|
|
40848
40958
|
const removed = removeSource(ctx, name);
|
|
40849
40959
|
const documents = db.deleteDocumentsBySource(docsTag(name));
|
|
40850
40960
|
clearSyncState(db, name);
|
|
40851
|
-
if (removed) syncDocsGraphQuietly(ctx, db);
|
|
40961
|
+
if (removed || documents) syncDocsGraphQuietly(ctx, db);
|
|
40852
40962
|
return { removed, documents };
|
|
40853
40963
|
}
|
|
40854
40964
|
function formatAdd(o) {
|
|
@@ -40903,7 +41013,8 @@ var init_add = __esm({
|
|
|
40903
41013
|
var em_docs_exports = {};
|
|
40904
41014
|
__export(em_docs_exports, {
|
|
40905
41015
|
emDocs: () => emDocs,
|
|
40906
|
-
formatDiscovery: () => formatDiscovery
|
|
41016
|
+
formatDiscovery: () => formatDiscovery,
|
|
41017
|
+
orphanedDocSources: () => orphanedDocSources
|
|
40907
41018
|
});
|
|
40908
41019
|
function toInput(a) {
|
|
40909
41020
|
return {
|
|
@@ -40930,6 +41041,26 @@ function ago(ts) {
|
|
|
40930
41041
|
if (s < 172800) return `${Math.round(s / 3600)}h ago`;
|
|
40931
41042
|
return `${Math.round(s / 86400)}d ago`;
|
|
40932
41043
|
}
|
|
41044
|
+
function orphanedDocSources(db, registered, stats = db.documentSourceStats(DOCS_PREFIX)) {
|
|
41045
|
+
const known = new Set(registered);
|
|
41046
|
+
const out = [];
|
|
41047
|
+
for (const s of stats) {
|
|
41048
|
+
const name = nameFromTag(s.source);
|
|
41049
|
+
if (name === void 0 || known.has(name)) continue;
|
|
41050
|
+
out.push({ name, documents: s.documents, chunks: s.chunks, latest: s.latest });
|
|
41051
|
+
}
|
|
41052
|
+
return out;
|
|
41053
|
+
}
|
|
41054
|
+
function formatOrphans(orphans2) {
|
|
41055
|
+
const names = orphans2.map((o) => o.name);
|
|
41056
|
+
return [
|
|
41057
|
+
`# Orphaned in the index (${orphans2.length}; pages without a manifest entry)`,
|
|
41058
|
+
...orphans2.map(
|
|
41059
|
+
(o) => `- ${o.name} \xB7 ${o.documents} doc(s) \xB7 ${o.chunks} chunks \xB7 indexed ${ago(o.latest)}`
|
|
41060
|
+
),
|
|
41061
|
+
`em_docs {action:"remove", names:${JSON.stringify(names)}} drops them; {action:"add", pick:"recommended"} or {action:"add", name, url|path} re-registers.`
|
|
41062
|
+
];
|
|
41063
|
+
}
|
|
40933
41064
|
function formatDiscovery(d, max = 40) {
|
|
40934
41065
|
const lines = [];
|
|
40935
41066
|
lines.push(
|
|
@@ -41112,19 +41243,30 @@ var init_em_docs = __esm({
|
|
|
41112
41243
|
case "remove": {
|
|
41113
41244
|
const names = [...args.name ? [args.name] : [], ...args.names ?? []];
|
|
41114
41245
|
if (!names.length) return errorResult("remove needs `name` or `names`");
|
|
41246
|
+
let unknown2 = 0;
|
|
41115
41247
|
const lines = names.map((n) => {
|
|
41116
41248
|
const r = removeDocSource(ctx, db, n);
|
|
41117
|
-
|
|
41249
|
+
if (r.removed) return `removed ${n} (${r.documents} document(s) dropped)`;
|
|
41250
|
+
if (r.documents) return `removed ${n} (orphaned; ${r.documents} document(s) dropped)`;
|
|
41251
|
+
unknown2++;
|
|
41252
|
+
return `${n}: not registered and not in the index; em_docs {action:"list"} shows both`;
|
|
41118
41253
|
});
|
|
41119
|
-
|
|
41254
|
+
const out = lines.join("\n");
|
|
41255
|
+
return unknown2 === names.length ? errorResult(out) : done(out);
|
|
41120
41256
|
}
|
|
41121
41257
|
case "list": {
|
|
41122
41258
|
const m = readManifest(ctx);
|
|
41123
|
-
|
|
41124
|
-
|
|
41125
|
-
|
|
41126
|
-
)
|
|
41127
|
-
|
|
41259
|
+
const sourceStats = db.documentSourceStats(DOCS_PREFIX);
|
|
41260
|
+
const orphans2 = orphanedDocSources(
|
|
41261
|
+
db,
|
|
41262
|
+
m.sources.map((s) => s.name),
|
|
41263
|
+
sourceStats
|
|
41264
|
+
);
|
|
41265
|
+
if (!m.sources.length) {
|
|
41266
|
+
const none = `no documentation sources registered (${manifestPath(ctx)}). em_docs {action:"discover"} finds local docs and the libraries, services and APIs this project uses.`;
|
|
41267
|
+
return done(orphans2.length ? [none, "", ...formatOrphans(orphans2)].join("\n") : none);
|
|
41268
|
+
}
|
|
41269
|
+
const stats = new Map(sourceStats.map((s) => [s.source, s]));
|
|
41128
41270
|
const catalog = mergedCatalog(ctx.config.docs.catalog);
|
|
41129
41271
|
const categorised = m.sources.map((s) => ({
|
|
41130
41272
|
s,
|
|
@@ -41155,9 +41297,10 @@ var init_em_docs = __esm({
|
|
|
41155
41297
|
`- ${s.name} [${s.kind}]${s.provider ? ` \u2190 ${s.provider}` : ""}${s.tags?.length ? ` (${s.tags.join(", ")})` : ""} \xB7 ${st?.documents ?? 0} doc(s) \xB7 ${st?.chunks ?? 0} chunks \xB7 ${state} \xB7 ${where}`
|
|
41156
41298
|
);
|
|
41157
41299
|
}
|
|
41300
|
+
if (orphans2.length) lines.push("", ...formatOrphans(orphans2));
|
|
41158
41301
|
lines.push(
|
|
41159
41302
|
"",
|
|
41160
|
-
`${m.sources.length} source(s); em_docs {action:"lookup", query} searches them all, {action:"sync"} refreshes.`
|
|
41303
|
+
`${m.sources.length} source(s)${orphans2.length ? ` + ${orphans2.length} orphaned` : ""}; em_docs {action:"lookup", query} searches them all, {action:"sync"} refreshes.`
|
|
41161
41304
|
);
|
|
41162
41305
|
return done(lines.join("\n"));
|
|
41163
41306
|
}
|
|
@@ -41203,10 +41346,18 @@ ${r.urls.map((u) => ` ${u}`).join("\n")}
|
|
|
41203
41346
|
raw += r.hits.reduce((n, h) => n + h.chunk.body.length, 0);
|
|
41204
41347
|
responses.push(r);
|
|
41205
41348
|
}
|
|
41206
|
-
|
|
41207
|
-
|
|
41208
|
-
|
|
41349
|
+
let out = formatResults(responses, args.max_tokens ?? ctx.config.search.defaultMaxTokens);
|
|
41350
|
+
const hitSources = new Set(
|
|
41351
|
+
responses.flatMap((r) => r.hits.map((h) => h.chunk.document.source))
|
|
41209
41352
|
);
|
|
41353
|
+
const orphaned = orphanedDocSources(
|
|
41354
|
+
db,
|
|
41355
|
+
m.sources.map((s) => s.name)
|
|
41356
|
+
).filter((o) => hitSources.has(docsTag(o.name)));
|
|
41357
|
+
if (orphaned.length)
|
|
41358
|
+
out += `
|
|
41359
|
+
|
|
41360
|
+
note: ${orphaned.map((o) => o.name).join(", ")} ${orphaned.length === 1 ? "is" : "are"} not in the manifest (orphaned pages); em_docs {action:"list"} explains`;
|
|
41210
41361
|
recordStat(ctx, "em_docs", {
|
|
41211
41362
|
rawChars: raw,
|
|
41212
41363
|
returnedChars: out.length,
|
|
@@ -42467,6 +42618,9 @@ function memoriesForFile(db, rel, opts = {}) {
|
|
|
42467
42618
|
trace
|
|
42468
42619
|
};
|
|
42469
42620
|
}
|
|
42621
|
+
function dependentFiles(db, paths2) {
|
|
42622
|
+
return db.graphDependentFileCounts(paths2, DEPENDENCY_KINDS);
|
|
42623
|
+
}
|
|
42470
42624
|
function riskLine(path3, f) {
|
|
42471
42625
|
if (f.risk < RISKY && !f.bugOrigins) return void 0;
|
|
42472
42626
|
const parts = [`${f.fixes}/${f.commits} commits were fixes`];
|
|
@@ -42494,10 +42648,10 @@ function fileContext(db, rel, opts = {}) {
|
|
|
42494
42648
|
}
|
|
42495
42649
|
const fanKey = `fanin:${rel}`;
|
|
42496
42650
|
if (opts.edit && !seen.has(fanKey)) {
|
|
42497
|
-
const
|
|
42498
|
-
if (
|
|
42651
|
+
const dependents = dependentFiles(db, [rel]).get(rel) ?? 0;
|
|
42652
|
+
if (dependents >= FAN_IN) {
|
|
42499
42653
|
lines.push(
|
|
42500
|
-
`eminiar graph: ${rel} is imported by ${
|
|
42654
|
+
`eminiar graph: ${rel} is imported by ${dependents} files; em_graph_impact {node:"${fileId(rel)}"} lists them before you change an exported signature.`
|
|
42501
42655
|
);
|
|
42502
42656
|
keys.push(fanKey);
|
|
42503
42657
|
}
|
|
@@ -42554,6 +42708,7 @@ var init_recall = __esm({
|
|
|
42554
42708
|
"use strict";
|
|
42555
42709
|
init_esm_shims();
|
|
42556
42710
|
init_ids();
|
|
42711
|
+
init_metrics();
|
|
42557
42712
|
init_model();
|
|
42558
42713
|
init_pipeline();
|
|
42559
42714
|
init_query();
|
|
@@ -44546,12 +44701,17 @@ function usageLine(ctx, sessionId) {
|
|
|
44546
44701
|
if (!usageEnabled(ctx)) return void 0;
|
|
44547
44702
|
try {
|
|
44548
44703
|
const db = getStorage(ctx);
|
|
44549
|
-
|
|
44704
|
+
let failed;
|
|
44705
|
+
try {
|
|
44706
|
+
ingestSession(db, ctx.root, sessionId, void 0, { overrides: ctx.config.usage.pricing });
|
|
44707
|
+
} catch (err) {
|
|
44708
|
+
failed = `usage: ingest failed: ${err.message}`;
|
|
44709
|
+
}
|
|
44550
44710
|
const b = usageBrief(db, ctx, { sessionId });
|
|
44551
|
-
|
|
44552
|
-
return
|
|
44553
|
-
} catch {
|
|
44554
|
-
return
|
|
44711
|
+
const line = b.todayUsd || b.sessionUsd ? `usage: today ${formatUsd(b.todayUsd)} \xB7 session ${formatUsd(b.sessionUsd)} \xB7 see em_usage` : void 0;
|
|
44712
|
+
return [line, failed].filter((s) => s).join("\n") || void 0;
|
|
44713
|
+
} catch (err) {
|
|
44714
|
+
return `usage: unavailable: ${err.message}`;
|
|
44555
44715
|
}
|
|
44556
44716
|
}
|
|
44557
44717
|
function mergeByTool(rows) {
|
|
@@ -45825,7 +45985,10 @@ var init_em_search = __esm({
|
|
|
45825
45985
|
}
|
|
45826
45986
|
}
|
|
45827
45987
|
const out = formatResults(responses, args.max_tokens ?? ctx.config.search.defaultMaxTokens);
|
|
45828
|
-
|
|
45988
|
+
let note = mode !== "lexical" && mode !== "regex" && !vectorSearch && ctx.config.embeddings.enabled ? "\n\n(embeddings: model still loading \u2014 lexical results only this time)" : "";
|
|
45989
|
+
const sources = Array.isArray(args.source) ? args.source : args.source ? [args.source] : [];
|
|
45990
|
+
if (mode === "semantic" && !ctx.config.docs.embed && sources.some((s) => s.startsWith("docs:")) && responses.every((r) => r.hits.length === 0))
|
|
45991
|
+
note += '\n\n(docs:* pages are indexed without vectors because docs.embed is off; use mode "hybrid" or "lexical", or set docs.embed: true in eminiar.config.json and em_docs {action:"sync", force:true})';
|
|
45829
45992
|
recordStat(ctx, "em_search", {
|
|
45830
45993
|
rawChars: responses.reduce(
|
|
45831
45994
|
(a, r) => a + r.hits.reduce((b, h) => b + h.chunk.body.length, 0),
|
|
@@ -49037,7 +49200,8 @@ try {
|
|
|
49037
49200
|
} catch (err) {
|
|
49038
49201
|
console.error(`eminiar ${cmd} failed: ${err.message}`);
|
|
49039
49202
|
if (process.env.EMINIAR_DEBUG) console.error(err.stack);
|
|
49040
|
-
if (cmd
|
|
49203
|
+
if (cmd === "usage") console.error("run `eminiar doctor` to check the installation");
|
|
49204
|
+
else if (cmd !== "doctor") console.error(formatDoctor(runDoctor()));
|
|
49041
49205
|
process.exit(1);
|
|
49042
49206
|
}
|
|
49043
49207
|
/*! Bundled license information:
|