claude-mem-lite 3.96.0 → 3.97.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/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/adopt-cli.mjs +125 -51
- package/bash-utils.mjs +105 -29
- package/claudemd.mjs +63 -19
- package/cli/activity.mjs +36 -18
- package/cli/common.mjs +101 -18
- package/cli/doctor.mjs +29 -9
- package/cli.mjs +69 -10
- package/deep-search.mjs +63 -20
- package/format-utils.mjs +15 -7
- package/haiku-client.mjs +189 -111
- package/hash-utils.mjs +15 -6
- package/hook-context.mjs +135 -46
- package/hook-episode.mjs +76 -23
- package/hook-handoff.mjs +216 -84
- package/hook-llm.mjs +476 -216
- package/hook-memory.mjs +106 -43
- package/hook-optimize.mjs +272 -109
- package/hook-precompact.mjs +1 -1
- package/hook-semaphore.mjs +46 -12
- package/hook-shared.mjs +101 -131
- package/hook-update.mjs +286 -88
- package/hook.mjs +1207 -617
- package/install-metadata.mjs +246 -125
- package/install.mjs +1327 -917
- package/lib/activity.mjs +52 -29
- package/lib/atomic-write.mjs +23 -4
- package/lib/binding-probe.mjs +40 -38
- package/lib/browse-core.mjs +22 -8
- package/lib/citation-tracker.mjs +189 -79
- package/lib/cite-back-hint.mjs +37 -19
- package/lib/cite-recall-path.mjs +3 -1
- package/lib/cli-flags.mjs +4 -2
- package/lib/cli-project.mjs +8 -5
- package/lib/compress-core.mjs +34 -9
- package/lib/cooldown-path.mjs +3 -1
- package/lib/db-backup.mjs +33 -11
- package/lib/deferred-work.mjs +73 -32
- package/lib/delete-core.mjs +25 -9
- package/lib/doctor-benchmark.mjs +5 -1
- package/lib/doctor-drift.mjs +25 -8
- package/lib/edge-attribution.mjs +23 -9
- package/lib/env-number.mjs +7 -8
- package/lib/err-sampler.mjs +22 -11
- package/lib/error-recall-core.mjs +15 -17
- package/lib/events-injection.mjs +25 -8
- package/lib/export-columns.mjs +49 -10
- package/lib/fast-summary.mjs +22 -9
- package/lib/file-edge-match.mjs +4 -2
- package/lib/file-intel.mjs +38 -13
- package/lib/frontmatter.mjs +22 -5
- package/lib/get-core.mjs +78 -11
- package/lib/handoff-constants.mjs +18 -0
- package/lib/hook-stdin.mjs +10 -3
- package/lib/hook-stdout.mjs +21 -7
- package/lib/hook-telemetry.mjs +44 -17
- package/lib/id-routing.mjs +11 -6
- package/lib/import-jsonl.mjs +92 -41
- package/lib/inject-search-core.mjs +5 -2
- package/lib/injected-ids.mjs +19 -12
- package/lib/install-shape.mjs +47 -23
- package/lib/keyctx-marker.mjs +3 -1
- package/lib/lesson-bridge.mjs +8 -5
- package/lib/llm-call.mjs +76 -0
- package/lib/llm-provider-probe.mjs +24 -12
- package/lib/low-signal-patterns.mjs +25 -22
- package/lib/maintain-core.mjs +221 -92
- package/lib/mem-override.mjs +4 -2
- package/lib/metrics.mjs +30 -25
- package/lib/native-binding-hint.mjs +29 -8
- package/lib/observation-write.mjs +74 -19
- package/lib/patha-exclude-meter.mjs +10 -6
- package/lib/persist-reminder.mjs +22 -10
- package/lib/plan-reader.mjs +2 -2
- package/lib/private-strip.mjs +3 -3
- package/lib/proc-lock.mjs +15 -3
- package/lib/proxy-fetch.mjs +49 -13
- package/lib/quiet-scope.mjs +42 -0
- package/lib/recall-core.mjs +19 -7
- package/lib/recent-core.mjs +18 -5
- package/lib/registry-core.mjs +12 -6
- package/lib/release-digest.mjs +4 -1
- package/lib/relevance-floor.mjs +5 -2
- package/lib/reread-guard.mjs +16 -8
- package/lib/resolve-data-dir.mjs +1 -1
- package/lib/rrf.mjs +4 -1
- package/lib/save-enrich.mjs +36 -18
- package/lib/save-observation.mjs +78 -35
- package/lib/scrub-record.mjs +20 -6
- package/lib/search-core.mjs +456 -123
- package/lib/shard-gc.mjs +50 -0
- package/lib/startup-dashboard.mjs +20 -9
- package/lib/stats-core.mjs +101 -35
- package/lib/stats-quality.mjs +56 -20
- package/lib/summary-extractor.mjs +2 -2
- package/lib/task-imperative.mjs +12 -4
- package/lib/timeline-core.mjs +88 -28
- package/lib/tmp-fixture-sweep.mjs +32 -9
- package/lib/transcript-scan.mjs +20 -4
- package/lib/upgrade-banner.mjs +11 -7
- package/mem-cli.mjs +1314 -506
- package/memdir.mjs +53 -21
- package/nlp.mjs +68 -38
- package/npm-shrinkwrap.json +2 -2
- package/package.json +9 -2
- package/plugin-cache-guard.mjs +31 -15
- package/project-utils.mjs +44 -20
- package/registry-enricher.mjs +26 -6
- package/registry-github.mjs +4 -2
- package/registry-importer.mjs +151 -40
- package/registry-recommend.mjs +189 -96
- package/registry-retriever.mjs +229 -70
- package/registry-scanner.mjs +11 -6
- package/registry.mjs +163 -66
- package/rerank.mjs +18 -4
- package/resource-discovery.mjs +14 -4
- package/schema.mjs +258 -99
- package/scoring-sql.mjs +21 -10
- package/scripts/binding-probe-cli.mjs +17 -12
- package/scripts/hook-launcher.mjs +148 -31
- package/scripts/launch-preflight.mjs +5 -7
- package/scripts/launch.mjs +21 -9
- package/scripts/post-tool-recall.js +21 -5
- package/scripts/pre-agent-inject.js +51 -13
- package/scripts/pre-skill-bridge.js +22 -7
- package/scripts/pre-tool-recall.js +157 -77
- package/scripts/prompt-search-utils.mjs +70 -25
- package/scripts/setup.sh +16 -1
- package/scripts/user-prompt-search.js +238 -97
- package/search-engine.mjs +329 -96
- package/search-scoring.mjs +75 -33
- package/secret-scrub.mjs +48 -12
- package/server/fts-check.mjs +12 -9
- package/server.mjs +674 -243
- package/skip-tools.mjs +19 -8
- package/source-files.mjs +54 -21
- package/stop-words.mjs +152 -19
- package/synonyms.mjs +579 -164
- package/tfidf.mjs +130 -40
- package/tier.mjs +1 -1
- package/tool-schemas.mjs +381 -131
- package/utils.mjs +184 -36
package/registry.mjs
CHANGED
|
@@ -136,8 +136,14 @@ const INVOCATIONS_SCHEMA = `
|
|
|
136
136
|
// Canonical indexed-column list for resources_fts, in FTS5_SCHEMA order (see the BM25
|
|
137
137
|
// weight note above — order is load-bearing). Drift from this list triggers a rebuild.
|
|
138
138
|
const FTS5_COLUMNS = [
|
|
139
|
-
'trigger_patterns',
|
|
140
|
-
'
|
|
139
|
+
'trigger_patterns',
|
|
140
|
+
'keywords',
|
|
141
|
+
'capability_summary',
|
|
142
|
+
'intent_tags',
|
|
143
|
+
'use_cases',
|
|
144
|
+
'domain_tags',
|
|
145
|
+
'tech_stack',
|
|
146
|
+
'name',
|
|
141
147
|
];
|
|
142
148
|
|
|
143
149
|
const PREINSTALLED_SCHEMA = `
|
|
@@ -185,7 +191,7 @@ function assertRegistryNotNewer(db) {
|
|
|
185
191
|
if (row && typeof row.version === 'number' && row.version > REGISTRY_SCHEMA_VERSION) {
|
|
186
192
|
throw new Error(
|
|
187
193
|
`Registry DB schema is v${row.version} but this claude-mem-lite binary supports up to v${REGISTRY_SCHEMA_VERSION}. ` +
|
|
188
|
-
|
|
194
|
+
`A newer version wrote this DB; upgrade claude-mem-lite (npm i -g claude-mem-lite@latest) or point CLAUDE_MEM_DIR to a fresh directory.`,
|
|
189
195
|
);
|
|
190
196
|
}
|
|
191
197
|
}
|
|
@@ -209,11 +215,17 @@ function ensureResourcesFts(db) {
|
|
|
209
215
|
let recreated = false;
|
|
210
216
|
if (ftsRow) {
|
|
211
217
|
let existingCols = [];
|
|
212
|
-
try {
|
|
213
|
-
|
|
218
|
+
try {
|
|
219
|
+
existingCols = db
|
|
220
|
+
.prepare(`PRAGMA table_info(resources_fts)`)
|
|
221
|
+
.all()
|
|
222
|
+
.map((c) => c.name);
|
|
223
|
+
} catch {
|
|
224
|
+
/* unreadable → treat as drifted, recreate */
|
|
225
|
+
}
|
|
214
226
|
// Element-wise so additions, removals AND reordering all count as drift.
|
|
215
|
-
const drifted =
|
|
216
|
-
|| FTS5_COLUMNS.some((c, i) => existingCols[i] !== c);
|
|
227
|
+
const drifted =
|
|
228
|
+
existingCols.length !== FTS5_COLUMNS.length || FTS5_COLUMNS.some((c, i) => existingCols[i] !== c);
|
|
217
229
|
if (drifted) {
|
|
218
230
|
db.exec(`DROP TRIGGER IF EXISTS res_fts_insert`);
|
|
219
231
|
db.exec(`DROP TRIGGER IF EXISTS res_fts_update`);
|
|
@@ -249,32 +261,56 @@ export function ensureRegistryDb(dbPath) {
|
|
|
249
261
|
|
|
250
262
|
// Before ANY DDL: refuse a registry written by a newer client (see assertRegistryNotNewer).
|
|
251
263
|
// Close the handle on the way out so the caller isn't left leaking an open file.
|
|
252
|
-
try {
|
|
264
|
+
try {
|
|
265
|
+
assertRegistryNotNewer(db);
|
|
266
|
+
} catch (e) {
|
|
267
|
+
db.close();
|
|
268
|
+
throw e;
|
|
269
|
+
}
|
|
253
270
|
|
|
254
271
|
db.exec(RESOURCES_SCHEMA);
|
|
255
272
|
|
|
256
273
|
// Migrate: add missing columns to resources (single PRAGMA call for all)
|
|
257
274
|
try {
|
|
258
|
-
const resCols = new Set(
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
275
|
+
const resCols = new Set(
|
|
276
|
+
db
|
|
277
|
+
.prepare('PRAGMA table_info(resources)')
|
|
278
|
+
.all()
|
|
279
|
+
.map((c) => c.name),
|
|
280
|
+
);
|
|
281
|
+
if (!resCols.has('invocation_name'))
|
|
282
|
+
db.exec("ALTER TABLE resources ADD COLUMN invocation_name TEXT DEFAULT ''");
|
|
283
|
+
if (!resCols.has('silenced_until')) db.exec('ALTER TABLE resources ADD COLUMN silenced_until TEXT');
|
|
284
|
+
if (!resCols.has('cooldown_hours'))
|
|
285
|
+
db.exec('ALTER TABLE resources ADD COLUMN cooldown_hours INTEGER DEFAULT 0');
|
|
262
286
|
// recommendation_mode: 'proactive' (default, actively recommended), 'on_request' (only when explicitly asked)
|
|
263
|
-
if (!resCols.has('recommendation_mode'))
|
|
287
|
+
if (!resCols.has('recommendation_mode'))
|
|
288
|
+
db.exec("ALTER TABLE resources ADD COLUMN recommendation_mode TEXT DEFAULT 'proactive'");
|
|
264
289
|
// weighted_adopt_sum: continuous adoption score accumulator (vs binary adopt_count)
|
|
265
|
-
if (!resCols.has('weighted_adopt_sum'))
|
|
290
|
+
if (!resCols.has('weighted_adopt_sum'))
|
|
291
|
+
db.exec('ALTER TABLE resources ADD COLUMN weighted_adopt_sum REAL DEFAULT 0');
|
|
266
292
|
// Phase 2: Registry optimization columns
|
|
267
|
-
if (!resCols.has('category')) db.exec(
|
|
268
|
-
if (!resCols.has('quality_tier'))
|
|
269
|
-
|
|
270
|
-
if (!resCols.has('
|
|
271
|
-
|
|
272
|
-
if (!resCols.has('
|
|
273
|
-
|
|
274
|
-
if (!resCols.has('
|
|
293
|
+
if (!resCols.has('category')) db.exec('ALTER TABLE resources ADD COLUMN category TEXT');
|
|
294
|
+
if (!resCols.has('quality_tier'))
|
|
295
|
+
db.exec("ALTER TABLE resources ADD COLUMN quality_tier TEXT DEFAULT 'community'");
|
|
296
|
+
if (!resCols.has('popularity_score'))
|
|
297
|
+
db.exec('ALTER TABLE resources ADD COLUMN popularity_score REAL DEFAULT 0');
|
|
298
|
+
if (!resCols.has('personal_score'))
|
|
299
|
+
db.exec('ALTER TABLE resources ADD COLUMN personal_score REAL DEFAULT 0');
|
|
300
|
+
if (!resCols.has('enrichment_status'))
|
|
301
|
+
db.exec('ALTER TABLE resources ADD COLUMN enrichment_status TEXT DEFAULT NULL');
|
|
302
|
+
if (!resCols.has('enriched_at'))
|
|
303
|
+
db.exec('ALTER TABLE resources ADD COLUMN enriched_at INTEGER DEFAULT NULL');
|
|
304
|
+
if (!resCols.has('repo_updated_at'))
|
|
305
|
+
db.exec('ALTER TABLE resources ADD COLUMN repo_updated_at TEXT DEFAULT NULL');
|
|
306
|
+
if (!resCols.has('repo_forks')) db.exec('ALTER TABLE resources ADD COLUMN repo_forks INTEGER DEFAULT 0');
|
|
275
307
|
// Auto-set quality_tier for installed preinstalled resources
|
|
276
|
-
db.exec(
|
|
277
|
-
|
|
308
|
+
db.exec(
|
|
309
|
+
"UPDATE resources SET quality_tier = 'installed' WHERE source = 'preinstalled' AND quality_tier = 'community'",
|
|
310
|
+
);
|
|
311
|
+
} catch (e) {
|
|
312
|
+
debugCatch(e, 'resources-column-migration');
|
|
313
|
+
}
|
|
278
314
|
|
|
279
315
|
// Migrate: add 'github' to source CHECK constraint (required for smart import)
|
|
280
316
|
// Must disable FK checks during table recreation (RENAME triggers FK validation).
|
|
@@ -286,13 +322,17 @@ export function ensureRegistryDb(dbPath) {
|
|
|
286
322
|
// the original name, which the freshly-created `resources` table then satisfies.
|
|
287
323
|
let resourcesRebuilt = false;
|
|
288
324
|
try {
|
|
289
|
-
const resSchema = db
|
|
325
|
+
const resSchema = db
|
|
326
|
+
.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='resources'`)
|
|
327
|
+
.get();
|
|
290
328
|
if (resSchema?.sql && !resSchema.sql.includes("'github'")) {
|
|
291
329
|
db.pragma('foreign_keys = OFF');
|
|
292
330
|
db.pragma('legacy_alter_table = ON');
|
|
293
331
|
try {
|
|
294
332
|
db.transaction(() => {
|
|
295
|
-
const hasOld = db
|
|
333
|
+
const hasOld = db
|
|
334
|
+
.prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name='resources_old'`)
|
|
335
|
+
.get();
|
|
296
336
|
if (hasOld) db.exec(`DROP TABLE resources_old`);
|
|
297
337
|
// Drop FTS triggers first (reference resources table)
|
|
298
338
|
db.exec(`DROP TRIGGER IF EXISTS res_fts_insert`);
|
|
@@ -301,9 +341,17 @@ export function ensureRegistryDb(dbPath) {
|
|
|
301
341
|
db.exec(`ALTER TABLE resources RENAME TO resources_old`);
|
|
302
342
|
db.exec(RESOURCES_SCHEMA);
|
|
303
343
|
// Copy all existing data
|
|
304
|
-
const cols = db
|
|
305
|
-
|
|
306
|
-
|
|
344
|
+
const cols = db
|
|
345
|
+
.prepare('PRAGMA table_info(resources_old)')
|
|
346
|
+
.all()
|
|
347
|
+
.map((c) => c.name);
|
|
348
|
+
const newCols = new Set(
|
|
349
|
+
db
|
|
350
|
+
.prepare('PRAGMA table_info(resources)')
|
|
351
|
+
.all()
|
|
352
|
+
.map((c) => c.name),
|
|
353
|
+
);
|
|
354
|
+
const common = cols.filter((c) => newCols.has(c)).join(', ');
|
|
307
355
|
db.exec(`INSERT INTO resources (${common}) SELECT ${common} FROM resources_old`);
|
|
308
356
|
db.exec(`DROP TABLE resources_old`);
|
|
309
357
|
// Recreate the table's indexes: the CREATE INDEX IF NOT EXISTS inside
|
|
@@ -319,7 +367,9 @@ export function ensureRegistryDb(dbPath) {
|
|
|
319
367
|
}
|
|
320
368
|
resourcesRebuilt = true;
|
|
321
369
|
}
|
|
322
|
-
} catch (e) {
|
|
370
|
+
} catch (e) {
|
|
371
|
+
debugCatch(e, 'resources-source-check-migration');
|
|
372
|
+
}
|
|
323
373
|
|
|
324
374
|
// FTS5: create if absent, or drop+recreate if the indexed columns have drifted from
|
|
325
375
|
// FTS5_COLUMNS (a stale narrow index silently kills every resources write — audit P2-6).
|
|
@@ -336,8 +386,11 @@ export function ensureRegistryDb(dbPath) {
|
|
|
336
386
|
// repopulation for the opposite reason: it starts EMPTY, so rows written before the
|
|
337
387
|
// drift would be unreachable through search forever.
|
|
338
388
|
if (resourcesRebuilt || ftsRecreated) {
|
|
339
|
-
try {
|
|
340
|
-
|
|
389
|
+
try {
|
|
390
|
+
db.exec("INSERT INTO resources_fts(resources_fts) VALUES('rebuild')");
|
|
391
|
+
} catch (e) {
|
|
392
|
+
debugCatch(e, 'resources-fts-rebuild-after-source-check');
|
|
393
|
+
}
|
|
341
394
|
}
|
|
342
395
|
|
|
343
396
|
db.exec(INVOCATIONS_SCHEMA);
|
|
@@ -345,30 +398,40 @@ export function ensureRegistryDb(dbPath) {
|
|
|
345
398
|
// Migrate invocations CHECK constraint: add 'user_prompt' trigger value
|
|
346
399
|
// SQLite cannot ALTER CHECK constraints, so recreate table if needed
|
|
347
400
|
try {
|
|
348
|
-
const schema = db
|
|
401
|
+
const schema = db
|
|
402
|
+
.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='invocations'`)
|
|
403
|
+
.get();
|
|
349
404
|
if (schema?.sql && !schema.sql.includes('user_prompt')) {
|
|
350
405
|
db.transaction(() => {
|
|
351
406
|
// Clean up leftover from previous failed migration attempt
|
|
352
|
-
const hasOld = db
|
|
407
|
+
const hasOld = db
|
|
408
|
+
.prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name='invocations_old'`)
|
|
409
|
+
.get();
|
|
353
410
|
if (hasOld) db.exec(`DROP TABLE invocations_old`);
|
|
354
411
|
db.exec(`ALTER TABLE invocations RENAME TO invocations_old`);
|
|
355
412
|
db.exec(INVOCATIONS_SCHEMA);
|
|
356
413
|
// Omit rejection_reason — column may not exist yet on old DBs; ADD COLUMN migration below handles it
|
|
357
|
-
|
|
414
|
+
db.exec(`INSERT INTO invocations
|
|
358
415
|
(id, resource_id, session_id, trigger, tier, recommended, adopted, outcome, score, created_at)
|
|
359
416
|
SELECT id, resource_id, session_id, trigger, tier, recommended, adopted, outcome, score, created_at
|
|
360
417
|
FROM invocations_old`);
|
|
361
418
|
db.exec(`DROP TABLE invocations_old`);
|
|
362
419
|
})();
|
|
363
420
|
}
|
|
364
|
-
} catch (e) {
|
|
421
|
+
} catch (e) {
|
|
422
|
+
debugCatch(e, 'ensureRegistryDb-migration');
|
|
423
|
+
}
|
|
365
424
|
|
|
366
425
|
// Migrate invocations CHECK constraint: add 'ignored' outcome value
|
|
367
426
|
try {
|
|
368
|
-
const schema = db
|
|
427
|
+
const schema = db
|
|
428
|
+
.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='invocations'`)
|
|
429
|
+
.get();
|
|
369
430
|
if (schema?.sql && !schema.sql.includes("'ignored'")) {
|
|
370
431
|
db.transaction(() => {
|
|
371
|
-
const hasOld = db
|
|
432
|
+
const hasOld = db
|
|
433
|
+
.prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name='invocations_old'`)
|
|
434
|
+
.get();
|
|
372
435
|
if (hasOld) db.exec(`DROP TABLE invocations_old`);
|
|
373
436
|
db.exec(`ALTER TABLE invocations RENAME TO invocations_old`);
|
|
374
437
|
db.exec(INVOCATIONS_SCHEMA);
|
|
@@ -380,15 +443,19 @@ export function ensureRegistryDb(dbPath) {
|
|
|
380
443
|
db.exec(`DROP TABLE invocations_old`);
|
|
381
444
|
})();
|
|
382
445
|
}
|
|
383
|
-
} catch (e) {
|
|
446
|
+
} catch (e) {
|
|
447
|
+
debugCatch(e, 'ensureRegistryDb-ignored-migration');
|
|
448
|
+
}
|
|
384
449
|
|
|
385
450
|
// Migrate: add rejection_reason column if missing
|
|
386
451
|
try {
|
|
387
|
-
const cols = db.prepare(
|
|
388
|
-
if (!cols.some(c => c.name === 'rejection_reason')) {
|
|
389
|
-
db.exec(
|
|
452
|
+
const cols = db.prepare('PRAGMA table_info(invocations)').all();
|
|
453
|
+
if (!cols.some((c) => c.name === 'rejection_reason')) {
|
|
454
|
+
db.exec('ALTER TABLE invocations ADD COLUMN rejection_reason TEXT');
|
|
390
455
|
}
|
|
391
|
-
} catch (e) {
|
|
456
|
+
} catch (e) {
|
|
457
|
+
debugCatch(e, 'rejection_reason-migration');
|
|
458
|
+
}
|
|
392
459
|
|
|
393
460
|
// Migrate: add ON DELETE CASCADE to invocations.resource_id (audit P0 #4). Old DBs
|
|
394
461
|
// declared the FK with no ON DELETE action, so deleting a resource that had
|
|
@@ -398,10 +465,14 @@ export function ensureRegistryDb(dbPath) {
|
|
|
398
465
|
// invocations), so legacy_alter_table is not a concern here. Runs after the
|
|
399
466
|
// rejection_reason ADD COLUMN so the column exists in both old and new tables.
|
|
400
467
|
try {
|
|
401
|
-
const schema = db
|
|
468
|
+
const schema = db
|
|
469
|
+
.prepare(`SELECT sql FROM sqlite_master WHERE type='table' AND name='invocations'`)
|
|
470
|
+
.get();
|
|
402
471
|
if (schema?.sql && !/ON DELETE CASCADE/i.test(schema.sql)) {
|
|
403
472
|
db.transaction(() => {
|
|
404
|
-
const hasOld = db
|
|
473
|
+
const hasOld = db
|
|
474
|
+
.prepare(`SELECT 1 FROM sqlite_master WHERE type='table' AND name='invocations_old'`)
|
|
475
|
+
.get();
|
|
405
476
|
if (hasOld) db.exec(`DROP TABLE invocations_old`);
|
|
406
477
|
db.exec(`ALTER TABLE invocations RENAME TO invocations_old`);
|
|
407
478
|
db.exec(INVOCATIONS_SCHEMA);
|
|
@@ -421,7 +492,9 @@ export function ensureRegistryDb(dbPath) {
|
|
|
421
492
|
db.exec(INVOCATIONS_SCHEMA);
|
|
422
493
|
})();
|
|
423
494
|
}
|
|
424
|
-
} catch (e) {
|
|
495
|
+
} catch (e) {
|
|
496
|
+
debugCatch(e, 'invocations-ondelete-cascade-migration');
|
|
497
|
+
}
|
|
425
498
|
|
|
426
499
|
// (Removed the separate idx_invocations_resource_created migration — it was a column-
|
|
427
500
|
// identical duplicate of idx_inv_resource (resource_id, created_at) in INVOCATIONS_SCHEMA.
|
|
@@ -501,14 +574,28 @@ const UPSERT_SQL = `
|
|
|
501
574
|
export function upsertResource(db, r) {
|
|
502
575
|
return db.transaction(() => {
|
|
503
576
|
db.prepare(UPSERT_SQL).run(
|
|
504
|
-
r.name,
|
|
505
|
-
r.
|
|
506
|
-
r.
|
|
507
|
-
r.
|
|
508
|
-
r.
|
|
509
|
-
r.
|
|
510
|
-
r.
|
|
511
|
-
r.
|
|
577
|
+
r.name,
|
|
578
|
+
r.type,
|
|
579
|
+
r.status || 'active',
|
|
580
|
+
r.source || 'preinstalled',
|
|
581
|
+
r.repo_url || null,
|
|
582
|
+
r.repo_stars || 0,
|
|
583
|
+
r.local_path,
|
|
584
|
+
r.file_hash || null,
|
|
585
|
+
r.invocation_name || '',
|
|
586
|
+
r.intent_tags || '',
|
|
587
|
+
r.domain_tags || '',
|
|
588
|
+
r.action_type || '',
|
|
589
|
+
r.trigger_patterns || '',
|
|
590
|
+
r.capability_summary || '',
|
|
591
|
+
r.input_type || '',
|
|
592
|
+
r.output_type || '',
|
|
593
|
+
r.prerequisites || '{}',
|
|
594
|
+
r.keywords || '',
|
|
595
|
+
r.tech_stack || '',
|
|
596
|
+
r.use_cases || '',
|
|
597
|
+
r.complexity || 'intermediate',
|
|
598
|
+
r.indexed_at || null,
|
|
512
599
|
);
|
|
513
600
|
const row = db.prepare('SELECT id FROM resources WHERE type = ? AND name = ?').get(r.type, r.name);
|
|
514
601
|
return row?.id || 0;
|
|
@@ -528,16 +615,22 @@ export function upsertResource(db, r) {
|
|
|
528
615
|
*/
|
|
529
616
|
export function collectRegistryStats(rdb) {
|
|
530
617
|
const total = rdb.prepare('SELECT COUNT(*) as c FROM resources WHERE status = ?').get('active');
|
|
531
|
-
const byType = rdb
|
|
532
|
-
|
|
533
|
-
'
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
618
|
+
const byType = rdb
|
|
619
|
+
.prepare('SELECT type, COUNT(*) as c FROM resources WHERE status = ? GROUP BY type')
|
|
620
|
+
.all('active');
|
|
621
|
+
const topAdopted = rdb
|
|
622
|
+
.prepare(
|
|
623
|
+
'SELECT name, type, adopt_count, recommend_count FROM resources WHERE status = ? AND adopt_count > 0 ORDER BY adopt_count DESC LIMIT 10',
|
|
624
|
+
)
|
|
625
|
+
.all('active');
|
|
626
|
+
const zeroAdopt = rdb
|
|
627
|
+
.prepare(
|
|
628
|
+
'SELECT COUNT(*) as c FROM resources WHERE status = ? AND recommend_count > 0 AND adopt_count = 0',
|
|
629
|
+
)
|
|
630
|
+
.get('active');
|
|
631
|
+
const userAdded = rdb
|
|
632
|
+
.prepare("SELECT COUNT(*) as c FROM resources WHERE status = ? AND source = 'user'")
|
|
633
|
+
.get('active');
|
|
541
634
|
return { total: total.c, byType, topAdopted, zeroAdopt: zeroAdopt.c, userAdded: userAdded.c };
|
|
542
635
|
}
|
|
543
636
|
|
|
@@ -552,11 +645,15 @@ export function collectRegistryStats(rdb) {
|
|
|
552
645
|
export function listResourcesRanked(rdb, { type } = {}) {
|
|
553
646
|
const where = type ? 'WHERE type = ? AND status = ?' : 'WHERE status = ?';
|
|
554
647
|
const params = type ? [type, 'active'] : ['active'];
|
|
555
|
-
return rdb
|
|
648
|
+
return rdb
|
|
649
|
+
.prepare(
|
|
650
|
+
`
|
|
556
651
|
SELECT name, type, invocation_name, recommend_count, adopt_count, capability_summary
|
|
557
652
|
FROM resources ${where}
|
|
558
653
|
ORDER BY COALESCE(adopt_count, 0) DESC, COALESCE(recommend_count, 0) DESC, type, name
|
|
559
|
-
|
|
654
|
+
`,
|
|
655
|
+
)
|
|
656
|
+
.all(...params);
|
|
560
657
|
}
|
|
561
658
|
|
|
562
659
|
/** One list row, shared shape: `S name (invocation) — rec:N adopt:N — summary…` (80-char summary cap).
|
package/rerank.mjs
CHANGED
|
@@ -42,7 +42,12 @@ export function extractRanked(raw) {
|
|
|
42
42
|
if (obj && Array.isArray(obj.ranked)) return obj.ranked; // {"ranked":[...]}
|
|
43
43
|
const m = text.match(/\[\s*\d+(?:\s*,\s*\d+)*\s*\]/); // prose-wrapped [..]
|
|
44
44
|
if (m) {
|
|
45
|
-
try {
|
|
45
|
+
try {
|
|
46
|
+
const a = JSON.parse(m[0]);
|
|
47
|
+
if (Array.isArray(a)) return a;
|
|
48
|
+
} catch {
|
|
49
|
+
/* fall through */
|
|
50
|
+
}
|
|
46
51
|
}
|
|
47
52
|
return null;
|
|
48
53
|
}
|
|
@@ -50,9 +55,16 @@ export function extractRanked(raw) {
|
|
|
50
55
|
// Reorder candidate session ids per the LLM's chosen 1-based order; any failure →
|
|
51
56
|
// original order ("never worse than baseline"). { order: sid[], parsed: bool }.
|
|
52
57
|
export async function llmRerankOrder(query, cand /* [{sid,text}] */, llm) {
|
|
53
|
-
const prompt = buildRerankPrompt(
|
|
58
|
+
const prompt = buildRerankPrompt(
|
|
59
|
+
query,
|
|
60
|
+
cand.map((c) => c.text),
|
|
61
|
+
);
|
|
54
62
|
let raw;
|
|
55
|
-
try {
|
|
63
|
+
try {
|
|
64
|
+
raw = await llm(prompt);
|
|
65
|
+
} catch {
|
|
66
|
+
raw = null;
|
|
67
|
+
}
|
|
56
68
|
const order = extractRanked(raw);
|
|
57
69
|
if (!order) return { order: cand.map((c) => c.sid), parsed: false };
|
|
58
70
|
const seen = new Set();
|
|
@@ -64,7 +76,9 @@ export async function llmRerankOrder(query, cand /* [{sid,text}] */, llm) {
|
|
|
64
76
|
out.push(cand[idx].sid);
|
|
65
77
|
}
|
|
66
78
|
}
|
|
67
|
-
cand.forEach((c, i) => {
|
|
79
|
+
cand.forEach((c, i) => {
|
|
80
|
+
if (!seen.has(i)) out.push(c.sid);
|
|
81
|
+
}); // append omitted, original order
|
|
68
82
|
return { order: out, parsed: true };
|
|
69
83
|
}
|
|
70
84
|
|
package/resource-discovery.mjs
CHANGED
|
@@ -71,7 +71,11 @@ export function discoverFlat(dirPath, type, opts = {}) {
|
|
|
71
71
|
if (name.startsWith('.') || name === 'node_modules' || skipNames.has(name)) continue;
|
|
72
72
|
const fullPath = join(dirPath, name);
|
|
73
73
|
let stat;
|
|
74
|
-
try {
|
|
74
|
+
try {
|
|
75
|
+
stat = statSync(fullPath);
|
|
76
|
+
} catch {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
75
79
|
|
|
76
80
|
if (stat.isDirectory()) {
|
|
77
81
|
if (type === 'skill') {
|
|
@@ -111,7 +115,9 @@ export function discoverPlugin(pluginDir, pluginName) {
|
|
|
111
115
|
const absPath = join(agentSubDir, file);
|
|
112
116
|
try {
|
|
113
117
|
if (statSync(absPath).size === 0) continue;
|
|
114
|
-
} catch {
|
|
118
|
+
} catch {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
115
121
|
items.push({
|
|
116
122
|
type: 'agent',
|
|
117
123
|
name: `${pluginName}/${basename(file, '.md')}`,
|
|
@@ -155,7 +161,11 @@ export function discoverPlugins(dirPath) {
|
|
|
155
161
|
for (const pluginName of readdirSync(dirPath)) {
|
|
156
162
|
if (pluginName.startsWith('.') || pluginName === 'node_modules') continue;
|
|
157
163
|
const pluginDir = join(dirPath, pluginName);
|
|
158
|
-
try {
|
|
164
|
+
try {
|
|
165
|
+
if (!statSync(pluginDir).isDirectory()) continue;
|
|
166
|
+
} catch {
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
159
169
|
items.push(...discoverPlugin(pluginDir, pluginName));
|
|
160
170
|
}
|
|
161
171
|
return items;
|
|
@@ -182,7 +192,7 @@ export function discoverAllManaged(managedDir) {
|
|
|
182
192
|
* @returns {Array<DiscoveredResource & {filePath: string}>}
|
|
183
193
|
*/
|
|
184
194
|
export function withRelativePaths(items, managedDir) {
|
|
185
|
-
return items.map(item => ({
|
|
195
|
+
return items.map((item) => ({
|
|
186
196
|
...item,
|
|
187
197
|
filePath: relative(managedDir, item.absPath),
|
|
188
198
|
}));
|