@vheins/local-memory-mcp 0.19.4 → 0.19.5
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.
|
@@ -132,6 +132,7 @@ import fs3 from "fs";
|
|
|
132
132
|
import os from "os";
|
|
133
133
|
|
|
134
134
|
// src/mcp/storage/migrations.ts
|
|
135
|
+
var SCHEMA_VERSION = 2;
|
|
135
136
|
var MigrationManager = class {
|
|
136
137
|
constructor(db) {
|
|
137
138
|
this.db = db;
|
|
@@ -149,7 +150,25 @@ var MigrationManager = class {
|
|
|
149
150
|
get(sql) {
|
|
150
151
|
return this.db.prepare(sql).get();
|
|
151
152
|
}
|
|
153
|
+
getSchemaVersion() {
|
|
154
|
+
try {
|
|
155
|
+
const row = this.get("SELECT version FROM _schema_version LIMIT 1");
|
|
156
|
+
return row ? row.version : 0;
|
|
157
|
+
} catch {
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
setSchemaVersion(version) {
|
|
162
|
+
this.exec(`CREATE TABLE IF NOT EXISTS _schema_version (version INTEGER NOT NULL)`);
|
|
163
|
+
this.run("DELETE FROM _schema_version");
|
|
164
|
+
this.run("INSERT INTO _schema_version (version) VALUES (?)", version);
|
|
165
|
+
}
|
|
152
166
|
migrate() {
|
|
167
|
+
const currentVersion = this.getSchemaVersion();
|
|
168
|
+
if (currentVersion >= SCHEMA_VERSION) {
|
|
169
|
+
logger.debug(`[Migration] Schema already at version ${SCHEMA_VERSION}, skipping`);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
153
172
|
this.exec(`
|
|
154
173
|
CREATE TABLE IF NOT EXISTS memories (
|
|
155
174
|
id TEXT PRIMARY KEY,
|
|
@@ -599,6 +618,8 @@ var MigrationManager = class {
|
|
|
599
618
|
this.run("UPDATE tasks SET task_code = substr(id, 1, 8) WHERE task_code IS NULL");
|
|
600
619
|
} catch {
|
|
601
620
|
}
|
|
621
|
+
this.setSchemaVersion(SCHEMA_VERSION);
|
|
622
|
+
logger.info(`[Migration] Schema upgraded to version ${SCHEMA_VERSION}`);
|
|
602
623
|
}
|
|
603
624
|
ensureMemoryTypeConstraint() {
|
|
604
625
|
const tableSql = this.get("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'memories'");
|
|
@@ -3208,7 +3229,11 @@ var SQLiteStore = class _SQLiteStore {
|
|
|
3208
3229
|
this.db.pragma("foreign_keys = ON");
|
|
3209
3230
|
this.db.pragma("wal_autocheckpoint = 100");
|
|
3210
3231
|
if (finalPath !== ":memory:") {
|
|
3211
|
-
|
|
3232
|
+
try {
|
|
3233
|
+
this.db.pragma("wal_checkpoint(PASSIVE)");
|
|
3234
|
+
} catch (err) {
|
|
3235
|
+
logger.warn("[SQLiteStore] WAL checkpoint failed on startup", { error: String(err) });
|
|
3236
|
+
}
|
|
3212
3237
|
}
|
|
3213
3238
|
const migrator = new MigrationManager(this.db);
|
|
3214
3239
|
migrator.migrate();
|
|
@@ -3494,24 +3519,29 @@ function findPromptDir() {
|
|
|
3494
3519
|
return path4.resolve(__dirname, "./definitions");
|
|
3495
3520
|
}
|
|
3496
3521
|
var PROMPT_DIR = findPromptDir();
|
|
3522
|
+
var promptCache = /* @__PURE__ */ new Map();
|
|
3497
3523
|
function listPromptFiles() {
|
|
3498
3524
|
if (!fs4.existsSync(PROMPT_DIR)) return [];
|
|
3499
3525
|
return fs4.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();
|
|
3500
3526
|
}
|
|
3501
3527
|
function loadPromptFromMarkdown(name) {
|
|
3528
|
+
const cached = promptCache.get(name);
|
|
3529
|
+
if (cached) return cached;
|
|
3502
3530
|
const filePath = path4.join(PROMPT_DIR, `${name}.md`);
|
|
3503
3531
|
if (!fs4.existsSync(filePath)) {
|
|
3504
3532
|
throw new Error(`Prompt file not found: ${filePath}`);
|
|
3505
3533
|
}
|
|
3506
3534
|
const fileContent = fs4.readFileSync(filePath, "utf-8");
|
|
3507
3535
|
const { data, content } = matter(fileContent);
|
|
3508
|
-
|
|
3536
|
+
const result = {
|
|
3509
3537
|
name: data.name || name,
|
|
3510
3538
|
description: data.description || "",
|
|
3511
3539
|
arguments: data.arguments || [],
|
|
3512
3540
|
agent: data.agent,
|
|
3513
3541
|
content: content.trim()
|
|
3514
3542
|
};
|
|
3543
|
+
promptCache.set(name, result);
|
|
3544
|
+
return result;
|
|
3515
3545
|
}
|
|
3516
3546
|
function findServerInstructionsDir() {
|
|
3517
3547
|
const candidates = [
|
|
@@ -3533,14 +3563,17 @@ function findServerInstructionsDir() {
|
|
|
3533
3563
|
return path4.resolve(__dirname, "./server");
|
|
3534
3564
|
}
|
|
3535
3565
|
var SERVER_DIR = findServerInstructionsDir();
|
|
3566
|
+
var serverInstructionsCache = null;
|
|
3536
3567
|
function loadServerInstructions() {
|
|
3568
|
+
if (serverInstructionsCache !== null) return serverInstructionsCache;
|
|
3537
3569
|
const filePath = path4.join(SERVER_DIR, "instructions.md");
|
|
3538
3570
|
if (!fs4.existsSync(filePath)) {
|
|
3539
3571
|
throw new Error(`Server instructions file not found: ${filePath}`);
|
|
3540
3572
|
}
|
|
3541
3573
|
const fileContent = fs4.readFileSync(filePath, "utf-8");
|
|
3542
3574
|
const { content } = matter(fileContent);
|
|
3543
|
-
|
|
3575
|
+
serverInstructionsCache = content.trim();
|
|
3576
|
+
return serverInstructionsCache;
|
|
3544
3577
|
}
|
|
3545
3578
|
|
|
3546
3579
|
// src/mcp/tools/definitions/memory.ts
|
|
@@ -4255,7 +4288,10 @@ var TASK_TOOL_DEFINITIONS = [
|
|
|
4255
4288
|
properties: {
|
|
4256
4289
|
owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
|
|
4257
4290
|
repo: { type: "string", description: "Repository name" },
|
|
4258
|
-
task_code: {
|
|
4291
|
+
task_code: {
|
|
4292
|
+
type: "string",
|
|
4293
|
+
description: "Task code (e.g. PERF-1, TASK-001). Use instead of 'id' for string code lookup."
|
|
4294
|
+
},
|
|
4259
4295
|
structured: {
|
|
4260
4296
|
type: "boolean",
|
|
4261
4297
|
default: false,
|
|
@@ -4273,7 +4309,7 @@ var TASK_TOOL_DEFINITIONS = [
|
|
|
4273
4309
|
type: "array",
|
|
4274
4310
|
items: { type: "string" },
|
|
4275
4311
|
minItems: 1,
|
|
4276
|
-
description: "Array of task codes (e.g. TASK-001)"
|
|
4312
|
+
description: "Array of task codes (e.g. PERF-1, TASK-001). Use instead of 'ids' when identifying tasks by code rather than UUID."
|
|
4277
4313
|
},
|
|
4278
4314
|
structured: {
|
|
4279
4315
|
type: "boolean",
|
|
@@ -4401,7 +4437,7 @@ var TASK_TOOL_DEFINITIONS = [
|
|
|
4401
4437
|
{
|
|
4402
4438
|
name: "task-update",
|
|
4403
4439
|
title: "Task Update",
|
|
4404
|
-
description: "Update one or more tasks. Supports single update via 'id' or bulk
|
|
4440
|
+
description: "Update one or more tasks. Supports single update via 'id' (UUID) or 'task_code' (e.g. PERF-1), bulk via 'ids' (UUID array) or 'task_codes' (string array). Use 'task_code'/'task_codes' for human-readable identifiers, 'id'/'ids' for UUID lookups. Provide only the fields that need to be changed. MANDATORY WORKFLOW: Cannot move 'pending'/'blocked' \u2192 'completed' directly; MUST go through 'in_progress' first. Include 'est_tokens' when moving to 'completed'.",
|
|
4405
4441
|
annotations: {
|
|
4406
4442
|
readOnlyHint: false,
|
|
4407
4443
|
idempotentHint: false,
|
|
@@ -4482,7 +4518,7 @@ var TASK_TOOL_DEFINITIONS = [
|
|
|
4482
4518
|
ids: {
|
|
4483
4519
|
type: "array",
|
|
4484
4520
|
items: { type: "string", format: "uuid" },
|
|
4485
|
-
description: "Task
|
|
4521
|
+
description: "Task UUIDs (for bulk update). NOT task codes \u2014 use 'task_codes' for PERF-1 style identifiers."
|
|
4486
4522
|
},
|
|
4487
4523
|
phase: { type: "string" },
|
|
4488
4524
|
title: { type: "string", minLength: 3, maxLength: 100 },
|
|
@@ -4613,7 +4649,7 @@ var TASK_TOOL_DEFINITIONS = [
|
|
|
4613
4649
|
type: "array",
|
|
4614
4650
|
items: { type: "string" },
|
|
4615
4651
|
minItems: 1,
|
|
4616
|
-
description: "Array of task codes (e.g. TASK-001)"
|
|
4652
|
+
description: "Array of task codes (e.g. PERF-1, TASK-001). Use instead of 'ids' when identifying tasks by code rather than UUID."
|
|
4617
4653
|
},
|
|
4618
4654
|
phase: { type: "string" },
|
|
4619
4655
|
title: { type: "string", minLength: 3, maxLength: 100 },
|
|
@@ -4723,7 +4759,7 @@ var TASK_TOOL_DEFINITIONS = [
|
|
|
4723
4759
|
ids: {
|
|
4724
4760
|
type: "array",
|
|
4725
4761
|
items: { type: "string", format: "uuid" },
|
|
4726
|
-
description: "Task
|
|
4762
|
+
description: "Task UUIDs (for bulk deletion). NOT task codes \u2014 use 'task_codes' for PERF-1 style identifiers."
|
|
4727
4763
|
},
|
|
4728
4764
|
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
4729
4765
|
}
|
|
@@ -4734,7 +4770,10 @@ var TASK_TOOL_DEFINITIONS = [
|
|
|
4734
4770
|
properties: {
|
|
4735
4771
|
owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
|
|
4736
4772
|
repo: { type: "string", description: "Repository name" },
|
|
4737
|
-
task_code: {
|
|
4773
|
+
task_code: {
|
|
4774
|
+
type: "string",
|
|
4775
|
+
description: "Task code (e.g. PERF-1, TASK-001). Use instead of 'id' for string code lookup."
|
|
4776
|
+
},
|
|
4738
4777
|
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
4739
4778
|
}
|
|
4740
4779
|
},
|
|
@@ -4748,7 +4787,7 @@ var TASK_TOOL_DEFINITIONS = [
|
|
|
4748
4787
|
type: "array",
|
|
4749
4788
|
items: { type: "string" },
|
|
4750
4789
|
minItems: 1,
|
|
4751
|
-
description: "Array of task codes (e.g. TASK-001)"
|
|
4790
|
+
description: "Array of task codes (e.g. PERF-1, TASK-001). Use instead of 'ids' when identifying tasks by code rather than UUID."
|
|
4752
4791
|
},
|
|
4753
4792
|
structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
|
|
4754
4793
|
}
|
|
@@ -5622,7 +5661,7 @@ var AGENT_TOOL_DEFINITIONS = [
|
|
|
5622
5661
|
{
|
|
5623
5662
|
name: "decision-log",
|
|
5624
5663
|
title: "Decision Logger",
|
|
5625
|
-
description: "Logs a structured decision as a memory entry.
|
|
5664
|
+
description: "Logs a structured decision as a memory entry (type='decision', importance=4). Fields: summary (max 255 chars \u2014 becomes the memory title), context (situation/background, min 10 chars), rationale (why it was decided, min 10 chars), and alternatives (ARRAY of strings, NOT a single string \u2014 e.g. ['option A', 'option B']). Do NOT pass 'title' \u2014 that field does not exist on this tool. Owner and repo are auto-inferred from session when omitted.",
|
|
5626
5665
|
annotations: {
|
|
5627
5666
|
readOnlyHint: false,
|
|
5628
5667
|
idempotentHint: false,
|
|
@@ -6683,7 +6722,9 @@ var TaskUpdateSchema = z4.object({
|
|
|
6683
6722
|
structured: z4.boolean().default(false)
|
|
6684
6723
|
}).refine(
|
|
6685
6724
|
(data) => data.id !== void 0 || data.ids !== void 0 || data.task_code !== void 0 || data.task_codes !== void 0,
|
|
6686
|
-
{
|
|
6725
|
+
{
|
|
6726
|
+
message: "Either 'id' (UUID), 'ids' (array of UUIDs), 'task_code' (string code like PERF-1), or 'task_codes' (array of string codes) must be provided. Note: 'ids' expects UUID format, not task codes \u2014 use 'task_code'/'task_codes' for string identifiers."
|
|
6727
|
+
}
|
|
6687
6728
|
).refine((data) => Object.keys(data).length > 2, {
|
|
6688
6729
|
message: "At least one field besides repo and id/ids must be provided for update"
|
|
6689
6730
|
});
|
|
@@ -6906,10 +6947,10 @@ var AgentContextSchema = z7.object({
|
|
|
6906
6947
|
structured: z7.boolean().default(false)
|
|
6907
6948
|
});
|
|
6908
6949
|
var DecisionLogSchema = z7.object({
|
|
6909
|
-
summary: z7.string().min(3).max(255, { message: "Summary must be at most 255 characters" }),
|
|
6910
|
-
context: z7.string().min(10, { message: "Context must be at least 10 characters" }),
|
|
6911
|
-
rationale: z7.string().min(10, { message: "Rationale must be at least 10 characters" }),
|
|
6912
|
-
alternatives: z7.array(z7.string()).optional(),
|
|
6950
|
+
summary: z7.string().min(3, { message: "Summary must be at least 3 characters (this becomes the memory title)" }).max(255, { message: "Summary must be at most 255 characters (this becomes the memory title)" }),
|
|
6951
|
+
context: z7.string().min(10, { message: "Context must be at least 10 characters describing the situation" }),
|
|
6952
|
+
rationale: z7.string().min(10, { message: "Rationale must be at least 10 characters explaining why the decision was made" }),
|
|
6953
|
+
alternatives: z7.array(z7.string(), { message: "Alternatives must be an array of strings, e.g. ['option A', 'option B']" }).optional(),
|
|
6913
6954
|
tags: z7.array(z7.string()).optional(),
|
|
6914
6955
|
owner: z7.string().optional(),
|
|
6915
6956
|
repo: z7.string().optional(),
|
package/dist/dashboard/server.js
CHANGED
package/dist/mcp/server.js
CHANGED
|
@@ -61,7 +61,7 @@ import {
|
|
|
61
61
|
parseRepoInput,
|
|
62
62
|
rankCompletionValues,
|
|
63
63
|
toContextSlug
|
|
64
|
-
} from "../chunk-
|
|
64
|
+
} from "../chunk-ZSGQQS5F.js";
|
|
65
65
|
|
|
66
66
|
// src/mcp/server.ts
|
|
67
67
|
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
@@ -74,8 +74,8 @@ import path from "path";
|
|
|
74
74
|
import { fileURLToPath } from "url";
|
|
75
75
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
76
76
|
var pkgVersion = "0.1.0";
|
|
77
|
-
if ("0.19.
|
|
78
|
-
pkgVersion = "0.19.
|
|
77
|
+
if ("0.19.5") {
|
|
78
|
+
pkgVersion = "0.19.5";
|
|
79
79
|
} else {
|
|
80
80
|
let searchDir = __dirname;
|
|
81
81
|
for (let i = 0; i < 5; i++) {
|
|
@@ -154,7 +154,6 @@ function generateNextCode(owner, repo, entityType, storage, batchCodes) {
|
|
|
154
154
|
|
|
155
155
|
// src/mcp/tools/kg-archivist.ts
|
|
156
156
|
import { randomUUID } from "crypto";
|
|
157
|
-
import nlp from "compromise";
|
|
158
157
|
var MAX_CONTENT_LENGTH = 5e3;
|
|
159
158
|
var PRONOUNS = /* @__PURE__ */ new Set([
|
|
160
159
|
"i",
|
|
@@ -354,8 +353,9 @@ function isExcludedNoun(candidate) {
|
|
|
354
353
|
if (/^\d+$/.test(candidate)) return true;
|
|
355
354
|
return false;
|
|
356
355
|
}
|
|
357
|
-
function extractEntities(content) {
|
|
356
|
+
async function extractEntities(content) {
|
|
358
357
|
if (!content || content.trim().length === 0) return [];
|
|
358
|
+
const { default: nlp } = await import("compromise");
|
|
359
359
|
const text = content.length > MAX_CONTENT_LENGTH ? content.slice(0, MAX_CONTENT_LENGTH) : content;
|
|
360
360
|
const doc = nlp(text);
|
|
361
361
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -386,11 +386,11 @@ function extractEntities(content) {
|
|
|
386
386
|
}
|
|
387
387
|
return entities;
|
|
388
388
|
}
|
|
389
|
-
function saveExtractions(content, title, owner, repo, db2) {
|
|
389
|
+
async function saveExtractions(content, title, owner, repo, db2) {
|
|
390
390
|
if (!content || content.trim().length === 0) return;
|
|
391
391
|
let entities;
|
|
392
392
|
try {
|
|
393
|
-
entities = extractEntities(content);
|
|
393
|
+
entities = await extractEntities(content);
|
|
394
394
|
} catch (err) {
|
|
395
395
|
logger.warn("[KG-Archivist] Entity extraction failed, skipping", {
|
|
396
396
|
error: String(err)
|
|
@@ -507,7 +507,7 @@ async function storeSingleMemory(params, db2, vectors2) {
|
|
|
507
507
|
logger.warn("Failed to generate vector embedding", { error: String(error) });
|
|
508
508
|
}
|
|
509
509
|
try {
|
|
510
|
-
saveExtractions(entry.content, entry.title, entry.scope.owner, entry.scope.repo, db2);
|
|
510
|
+
await saveExtractions(entry.content, entry.title, entry.scope.owner, entry.scope.repo, db2);
|
|
511
511
|
} catch (error) {
|
|
512
512
|
logger.warn("[KG-Archivist] NLP extraction failed, memory stored without KG enrichment", {
|
|
513
513
|
error: String(error)
|
|
@@ -614,7 +614,7 @@ async function handleMemoryStore(params, db2, vectors2) {
|
|
|
614
614
|
logger.warn("Failed to generate vector embedding", { error: String(error) });
|
|
615
615
|
}
|
|
616
616
|
try {
|
|
617
|
-
saveExtractions(entry.content, entry.title, entry.scope.owner, entry.scope.repo, db2);
|
|
617
|
+
await saveExtractions(entry.content, entry.title, entry.scope.owner, entry.scope.repo, db2);
|
|
618
618
|
} catch (error) {
|
|
619
619
|
logger.warn("[KG-Archivist] NLP extraction failed, memory stored without KG enrichment", {
|
|
620
620
|
error: String(error)
|
|
@@ -3419,6 +3419,55 @@ async function handleKGBackfill(args, db2) {
|
|
|
3419
3419
|
};
|
|
3420
3420
|
const scanRepos = repo ? [repo] : db2.system.listRepoNavigation().map((r) => r.repo);
|
|
3421
3421
|
stats.reposScanned = scanRepos.length;
|
|
3422
|
+
const pendingOps = [];
|
|
3423
|
+
for (const r of scanRepos) {
|
|
3424
|
+
const currentOwner = owner || "unknown";
|
|
3425
|
+
if (source === "memories" || source === "both") {
|
|
3426
|
+
const rows = db2.db.prepare("SELECT title, content FROM memories WHERE repo = ?").all(r);
|
|
3427
|
+
for (let i = 0; i < rows.length; i++) {
|
|
3428
|
+
const row = rows[i];
|
|
3429
|
+
const text = `${row.content || ""} ${row.title || ""}`;
|
|
3430
|
+
try {
|
|
3431
|
+
const entities = await extractEntities(text);
|
|
3432
|
+
if (entities.length > 0) {
|
|
3433
|
+
pendingOps.push({
|
|
3434
|
+
entities,
|
|
3435
|
+
repo: r,
|
|
3436
|
+
owner: currentOwner,
|
|
3437
|
+
observationText: `Mentioned in memory: ${row.title || "untitled"}`
|
|
3438
|
+
});
|
|
3439
|
+
}
|
|
3440
|
+
} catch {
|
|
3441
|
+
stats.errors++;
|
|
3442
|
+
}
|
|
3443
|
+
stats.itemsProcessed++;
|
|
3444
|
+
if ((i + 1) % 100 === 0) {
|
|
3445
|
+
logger.info(`[kg-backfill] Processed ${i + 1}/${rows.length} memories in repo "${r}"`);
|
|
3446
|
+
}
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
if (source === "standards" || source === "both") {
|
|
3450
|
+
const rows = db2.db.prepare("SELECT title, content FROM coding_standards WHERE repo = ?").all(r);
|
|
3451
|
+
for (let i = 0; i < rows.length; i++) {
|
|
3452
|
+
const row = rows[i];
|
|
3453
|
+
const text = `${row.content || ""} ${row.title || ""}`;
|
|
3454
|
+
try {
|
|
3455
|
+
const entities = await extractEntities(text);
|
|
3456
|
+
if (entities.length > 0) {
|
|
3457
|
+
pendingOps.push({
|
|
3458
|
+
entities,
|
|
3459
|
+
repo: r,
|
|
3460
|
+
owner: currentOwner,
|
|
3461
|
+
observationText: `Mentioned in standard: ${row.title || "untitled"}`
|
|
3462
|
+
});
|
|
3463
|
+
}
|
|
3464
|
+
} catch {
|
|
3465
|
+
stats.errors++;
|
|
3466
|
+
}
|
|
3467
|
+
stats.itemsProcessed++;
|
|
3468
|
+
}
|
|
3469
|
+
}
|
|
3470
|
+
}
|
|
3422
3471
|
const insertEntity = db2.db.prepare(
|
|
3423
3472
|
`INSERT OR IGNORE INTO entities (name, type, description, repo, owner, created_at, updated_at)
|
|
3424
3473
|
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
@@ -3429,66 +3478,12 @@ async function handleKGBackfill(args, db2) {
|
|
|
3429
3478
|
);
|
|
3430
3479
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
3431
3480
|
const transaction = db2.db.transaction(() => {
|
|
3432
|
-
for (const
|
|
3433
|
-
const
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
const content = `${row.content || ""} ${row.title || ""}`;
|
|
3439
|
-
let entities;
|
|
3440
|
-
try {
|
|
3441
|
-
entities = extractEntities(content);
|
|
3442
|
-
} catch {
|
|
3443
|
-
stats.errors++;
|
|
3444
|
-
continue;
|
|
3445
|
-
}
|
|
3446
|
-
for (const ent of entities) {
|
|
3447
|
-
insertEntity.run(ent.name, ent.type, null, r, currentOwner, now, now);
|
|
3448
|
-
stats.entitiesCreated++;
|
|
3449
|
-
insertObservation.run(
|
|
3450
|
-
randomUUID6(),
|
|
3451
|
-
ent.name,
|
|
3452
|
-
`Mentioned in memory: ${row.title || "untitled"}`,
|
|
3453
|
-
r,
|
|
3454
|
-
currentOwner,
|
|
3455
|
-
now
|
|
3456
|
-
);
|
|
3457
|
-
stats.observationsCreated++;
|
|
3458
|
-
}
|
|
3459
|
-
stats.itemsProcessed++;
|
|
3460
|
-
if ((i + 1) % 100 === 0) {
|
|
3461
|
-
logger.info(`[kg-backfill] Processed ${i + 1}/${rows.length} memories in repo "${r}"`);
|
|
3462
|
-
}
|
|
3463
|
-
}
|
|
3464
|
-
}
|
|
3465
|
-
if (source === "standards" || source === "both") {
|
|
3466
|
-
const rows = db2.db.prepare("SELECT title, content FROM coding_standards WHERE repo = ?").all(r);
|
|
3467
|
-
for (let i = 0; i < rows.length; i++) {
|
|
3468
|
-
const row = rows[i];
|
|
3469
|
-
const content = `${row.content || ""} ${row.title || ""}`;
|
|
3470
|
-
let entities;
|
|
3471
|
-
try {
|
|
3472
|
-
entities = extractEntities(content);
|
|
3473
|
-
} catch {
|
|
3474
|
-
stats.errors++;
|
|
3475
|
-
continue;
|
|
3476
|
-
}
|
|
3477
|
-
for (const ent of entities) {
|
|
3478
|
-
insertEntity.run(ent.name, ent.type, null, r, currentOwner, now, now);
|
|
3479
|
-
stats.entitiesCreated++;
|
|
3480
|
-
insertObservation.run(
|
|
3481
|
-
randomUUID6(),
|
|
3482
|
-
ent.name,
|
|
3483
|
-
`Mentioned in standard: ${row.title || "untitled"}`,
|
|
3484
|
-
r,
|
|
3485
|
-
currentOwner,
|
|
3486
|
-
now
|
|
3487
|
-
);
|
|
3488
|
-
stats.observationsCreated++;
|
|
3489
|
-
}
|
|
3490
|
-
stats.itemsProcessed++;
|
|
3491
|
-
}
|
|
3481
|
+
for (const op of pendingOps) {
|
|
3482
|
+
for (const ent of op.entities) {
|
|
3483
|
+
insertEntity.run(ent.name, ent.type, null, op.repo, op.owner, now, now);
|
|
3484
|
+
stats.entitiesCreated++;
|
|
3485
|
+
insertObservation.run(randomUUID6(), ent.name, op.observationText, op.repo, op.owner, now);
|
|
3486
|
+
stats.observationsCreated++;
|
|
3492
3487
|
}
|
|
3493
3488
|
}
|
|
3494
3489
|
});
|
|
@@ -3835,8 +3830,8 @@ function registerAllResources(server, store, _vectors, session) {
|
|
|
3835
3830
|
mimeType: "application/json"
|
|
3836
3831
|
},
|
|
3837
3832
|
async (uri, _extra) => {
|
|
3838
|
-
const
|
|
3839
|
-
const payload = JSON.stringify(
|
|
3833
|
+
const repos = db2.system.listRepoNavigation();
|
|
3834
|
+
const payload = JSON.stringify(repos, null, 2);
|
|
3840
3835
|
return {
|
|
3841
3836
|
contents: [
|
|
3842
3837
|
{
|
|
@@ -3869,15 +3864,29 @@ function registerAllResources(server, store, _vectors, session) {
|
|
|
3869
3864
|
};
|
|
3870
3865
|
}
|
|
3871
3866
|
);
|
|
3872
|
-
|
|
3873
|
-
|
|
3867
|
+
function reposLazy() {
|
|
3868
|
+
let cached = null;
|
|
3869
|
+
return () => {
|
|
3870
|
+
if (cached === null) cached = getRepos();
|
|
3871
|
+
return cached;
|
|
3872
|
+
};
|
|
3873
|
+
}
|
|
3874
|
+
function tagsLazy() {
|
|
3875
|
+
let cached = null;
|
|
3876
|
+
return () => {
|
|
3877
|
+
if (cached === null) cached = getTags();
|
|
3878
|
+
return cached;
|
|
3879
|
+
};
|
|
3880
|
+
}
|
|
3881
|
+
const reposFn = reposLazy();
|
|
3882
|
+
const tagsFn = tagsLazy();
|
|
3874
3883
|
server.registerResource(
|
|
3875
3884
|
"repository-memories",
|
|
3876
3885
|
new ResourceTemplate("repository://{name}/memories{?search,type,tag,limit,offset}", {
|
|
3877
3886
|
list: void 0,
|
|
3878
3887
|
complete: {
|
|
3879
|
-
name: async (value) => rankCompletionValues(
|
|
3880
|
-
tag: async (value) => rankCompletionValues(
|
|
3888
|
+
name: async (value) => rankCompletionValues(reposFn(), value),
|
|
3889
|
+
tag: async (value) => rankCompletionValues(tagsFn(), value)
|
|
3881
3890
|
}
|
|
3882
3891
|
}),
|
|
3883
3892
|
{
|
|
@@ -3941,7 +3950,7 @@ function registerAllResources(server, store, _vectors, session) {
|
|
|
3941
3950
|
new ResourceTemplate("repository://{name}/tasks{?status,priority,limit,offset}", {
|
|
3942
3951
|
list: void 0,
|
|
3943
3952
|
complete: {
|
|
3944
|
-
name: async (value) => rankCompletionValues(
|
|
3953
|
+
name: async (value) => rankCompletionValues(reposFn(), value)
|
|
3945
3954
|
}
|
|
3946
3955
|
}),
|
|
3947
3956
|
{
|
|
@@ -4010,7 +4019,7 @@ function registerAllResources(server, store, _vectors, session) {
|
|
|
4010
4019
|
new ResourceTemplate("repository://{name}/summary", {
|
|
4011
4020
|
list: void 0,
|
|
4012
4021
|
complete: {
|
|
4013
|
-
name: async (value) => rankCompletionValues(
|
|
4022
|
+
name: async (value) => rankCompletionValues(reposFn(), value)
|
|
4014
4023
|
}
|
|
4015
4024
|
}),
|
|
4016
4025
|
{
|
|
@@ -4038,7 +4047,7 @@ function registerAllResources(server, store, _vectors, session) {
|
|
|
4038
4047
|
new ResourceTemplate("repository://{name}/actions{?limit,offset}", {
|
|
4039
4048
|
list: void 0,
|
|
4040
4049
|
complete: {
|
|
4041
|
-
name: async (value) => rankCompletionValues(
|
|
4050
|
+
name: async (value) => rankCompletionValues(reposFn(), value)
|
|
4042
4051
|
}
|
|
4043
4052
|
}),
|
|
4044
4053
|
{
|