akm-cli 0.9.0-beta.54 → 0.9.0-beta.56
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +5 -3
- package/dist/commands/agent/contribute-cli.js +2 -3
- package/dist/commands/env/env-cli.js +187 -202
- package/dist/commands/env/secret-cli.js +109 -121
- package/dist/commands/feedback-cli.js +152 -155
- package/dist/commands/health/advisories.js +151 -0
- package/dist/commands/health/improve-metrics.js +754 -0
- package/dist/commands/health/llm-usage.js +65 -0
- package/dist/commands/health/md-report.js +103 -0
- package/dist/commands/health/metrics.js +278 -0
- package/dist/commands/health/task-runs.js +135 -0
- package/dist/commands/health/types.js +18 -0
- package/dist/commands/health/windows.js +196 -0
- package/dist/commands/health.js +14 -1624
- package/dist/commands/improve/anti-collapse.js +170 -0
- package/dist/commands/improve/collapse-detector.js +3 -2
- package/dist/commands/improve/consolidate.js +636 -633
- package/dist/commands/improve/dedup.js +1 -1
- package/dist/commands/improve/distill/content-repair.js +202 -0
- package/dist/commands/improve/distill/promote-memory.js +228 -0
- package/dist/commands/improve/distill/quality-gate.js +233 -0
- package/dist/commands/improve/distill-guards.js +127 -0
- package/dist/commands/improve/distill.js +49 -575
- package/dist/commands/improve/extract-cli.js +74 -76
- package/dist/commands/improve/extract.js +6 -4
- package/dist/commands/improve/hot-probation.js +45 -0
- package/dist/commands/improve/improve-auto-accept.js +3 -2
- package/dist/commands/improve/improve-cli.js +14 -13
- package/dist/commands/improve/improve-result-file.js +2 -1
- package/dist/commands/improve/improve.js +6 -5
- package/dist/commands/improve/loop-stages.js +19 -21
- package/dist/commands/improve/preparation.js +4 -2
- package/dist/commands/improve/procedural.js +10 -31
- package/dist/commands/improve/recombine.js +19 -43
- package/dist/commands/improve/reflect.js +1 -1
- package/dist/commands/improve/schema-similarity-gate.js +168 -0
- package/dist/commands/improve/shared.js +48 -0
- package/dist/commands/observability-cli.js +4 -4
- package/dist/commands/proposal/drain-policies.js +2 -2
- package/dist/commands/proposal/drain.js +1 -1
- package/dist/commands/proposal/legacy-import.js +115 -0
- package/dist/commands/proposal/proposal-cli.js +3 -3
- package/dist/commands/proposal/proposal.js +2 -1
- package/dist/commands/proposal/propose.js +1 -1
- package/dist/commands/proposal/repository.js +829 -0
- package/dist/commands/proposal/validators/proposals.js +5 -920
- package/dist/commands/read/remember-cli.js +132 -137
- package/dist/commands/read/search-cli.js +1 -1
- package/dist/commands/registry-cli.js +76 -87
- package/dist/commands/sources/add-cli.js +90 -94
- package/dist/commands/sources/history.js +1 -1
- package/dist/commands/sources/schema-repair.js +1 -1
- package/dist/commands/sources/sources-cli.js +3 -3
- package/dist/commands/sources/stash-cli.js +1 -1
- package/dist/commands/tasks/tasks-cli.js +1 -2
- package/dist/commands/wiki-cli.js +2 -3
- package/dist/core/common.js +3 -3
- package/dist/core/config/config-schema.js +6 -0
- package/dist/core/deep-merge.js +38 -0
- package/dist/core/events.js +2 -1
- package/dist/core/logs-db.js +8 -13
- package/dist/core/paths.js +14 -14
- package/dist/core/state-db.js +13 -1140
- package/dist/indexer/db/db.js +96 -723
- package/dist/indexer/db/entry-mapper.js +41 -0
- package/dist/indexer/db/schema.js +516 -0
- package/dist/indexer/feedback/utility-policy.js +75 -0
- package/dist/indexer/graph/graph-extraction.js +2 -1
- package/dist/indexer/index-writer-lock.js +9 -0
- package/dist/indexer/indexer.js +78 -23
- package/dist/indexer/search/fts-query.js +51 -0
- package/dist/integrations/agent/spawn.js +15 -66
- package/dist/llm/embedders/cache.js +3 -1
- package/dist/output/text/helpers.js +13 -0
- package/dist/registry/resolve.js +5 -0
- package/dist/scripts/migrate-storage.js +6908 -7447
- package/dist/scripts/migrations/import-fs-improve-runs-to-db.js +44 -43
- package/dist/setup/legacy-config.js +106 -0
- package/dist/setup/prompt.js +57 -0
- package/dist/setup/providers.js +14 -0
- package/dist/setup/semantic-assets.js +124 -0
- package/dist/setup/setup.js +24 -1607
- package/dist/setup/steps/connection.js +734 -0
- package/dist/setup/steps/output.js +31 -0
- package/dist/setup/steps/platforms.js +124 -0
- package/dist/setup/steps/semantic.js +27 -0
- package/dist/setup/steps/sources.js +222 -0
- package/dist/setup/steps/stashdir.js +42 -0
- package/dist/setup/steps/tasks.js +152 -0
- package/dist/storage/repositories/canaries-repository.js +107 -0
- package/dist/storage/repositories/consolidation-repository.js +38 -0
- package/dist/storage/repositories/embeddings-repository.js +72 -0
- package/dist/storage/repositories/events-repository.js +187 -0
- package/dist/storage/repositories/extract-sessions-repository.js +96 -0
- package/dist/storage/repositories/improve-runs-repository.js +130 -0
- package/dist/storage/repositories/index-db.js +4 -7
- package/dist/storage/repositories/proposals-repository.js +220 -0
- package/dist/storage/repositories/recombine-repository.js +213 -0
- package/dist/storage/repositories/task-history-repository.js +93 -0
- package/dist/storage/sqlite-pragmas.js +3 -3
- package/dist/tasks/runner.js +2 -1
- package/package.json +1 -1
- package/dist/commands/improve/homeostatic.js +0 -497
package/dist/indexer/indexer.js
CHANGED
|
@@ -143,6 +143,7 @@ async function runEmbeddingPhase(ctx) {
|
|
|
143
143
|
*/
|
|
144
144
|
async function runFinalizePhase(ctx) {
|
|
145
145
|
const { db, config, sources, sourceDirs, isIncremental, stashDir, signal, onProgress } = ctx;
|
|
146
|
+
ctx.timing.tFinalizeStart = Date.now();
|
|
146
147
|
// Rebuild FTS after all inserts. Use incremental mode when this whole
|
|
147
148
|
// index run is incremental — only entries touched by `upsertEntry`
|
|
148
149
|
// since the last rebuild are re-indexed.
|
|
@@ -153,10 +154,13 @@ async function runFinalizePhase(ctx) {
|
|
|
153
154
|
});
|
|
154
155
|
ctx.timing.tFtsEnd = Date.now();
|
|
155
156
|
// Re-link detached usage_events and recompute utility scores.
|
|
157
|
+
onProgress({ phase: "finalize", message: "Relinking usage events." });
|
|
156
158
|
relinkUsageEvents(db);
|
|
159
|
+
onProgress({ phase: "finalize", message: "Recomputing utility scores." });
|
|
157
160
|
recomputeUtilityScores(db);
|
|
158
161
|
// Purge LLM cache entries for assets that no longer exist in the index.
|
|
159
162
|
try {
|
|
163
|
+
onProgress({ phase: "finalize", message: "Clearing stale LLM cache entries." });
|
|
160
164
|
clearStaleCacheEntries(db);
|
|
161
165
|
}
|
|
162
166
|
catch {
|
|
@@ -164,6 +168,7 @@ async function runFinalizePhase(ctx) {
|
|
|
164
168
|
}
|
|
165
169
|
// Regenerate each wiki's index.md from its pages' frontmatter. Best-effort.
|
|
166
170
|
try {
|
|
171
|
+
onProgress({ phase: "finalize", message: "Regenerating wiki indexes." });
|
|
167
172
|
const { regenerateAllWikiIndexes } = await import("../wiki/wiki.js");
|
|
168
173
|
regenerateAllWikiIndexes(stashDir);
|
|
169
174
|
}
|
|
@@ -180,6 +185,7 @@ async function runFinalizePhase(ctx) {
|
|
|
180
185
|
warnIfVecMissing(db);
|
|
181
186
|
const totalEntries = getEntryCount(db);
|
|
182
187
|
const semanticEntryCount = getEmbeddableEntryCount(db);
|
|
188
|
+
onProgress({ phase: "finalize", message: "Verifying semantic search state." });
|
|
183
189
|
const verification = verifyIndexState(db, config, semanticEntryCount, embeddingResult);
|
|
184
190
|
if (config.semanticSearchMode === "off") {
|
|
185
191
|
clearSemanticStatus();
|
|
@@ -199,6 +205,7 @@ async function runFinalizePhase(ctx) {
|
|
|
199
205
|
// Store verification result and totalEntries on ctx for the caller to use
|
|
200
206
|
ctx.verification = verification;
|
|
201
207
|
ctx.totalEntries = totalEntries;
|
|
208
|
+
ctx.timing.tFinalizeEnd = Date.now();
|
|
202
209
|
// suppress unused warning — sources was previously used inline
|
|
203
210
|
void sources;
|
|
204
211
|
}
|
|
@@ -239,7 +246,21 @@ export async function akmIndex(options) {
|
|
|
239
246
|
return akmIndexReal(options);
|
|
240
247
|
}
|
|
241
248
|
async function akmIndexReal(options) {
|
|
242
|
-
|
|
249
|
+
const requestedAt = Date.now();
|
|
250
|
+
let acquiredAt = requestedAt;
|
|
251
|
+
return withIndexWriterLease({
|
|
252
|
+
purpose: "akm-index",
|
|
253
|
+
signal: options?.signal,
|
|
254
|
+
onWait: ({ waitedMs }) => {
|
|
255
|
+
options?.onProgress?.({
|
|
256
|
+
phase: "preflight",
|
|
257
|
+
message: `Waiting for index writer lease (${Math.round(waitedMs / 1000)}s elapsed).`,
|
|
258
|
+
});
|
|
259
|
+
},
|
|
260
|
+
onAcquired: ({ waitedMs }) => {
|
|
261
|
+
acquiredAt = requestedAt + waitedMs;
|
|
262
|
+
},
|
|
263
|
+
}, async () => {
|
|
243
264
|
const stashDir = options?.stashDir || resolveStashDir();
|
|
244
265
|
const onProgress = options?.onProgress ?? (() => { });
|
|
245
266
|
const signal = options?.signal;
|
|
@@ -258,10 +279,17 @@ async function akmIndexReal(options) {
|
|
|
258
279
|
warnOnUnmigratedVaults(stashDir);
|
|
259
280
|
// Ensure git stash caches are extracted before resolving stash dirs,
|
|
260
281
|
// so their content directories exist on disk for the walker to discover.
|
|
282
|
+
const sourceCacheStart = Date.now();
|
|
283
|
+
onProgress({ phase: "preflight", message: "Hydrating source caches." });
|
|
261
284
|
const { ensureSourceCaches, resolveSourceEntries } = await import("./search/search-source.js");
|
|
262
285
|
await ensureSourceCaches(config, { force: full });
|
|
286
|
+
const sourceCacheEnd = Date.now();
|
|
263
287
|
const allSourceEntries = resolveSourceEntries(stashDir, config);
|
|
264
288
|
const allSourceDirs = allSourceEntries.map((s) => s.path);
|
|
289
|
+
onProgress({
|
|
290
|
+
phase: "preflight",
|
|
291
|
+
message: `Resolved ${allSourceDirs.length} stash source${allSourceDirs.length === 1 ? "" : "s"}.`,
|
|
292
|
+
});
|
|
265
293
|
const t0 = Date.now();
|
|
266
294
|
// Open database — pass embedding dimension from config if available
|
|
267
295
|
const dbPath = getDbPath();
|
|
@@ -291,6 +319,8 @@ async function akmIndexReal(options) {
|
|
|
291
319
|
tLlmEnd: t0,
|
|
292
320
|
tFtsEnd: t0,
|
|
293
321
|
tEmbedEnd: t0,
|
|
322
|
+
tFinalizeStart: t0,
|
|
323
|
+
tFinalizeEnd: t0,
|
|
294
324
|
},
|
|
295
325
|
isIncremental,
|
|
296
326
|
builtAtMs,
|
|
@@ -328,9 +358,15 @@ async function akmIndexReal(options) {
|
|
|
328
358
|
// After the normal index completes, remove entries whose source files no
|
|
329
359
|
// longer exist on disk. Remote entries (empty file_path) are skipped.
|
|
330
360
|
let cleanResult;
|
|
361
|
+
const cleanStart = Date.now();
|
|
331
362
|
if (clean) {
|
|
363
|
+
onProgress({
|
|
364
|
+
phase: "finalize",
|
|
365
|
+
message: dryRun ? "Scanning for stale index entries (dry run)." : "Removing stale index entries.",
|
|
366
|
+
});
|
|
332
367
|
cleanResult = runCleanPass(db, dryRun);
|
|
333
368
|
}
|
|
369
|
+
const cleanEnd = Date.now();
|
|
334
370
|
// ────────────────────────────────────────────────────────────────────────
|
|
335
371
|
return {
|
|
336
372
|
stashDir,
|
|
@@ -348,6 +384,12 @@ async function akmIndexReal(options) {
|
|
|
348
384
|
llmMs: timing.tLlmEnd - timing.tWalkEnd,
|
|
349
385
|
embedMs: timing.tEmbedEnd - timing.tLlmEnd,
|
|
350
386
|
ftsMs: timing.tFtsEnd - timing.tEmbedEnd,
|
|
387
|
+
finalizeMs: timing.tFinalizeEnd - timing.tFinalizeStart,
|
|
388
|
+
cleanMs: clean ? cleanEnd - cleanStart : 0,
|
|
389
|
+
preflightMs: timing.t0 - requestedAt,
|
|
390
|
+
leaseWaitMs: acquiredAt - requestedAt,
|
|
391
|
+
sourceCacheMs: sourceCacheEnd - sourceCacheStart,
|
|
392
|
+
endToEndMs: Date.now() - requestedAt,
|
|
351
393
|
},
|
|
352
394
|
...(cleanResult !== undefined ? { clean: cleanResult } : {}),
|
|
353
395
|
};
|
|
@@ -854,31 +896,44 @@ async function generateEmbeddingsForDb(db, config, onProgress, signal) {
|
|
|
854
896
|
warnVerbose(`[embed] ${ref} (${chars} chars, est. ${tokens} tokens) → batch ${batchNum}/${totalBatches}`);
|
|
855
897
|
}
|
|
856
898
|
}
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
899
|
+
let heartbeatTimer;
|
|
900
|
+
try {
|
|
901
|
+
heartbeatTimer = setInterval(() => {
|
|
902
|
+
onProgress({
|
|
903
|
+
phase: "embeddings",
|
|
904
|
+
message: `Still generating embeddings for ${allEntries.length} entr${allEntries.length === 1 ? "y" : "ies"}; waiting on embedding provider.`,
|
|
905
|
+
});
|
|
906
|
+
}, 15000);
|
|
907
|
+
const embeddings = await embedBatch(texts, config.embedding, signal);
|
|
908
|
+
throwIfAborted(signal);
|
|
909
|
+
// Wrap all embedding upserts in a single transaction so partial
|
|
910
|
+
// state is rolled back on failure rather than leaving the table half-filled.
|
|
911
|
+
let storedCount = 0;
|
|
912
|
+
let skippedCount = 0;
|
|
913
|
+
db.transaction(() => {
|
|
914
|
+
for (let i = 0; i < allEntries.length; i++) {
|
|
915
|
+
if (upsertEmbedding(db, allEntries[i].id, embeddings[i])) {
|
|
916
|
+
storedCount++;
|
|
917
|
+
}
|
|
918
|
+
else {
|
|
919
|
+
skippedCount++;
|
|
920
|
+
}
|
|
870
921
|
}
|
|
922
|
+
})();
|
|
923
|
+
if (skippedCount > 0) {
|
|
924
|
+
warn(`[embed] ${skippedCount} embedding${skippedCount === 1 ? "" : "s"} skipped (entry deleted between queue and write)`);
|
|
871
925
|
}
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
926
|
+
onProgress({
|
|
927
|
+
phase: "embeddings",
|
|
928
|
+
message: `Stored ${storedCount} embedding${storedCount === 1 ? "" : "s"}.`,
|
|
929
|
+
});
|
|
930
|
+
setMeta(db, "embeddingFingerprint", currentFingerprint);
|
|
931
|
+
return { success: true };
|
|
932
|
+
}
|
|
933
|
+
finally {
|
|
934
|
+
if (heartbeatTimer)
|
|
935
|
+
clearInterval(heartbeatTimer);
|
|
875
936
|
}
|
|
876
|
-
onProgress({
|
|
877
|
-
phase: "embeddings",
|
|
878
|
-
message: `Stored ${storedCount} embedding${storedCount === 1 ? "" : "s"}.`,
|
|
879
|
-
});
|
|
880
|
-
setMeta(db, "embeddingFingerprint", currentFingerprint);
|
|
881
|
-
return { success: true };
|
|
882
937
|
}
|
|
883
938
|
catch (error) {
|
|
884
939
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
2
|
+
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
3
|
+
// file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
4
|
+
/**
|
|
5
|
+
* Pure FTS5 query-string helpers, extracted from indexer/db/db.ts.
|
|
6
|
+
*
|
|
7
|
+
* These transform a raw user query into an FTS5-safe MATCH expression. They
|
|
8
|
+
* touch no database state, so they are unit-testable with zero DB setup.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Sanitize a raw user query into an FTS5-safe implicit-AND expression.
|
|
12
|
+
*
|
|
13
|
+
* Allows only characters safe in FTS5 queries: letters, digits, underscores,
|
|
14
|
+
* and whitespace. Everything else (hyphens, dots, quotes, parens, asterisks,
|
|
15
|
+
* colons, carets, @, !, etc.) is replaced with a space so that compound
|
|
16
|
+
* identifiers like "code-review" or "k8s.setup" become AND-joined tokens
|
|
17
|
+
* ("code review", "k8s setup") rather than triggering FTS5 syntax errors.
|
|
18
|
+
*/
|
|
19
|
+
export function sanitizeFtsQuery(query) {
|
|
20
|
+
let sanitized = query.replace(/[^a-zA-Z0-9_\s]/g, " ");
|
|
21
|
+
// Neutralize the NEAR operator (FTS5 proximity syntax)
|
|
22
|
+
sanitized = sanitized.replace(/\bNEAR\b/g, " ");
|
|
23
|
+
const tokens = sanitized.split(/\s+/).filter((t) => t.length >= 1);
|
|
24
|
+
if (tokens.length === 0)
|
|
25
|
+
return "";
|
|
26
|
+
// Use implicit AND (space-separated tokens) for precision. FTS5 treats
|
|
27
|
+
// space-separated tokens as an implicit AND, matching only rows that
|
|
28
|
+
// contain ALL terms.
|
|
29
|
+
return tokens.join(" ");
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Build a prefix query from an FTS5 query string by appending `*` to each
|
|
33
|
+
* token that is 3+ characters long. Tokens shorter than 3 characters are
|
|
34
|
+
* kept as-is (no prefix expansion) to avoid overly broad matches.
|
|
35
|
+
*
|
|
36
|
+
* Returns null if no tokens qualify for prefix expansion.
|
|
37
|
+
*/
|
|
38
|
+
export function buildPrefixQuery(ftsQuery) {
|
|
39
|
+
const tokens = ftsQuery.split(/\s+/).filter(Boolean);
|
|
40
|
+
let hasPrefix = false;
|
|
41
|
+
const prefixTokens = tokens.map((t) => {
|
|
42
|
+
if (t.length >= 3) {
|
|
43
|
+
hasPrefix = true;
|
|
44
|
+
return `${t}*`;
|
|
45
|
+
}
|
|
46
|
+
return t;
|
|
47
|
+
});
|
|
48
|
+
if (!hasPrefix)
|
|
49
|
+
return null;
|
|
50
|
+
return prefixTokens.join(" ");
|
|
51
|
+
}
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
import fs from "node:fs";
|
|
18
18
|
import os from "node:os";
|
|
19
19
|
import path from "node:path";
|
|
20
|
+
import { parseEmbeddedJsonResponse } from "../../core/parse.js";
|
|
20
21
|
import { spawn as runtimeSpawn } from "../../runtime.js";
|
|
21
22
|
import { getCommandBuilder } from "./builders.js";
|
|
22
23
|
import { DEFAULT_AGENT_TIMEOUT_MS } from "./config.js";
|
|
@@ -346,72 +347,20 @@ export async function runAgent(profile, prompt, options = {}) {
|
|
|
346
347
|
};
|
|
347
348
|
}
|
|
348
349
|
if (parseOutput === "json" && stdioMode === "captured") {
|
|
349
|
-
// Strip <think> blocks and code fences, then
|
|
350
|
-
//
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
// Fallback: extract the first balanced {…} from prose output.
|
|
364
|
-
let found;
|
|
365
|
-
for (let s = 0; s < cleaned.length; s++) {
|
|
366
|
-
if (cleaned[s] !== "{")
|
|
367
|
-
continue;
|
|
368
|
-
let depth = 0, inStr = false, esc = false;
|
|
369
|
-
for (let i = s; i < cleaned.length; i++) {
|
|
370
|
-
const c = cleaned[i];
|
|
371
|
-
if (inStr) {
|
|
372
|
-
if (esc) {
|
|
373
|
-
esc = false;
|
|
374
|
-
}
|
|
375
|
-
else if (c === "\\") {
|
|
376
|
-
esc = true;
|
|
377
|
-
}
|
|
378
|
-
else if (c === '"') {
|
|
379
|
-
inStr = false;
|
|
380
|
-
}
|
|
381
|
-
continue;
|
|
382
|
-
}
|
|
383
|
-
if (c === '"') {
|
|
384
|
-
inStr = true;
|
|
385
|
-
continue;
|
|
386
|
-
}
|
|
387
|
-
if (c === "{")
|
|
388
|
-
depth++;
|
|
389
|
-
if (c === "}") {
|
|
390
|
-
depth--;
|
|
391
|
-
if (depth === 0) {
|
|
392
|
-
try {
|
|
393
|
-
found = JSON.parse(cleaned.slice(s, i + 1));
|
|
394
|
-
}
|
|
395
|
-
catch { }
|
|
396
|
-
break;
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
}
|
|
400
|
-
if (found !== undefined)
|
|
401
|
-
break;
|
|
402
|
-
}
|
|
403
|
-
if (found === undefined) {
|
|
404
|
-
return {
|
|
405
|
-
ok: false,
|
|
406
|
-
exitCode,
|
|
407
|
-
stdout,
|
|
408
|
-
stderr,
|
|
409
|
-
durationMs,
|
|
410
|
-
reason: "parse_error",
|
|
411
|
-
error: "no JSON object found in agent output",
|
|
412
|
-
};
|
|
413
|
-
}
|
|
414
|
-
parsed = found;
|
|
350
|
+
// Strip <think> blocks and code fences, then parse with embedded-JSON
|
|
351
|
+
// fallback for local LLMs that emit prose around the payload. Handles
|
|
352
|
+
// both top-level `{…}` and `[…]` structures.
|
|
353
|
+
const parsed = parseEmbeddedJsonResponse(stdout);
|
|
354
|
+
if (parsed === undefined) {
|
|
355
|
+
return {
|
|
356
|
+
ok: false,
|
|
357
|
+
exitCode,
|
|
358
|
+
stdout,
|
|
359
|
+
stderr,
|
|
360
|
+
durationMs,
|
|
361
|
+
reason: "parse_error",
|
|
362
|
+
error: "no JSON structure found in agent output",
|
|
363
|
+
};
|
|
415
364
|
}
|
|
416
365
|
return { ok: true, exitCode, stdout, stderr, durationMs, parsed };
|
|
417
366
|
}
|
|
@@ -25,7 +25,9 @@ export function getCachedEmbedding(key) {
|
|
|
25
25
|
return cached;
|
|
26
26
|
}
|
|
27
27
|
export function setCachedEmbedding(key, value) {
|
|
28
|
-
//
|
|
28
|
+
// Delete first so an overwrite refreshes LRU recency AND is not counted as a
|
|
29
|
+
// new insert: only a genuinely new key at capacity should evict the oldest.
|
|
30
|
+
embedCache.delete(key);
|
|
29
31
|
if (embedCache.size >= EMBED_CACHE_MAX) {
|
|
30
32
|
const oldest = embedCache.keys().next().value;
|
|
31
33
|
if (oldest !== undefined) {
|
|
@@ -1058,6 +1058,19 @@ export function formatIndexPlain(r) {
|
|
|
1058
1058
|
if (verification?.ok === false && verification.message) {
|
|
1059
1059
|
out += `\nVerification: ${String(verification.message)}`;
|
|
1060
1060
|
}
|
|
1061
|
+
const timing = indexResult.timing;
|
|
1062
|
+
if (timing) {
|
|
1063
|
+
out +=
|
|
1064
|
+
`\nTiming: total ${timing.totalMs}ms` +
|
|
1065
|
+
`, preflight ${timing.preflightMs}ms` +
|
|
1066
|
+
`, walk ${timing.walkMs}ms` +
|
|
1067
|
+
`, llm ${timing.llmMs}ms` +
|
|
1068
|
+
`, embeddings ${timing.embedMs}ms` +
|
|
1069
|
+
`, fts ${timing.ftsMs}ms` +
|
|
1070
|
+
`, finalize ${timing.finalizeMs}ms` +
|
|
1071
|
+
`, clean ${timing.cleanMs}ms` +
|
|
1072
|
+
`, end-to-end ${timing.endToEndMs}ms`;
|
|
1073
|
+
}
|
|
1061
1074
|
return out;
|
|
1062
1075
|
}
|
|
1063
1076
|
export function formatListPlain(r) {
|
package/dist/registry/resolve.js
CHANGED
|
@@ -238,6 +238,11 @@ function tryParseLocalRef(rawRef, explicitPath) {
|
|
|
238
238
|
};
|
|
239
239
|
}
|
|
240
240
|
function isPathLikeRef(ref) {
|
|
241
|
+
// A leading `@` marks an npm scope (`@scope/pkg`), never a filesystem path —
|
|
242
|
+
// treat it as non-path so it falls through to the npm branch instead of
|
|
243
|
+
// being resolved (and rejected) as an explicit local path.
|
|
244
|
+
if (ref.startsWith("@"))
|
|
245
|
+
return false;
|
|
241
246
|
if (ref === "." || ref === "..")
|
|
242
247
|
return true;
|
|
243
248
|
if (path.isAbsolute(ref))
|