@vheins/local-memory-mcp 0.18.8 → 0.18.10

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.
@@ -81,8 +81,8 @@ function loadServerInstructions() {
81
81
  // src/mcp/capabilities.ts
82
82
  var __dirname2 = path2.dirname(fileURLToPath2(import.meta.url));
83
83
  var pkgVersion = "0.1.0";
84
- if ("0.18.8") {
85
- pkgVersion = "0.18.8";
84
+ if ("0.18.10") {
85
+ pkgVersion = "0.18.10";
86
86
  } else {
87
87
  let searchDir = __dirname2;
88
88
  for (let i = 0; i < 5; i++) {
@@ -367,7 +367,6 @@ var MigrationManager = class {
367
367
  );
368
368
 
369
369
  CREATE INDEX IF NOT EXISTS idx_tasks_repo ON tasks(repo);
370
- CREATE INDEX IF NOT EXISTS idx_tasks_code ON tasks(task_code);
371
370
  CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status);
372
371
  CREATE INDEX IF NOT EXISTS idx_tasks_phase ON tasks(phase);
373
372
  CREATE INDEX IF NOT EXISTS idx_tasks_priority ON tasks(priority);
@@ -666,44 +665,32 @@ var MigrationManager = class {
666
665
  CREATE INDEX IF NOT EXISTS idx_memories_is_global ON memories(is_global);
667
666
  CREATE INDEX IF NOT EXISTS idx_coding_standards_hit_count ON coding_standards(hit_count);
668
667
  `);
669
- try {
670
- this.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_code_owner_repo ON tasks(owner, repo, task_code);`);
671
- } catch {
672
- const dupRows = this.all(`
673
- SELECT task_code, owner, repo, COUNT(*) as cnt
674
- FROM tasks
675
- GROUP BY owner, repo, task_code
676
- HAVING cnt > 1
677
- `);
678
- if (dupRows.length > 0) {
679
- console.log(`Found ${dupRows.length} duplicate task_code(s). Deduplicating...`);
680
- for (const dup of dupRows) {
681
- const idsToDelete = this.all(
682
- `SELECT t1.id FROM tasks t1
683
- WHERE t1.owner = ? AND t1.repo = ? AND t1.task_code = ?
684
- AND t1.id != (
685
- SELECT t2.id FROM tasks t2
686
- WHERE t2.owner = ? AND t2.repo = ? AND t2.task_code = ?
687
- ORDER BY t2.updated_at DESC
688
- LIMIT 1
689
- )`,
690
- dup.owner,
691
- dup.repo,
692
- dup.task_code,
693
- dup.owner,
694
- dup.repo,
695
- dup.task_code
696
- );
697
- for (const row of idsToDelete) {
698
- this.run("DELETE FROM task_comments WHERE task_id = ?", row.id);
699
- this.run("DELETE FROM tasks WHERE id = ?", row.id);
700
- }
701
- console.log(` Deduplicated ${dup.task_code}: kept 1, removed ${idsToDelete.length}`);
668
+ const dupRows = this.all(`
669
+ SELECT owner, repo, task_code, COUNT(*) as cnt
670
+ FROM tasks
671
+ GROUP BY owner, repo, task_code
672
+ HAVING cnt > 1
673
+ `);
674
+ if (dupRows.length > 0) {
675
+ console.log(`Found ${dupRows.length} duplicate task_code(s). Deduplicating by suffix...`);
676
+ for (const dup of dupRows) {
677
+ const rows = this.all(
678
+ `SELECT id, task_code, created_at FROM tasks
679
+ WHERE owner = ? AND repo = ? AND task_code = ?
680
+ ORDER BY created_at ASC, id ASC`,
681
+ dup.owner,
682
+ dup.repo,
683
+ dup.task_code
684
+ );
685
+ for (let i = 1; i < rows.length; i++) {
686
+ const newCode = `${dup.task_code}-${i + 1}`;
687
+ this.run("UPDATE tasks SET task_code = ? WHERE id = ?", newCode, rows[i].id);
702
688
  }
689
+ console.log(` Deduplicated ${dup.task_code}: kept 1 (${rows[0].id}), renamed ${rows.length - 1} rows`);
703
690
  }
704
- this.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_code_owner_repo ON tasks(owner, repo, task_code);`);
705
- console.log("UNIQUE INDEX on (owner, repo, task_code) created after deduplication.");
706
691
  }
692
+ this.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_code_owner_repo ON tasks(owner, repo, task_code);`);
693
+ this.exec("DROP INDEX IF EXISTS idx_tasks_code;");
707
694
  try {
708
695
  this.run("UPDATE tasks SET task_code = substr(id, 1, 8) WHERE task_code IS NULL");
709
696
  } catch {
@@ -1332,8 +1319,14 @@ var MemoryEntity = class extends BaseEntity {
1332
1319
  const row = this.get("SELECT * FROM memories WHERE id = ?", [id]);
1333
1320
  return row ? this.rowToMemoryEntry(row) : null;
1334
1321
  }
1335
- getByCode(code) {
1336
- const row = this.get("SELECT * FROM memories WHERE code = ?", [code]);
1322
+ getByCode(code, owner, repo) {
1323
+ let sql = "SELECT * FROM memories WHERE code = ?";
1324
+ const params = [code];
1325
+ if (owner && repo) {
1326
+ sql += " AND owner = ? AND repo = ?";
1327
+ params.push(owner, repo);
1328
+ }
1329
+ const row = this.get(sql, params);
1337
1330
  return row ? this.rowToMemoryEntry(row) : null;
1338
1331
  }
1339
1332
  getByIdWithStats(id) {
@@ -2099,10 +2092,27 @@ var TaskEntity = class extends BaseEntity {
2099
2092
  );
2100
2093
  return new Set(rows.map((r) => r.task_code));
2101
2094
  }
2095
+ /**
2096
+ * Bulk inserts tasks into the database within a single transaction.
2097
+ * Pre-deduplicates against existing task_codes before the transaction.
2098
+ * Rows whose task_code already exists in the DB are silently skipped (skip-and-continue).
2099
+ * The per-row INSERT has no try/catch since all rows are pre-validated.
2100
+ *
2101
+ * @param tasks - Array of tasks to insert
2102
+ * @returns Number of tasks actually inserted (excluding skipped duplicates)
2103
+ */
2102
2104
  bulkInsertTasks(tasks) {
2105
+ if (tasks.length === 0) return 0;
2106
+ const owner = tasks[0].owner || "";
2107
+ const repo = tasks[0].repo;
2108
+ const codes = tasks.map((t) => t.task_code);
2109
+ const existingCodes = this.getExistingTaskCodes(owner, repo, codes);
2103
2110
  return this.transaction(() => {
2104
2111
  let count = 0;
2105
2112
  for (const task of tasks) {
2113
+ if (existingCodes.has(task.task_code)) {
2114
+ continue;
2115
+ }
2106
2116
  this.run(
2107
2117
  `INSERT INTO tasks (
2108
2118
  id, repo, owner, task_code, phase, title, description, status, priority,
@@ -2753,8 +2763,14 @@ var StandardEntity = class extends BaseEntity {
2753
2763
  const row = this.get("SELECT * FROM coding_standards WHERE id = ?", [id]);
2754
2764
  return row ? this.rowToEntry(row) : null;
2755
2765
  }
2756
- getByCode(code) {
2757
- const row = this.get("SELECT * FROM coding_standards WHERE code = ?", [code]);
2766
+ getByCode(code, owner, repo) {
2767
+ let sql = "SELECT * FROM coding_standards WHERE code = ?";
2768
+ const params = [code];
2769
+ if (owner && repo) {
2770
+ sql += " AND owner = ? AND repo = ?";
2771
+ params.push(owner, repo);
2772
+ }
2773
+ const row = this.get(sql, params);
2758
2774
  return row ? this.rowToEntry(row) : null;
2759
2775
  }
2760
2776
  search(options) {
@@ -3642,6 +3658,8 @@ var MemoryStoreSchema = z.object({
3642
3658
  var MemoryUpdateSchema = z.object({
3643
3659
  id: z.string().uuid().optional(),
3644
3660
  code: z.string().max(20).optional(),
3661
+ owner: z.string().min(1),
3662
+ repo: z.string().min(1).transform(normalizeRepo),
3645
3663
  type: MemoryTypeSchema.optional(),
3646
3664
  title: z.string().min(3).max(255).optional(),
3647
3665
  content: z.string().min(10).optional(),
@@ -3680,6 +3698,8 @@ var MemorySearchSchema = z.object({
3680
3698
  var MemoryAcknowledgeSchema = z.object({
3681
3699
  memory_id: z.string().uuid().optional(),
3682
3700
  code: z.string().max(20).optional(),
3701
+ owner: z.string().min(1),
3702
+ repo: z.string().min(1).transform(normalizeRepo),
3683
3703
  status: z.enum(["used", "irrelevant", "contradictory"]),
3684
3704
  application_context: z.string().min(10).optional(),
3685
3705
  structured: z.boolean().default(false)
@@ -3694,8 +3714,8 @@ var MemoryRecapSchema = z.object({
3694
3714
  structured: z.boolean().default(false)
3695
3715
  });
3696
3716
  var MemoryDeleteSchema = z.object({
3697
- owner: z.string().optional().default(""),
3698
- repo: z.string().min(1).transform(normalizeRepo).optional(),
3717
+ owner: z.string().min(1),
3718
+ repo: z.string().min(1).transform(normalizeRepo),
3699
3719
  id: z.string().uuid().optional(),
3700
3720
  ids: z.array(z.string().uuid()).min(1).optional(),
3701
3721
  code: z.string().max(20).optional(),
@@ -3854,10 +3874,20 @@ var TaskUpdateSchema = z.object({
3854
3874
  }).refine((data) => Object.keys(data).length > 2, {
3855
3875
  message: "At least one field besides repo and id/ids must be provided for update"
3856
3876
  });
3877
+ var TaskStatusValues = TaskStatusSchema.options;
3878
+ var TaskStatusListSchema = z.string().refine(
3879
+ (val) => {
3880
+ if (val === "all") return true;
3881
+ const parts = val.split(",").map((s) => s.trim()).filter(Boolean);
3882
+ if (parts.length === 0) return false;
3883
+ return parts.every((p) => TaskStatusValues.includes(p));
3884
+ },
3885
+ { message: "status must be 'all' or a comma-separated list of valid TaskStatus values" }
3886
+ );
3857
3887
  var TaskListSchema = z.object({
3858
3888
  owner: z.string().min(1),
3859
3889
  repo: z.string().min(1).transform(normalizeRepo),
3860
- status: z.string().optional(),
3890
+ status: TaskStatusListSchema.default("backlog,pending,in_progress,blocked"),
3861
3891
  phase: z.string().optional(),
3862
3892
  query: z.string().optional(),
3863
3893
  limit: z.number().min(1).max(100).default(15),
@@ -3888,6 +3918,8 @@ var TaskDeleteSchema = z.object({
3888
3918
  var MemoryDetailSchema = z.object({
3889
3919
  id: z.string().uuid().optional(),
3890
3920
  code: z.string().max(20).optional(),
3921
+ owner: z.string().min(1),
3922
+ repo: z.string().min(1).transform(normalizeRepo),
3891
3923
  structured: z.boolean().default(false)
3892
3924
  }).refine((data) => data.id !== void 0 || data.code !== void 0, {
3893
3925
  message: "Either id or code must be provided"
@@ -3895,13 +3927,15 @@ var MemoryDetailSchema = z.object({
3895
3927
  var StandardDetailSchema = z.object({
3896
3928
  id: z.string().uuid().optional(),
3897
3929
  code: z.string().max(20).optional(),
3930
+ owner: z.string().min(1),
3931
+ repo: z.string().min(1).transform(normalizeRepo),
3898
3932
  structured: z.boolean().default(false)
3899
3933
  }).refine((data) => data.id !== void 0 || data.code !== void 0, {
3900
3934
  message: "Either id or code must be provided"
3901
3935
  });
3902
3936
  var StandardDeleteSchema = z.object({
3903
- owner: z.string().optional().default(""),
3904
- repo: z.string().min(1).transform(normalizeRepo).optional(),
3937
+ owner: z.string().min(1),
3938
+ repo: z.string().min(1).transform(normalizeRepo),
3905
3939
  id: z.string().uuid().optional(),
3906
3940
  ids: z.array(z.string().uuid()).min(1).optional(),
3907
3941
  code: z.string().max(20).optional(),
@@ -4028,8 +4062,8 @@ var StandardUpdateSchema = z.object({
4028
4062
  version: z.string().optional(),
4029
4063
  language: z.string().optional(),
4030
4064
  stack: z.array(z.string().min(1)).min(1).optional(),
4031
- owner: z.string().optional().default(""),
4032
- repo: z.string().transform(normalizeRepo).optional(),
4065
+ owner: z.string().min(1),
4066
+ repo: z.string().transform(normalizeRepo),
4033
4067
  is_global: z.boolean().optional(),
4034
4068
  tags: z.array(z.string().min(1)).min(1).optional(),
4035
4069
  metadata: z.record(z.string(), z.any()).refine((value) => Object.keys(value).length > 0, { message: "metadata must contain at least one key" }).optional(),
@@ -4076,7 +4110,7 @@ var TOOL_DEFINITIONS = [
4076
4110
  properties: {
4077
4111
  owner: {
4078
4112
  type: "string",
4079
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
4113
+ description: "Organization/namespace (e.g., GitHub org or username)"
4080
4114
  },
4081
4115
  repo: { type: "string", description: "Repository/project name. Optional when a single MCP root is active." },
4082
4116
  objective: { type: "string", minLength: 5, description: "Question or synthesis objective." },
@@ -4124,10 +4158,7 @@ var TOOL_DEFINITIONS = [
4124
4158
  inputSchema: {
4125
4159
  type: "object",
4126
4160
  properties: {
4127
- owner: {
4128
- type: "string",
4129
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
4130
- },
4161
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4131
4162
  repo: {
4132
4163
  type: "string",
4133
4164
  description: "Repository name. Optional when it can be inferred from MCP roots or elicited from the user."
@@ -4200,10 +4231,7 @@ var TOOL_DEFINITIONS = [
4200
4231
  inputSchema: {
4201
4232
  type: "object",
4202
4233
  properties: {
4203
- owner: {
4204
- type: "string",
4205
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
4206
- },
4234
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4207
4235
  repo: { type: "string", description: "Repository name" },
4208
4236
  id: { type: "string", format: "uuid", description: "Task ID (optional if task_code is provided)" },
4209
4237
  task_code: { type: "string", description: "Task code (e.g. TASK-001) (optional if id is provided)" },
@@ -4266,10 +4294,7 @@ var TOOL_DEFINITIONS = [
4266
4294
  scope: {
4267
4295
  type: "object",
4268
4296
  properties: {
4269
- owner: {
4270
- type: "string",
4271
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
4272
- },
4297
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4273
4298
  repo: { type: "string", description: "Repository/project name" },
4274
4299
  branch: { type: "string", description: "Git branch this memory relates to" },
4275
4300
  folder: { type: "string", description: "Subdirectory within the repo" },
@@ -4318,10 +4343,7 @@ var TOOL_DEFINITIONS = [
4318
4343
  scope: {
4319
4344
  type: "object",
4320
4345
  properties: {
4321
- owner: {
4322
- type: "string",
4323
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
4324
- },
4346
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4325
4347
  repo: { type: "string", description: "Repository/project name" },
4326
4348
  branch: { type: "string", description: "Git branch this memory relates to" },
4327
4349
  folder: { type: "string", description: "Subdirectory within the repo" },
@@ -4462,10 +4484,7 @@ var TOOL_DEFINITIONS = [
4462
4484
  description: "Search keyword to match against memory titles and content"
4463
4485
  },
4464
4486
  prompt: { type: "string", description: "Natural language prompt for semantic search" },
4465
- owner: {
4466
- type: "string",
4467
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
4468
- },
4487
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4469
4488
  repo: { type: "string", description: "Repository/project name" },
4470
4489
  current_tags: {
4471
4490
  type: "array",
@@ -4493,10 +4512,7 @@ var TOOL_DEFINITIONS = [
4493
4512
  scope: {
4494
4513
  type: "object",
4495
4514
  properties: {
4496
- owner: {
4497
- type: "string",
4498
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
4499
- },
4515
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4500
4516
  repo: { type: "string", description: "Repository/project name" },
4501
4517
  branch: { type: "string", description: "Git branch filter" },
4502
4518
  folder: { type: "string", description: "Subdirectory filter" },
@@ -4552,10 +4568,7 @@ var TOOL_DEFINITIONS = [
4552
4568
  inputSchema: {
4553
4569
  type: "object",
4554
4570
  properties: {
4555
- owner: {
4556
- type: "string",
4557
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
4558
- },
4571
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4559
4572
  repo: { type: "string", description: "Repository/project name" },
4560
4573
  signals: {
4561
4574
  type: "array",
@@ -4684,10 +4697,7 @@ var TOOL_DEFINITIONS = [
4684
4697
  inputSchema: {
4685
4698
  type: "object",
4686
4699
  properties: {
4687
- owner: {
4688
- type: "string",
4689
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
4690
- },
4700
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4691
4701
  repo: { type: "string", description: "Repository/project name (required)" },
4692
4702
  limit: {
4693
4703
  type: "number",
@@ -4761,10 +4771,7 @@ var TOOL_DEFINITIONS = [
4761
4771
  inputSchema: {
4762
4772
  type: "object",
4763
4773
  properties: {
4764
- owner: {
4765
- type: "string",
4766
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
4767
- },
4774
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4768
4775
  repo: { type: "string", description: "Repository/project name" },
4769
4776
  task_code: { type: "string", description: "Unique task code (e.g. TASK-001) (Required for single task)" },
4770
4777
  phase: { type: "string", description: "Project phase (Required for single task)" },
@@ -4877,10 +4884,7 @@ var TOOL_DEFINITIONS = [
4877
4884
  inputSchema: {
4878
4885
  type: "object",
4879
4886
  properties: {
4880
- owner: {
4881
- type: "string",
4882
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
4883
- },
4887
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4884
4888
  repo: { type: "string", description: "Repository name" },
4885
4889
  id: { type: "string", format: "uuid", description: "Task ID (for single update)" },
4886
4890
  ids: { type: "array", items: { type: "string", format: "uuid" }, description: "Task IDs (for bulk update)" },
@@ -4973,10 +4977,7 @@ var TOOL_DEFINITIONS = [
4973
4977
  inputSchema: {
4974
4978
  type: "object",
4975
4979
  properties: {
4976
- owner: {
4977
- type: "string",
4978
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
4979
- },
4980
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4980
4981
  repo: { type: "string", description: "Repository name" },
4981
4982
  id: {
4982
4983
  type: "string",
@@ -5005,7 +5006,7 @@ var TOOL_DEFINITIONS = [
5005
5006
  {
5006
5007
  name: "task-list",
5007
5008
  title: "Task List",
5008
- description: "PRIMARY navigation and search tool for tasks. Returns a compact tabular list of tasks (id, task_code, title, status, priority, updated_at, comments_count). Defaults to in_progress and pending tasks. Use 'query' to filter by code, title, or description. Use 'status' (comma-separated) for specific filters, or 'all' for all statuses. AGENTS: call this once at start, pick ONE task, then call task-detail.",
5009
+ description: "PRIMARY navigation and search tool for tasks. Returns a compact tabular list of tasks (id, task_code, title, status, priority, updated_at, comments_count). Defaults to backlog, pending, in_progress, and blocked tasks. Use 'status' (comma-separated) to override or 'all' for all statuses. AGENTS: call this once at start, pick ONE task, then call task-detail.",
5009
5010
  annotations: {
5010
5011
  readOnlyHint: true,
5011
5012
  idempotentHint: true,
@@ -5014,17 +5015,14 @@ var TOOL_DEFINITIONS = [
5014
5015
  inputSchema: {
5015
5016
  type: "object",
5016
5017
  properties: {
5017
- owner: {
5018
- type: "string",
5019
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
5020
- },
5018
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5021
5019
  repo: {
5022
5020
  type: "string",
5023
5021
  description: "Repository/project name (required)"
5024
5022
  },
5025
5023
  status: {
5026
5024
  type: "string",
5027
- default: "in_progress,pending",
5025
+ default: "backlog,pending,in_progress,blocked",
5028
5026
  description: "Comma-separated status filter (backlog, pending, in_progress, completed, canceled, blocked) or 'all' for all statuses. Defaults to 'backlog,pending,in_progress,blocked'."
5029
5027
  },
5030
5028
  phase: {
@@ -5094,10 +5092,7 @@ var TOOL_DEFINITIONS = [
5094
5092
  inputSchema: {
5095
5093
  type: "object",
5096
5094
  properties: {
5097
- owner: {
5098
- type: "string",
5099
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
5100
- },
5095
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5101
5096
  repo: { type: "string", description: "Repository/project name" },
5102
5097
  query: {
5103
5098
  type: "string",
@@ -5155,10 +5150,7 @@ var TOOL_DEFINITIONS = [
5155
5150
  inputSchema: {
5156
5151
  type: "object",
5157
5152
  properties: {
5158
- owner: {
5159
- type: "string",
5160
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
5161
- },
5153
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5162
5154
  repo: { type: "string", description: "Repository/project name" },
5163
5155
  from_agent: { type: "string", description: "Agent creating the handoff" },
5164
5156
  to_agent: { type: "string", description: "Optional target agent" },
@@ -5233,10 +5225,7 @@ var TOOL_DEFINITIONS = [
5233
5225
  inputSchema: {
5234
5226
  type: "object",
5235
5227
  properties: {
5236
- owner: {
5237
- type: "string",
5238
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
5239
- },
5228
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5240
5229
  repo: { type: "string", description: "Repository/project name" },
5241
5230
  status: { type: "string", enum: ["pending", "accepted", "rejected", "expired"] },
5242
5231
  from_agent: { type: "string" },
@@ -5286,10 +5275,7 @@ var TOOL_DEFINITIONS = [
5286
5275
  inputSchema: {
5287
5276
  type: "object",
5288
5277
  properties: {
5289
- owner: {
5290
- type: "string",
5291
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
5292
- },
5278
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5293
5279
  repo: { type: "string", description: "Repository/project name" },
5294
5280
  task_id: {
5295
5281
  type: "string",
@@ -5333,10 +5319,7 @@ var TOOL_DEFINITIONS = [
5333
5319
  inputSchema: {
5334
5320
  type: "object",
5335
5321
  properties: {
5336
- owner: {
5337
- type: "string",
5338
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
5339
- },
5322
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5340
5323
  repo: { type: "string", description: "Repository/project name" },
5341
5324
  agent: { type: "string", description: "Optional agent filter" },
5342
5325
  active_only: { type: "boolean", description: "When true, return only unreleased claims" },
@@ -5385,10 +5368,7 @@ var TOOL_DEFINITIONS = [
5385
5368
  inputSchema: {
5386
5369
  type: "object",
5387
5370
  properties: {
5388
- owner: {
5389
- type: "string",
5390
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
5391
- },
5371
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5392
5372
  repo: { type: "string", description: "Repository name" },
5393
5373
  task_id: {
5394
5374
  type: "string",
@@ -5426,10 +5406,7 @@ var TOOL_DEFINITIONS = [
5426
5406
  inputSchema: {
5427
5407
  type: "object",
5428
5408
  properties: {
5429
- owner: {
5430
- type: "string",
5431
- description: "GitHub repository owner (username or organization). For repo 'vheins/my-repo', owner='vheins'. CRITICAL: this is the GitHub owner, NOT the agent name. Do NOT use the calling agent's name."
5432
- },
5409
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5433
5410
  name: { type: "string", minLength: 3, maxLength: 255, description: "Human-readable standard name" },
5434
5411
  content: {
5435
5412
  type: "string",
@@ -6413,7 +6390,7 @@ async function handleTaskClaim(args, storage) {
6413
6390
  const { owner, repo, task_id, task_code, agent, role, metadata, structured } = validated;
6414
6391
  let taskId = task_id;
6415
6392
  let resolvedTaskCode;
6416
- let task = null;
6393
+ let task;
6417
6394
  if (taskId) {
6418
6395
  task = storage.tasks.getTaskById(taskId);
6419
6396
  if (!task || task.repo !== repo) {
@@ -16,7 +16,7 @@ import {
16
16
  handleTaskClaim,
17
17
  listResources,
18
18
  logger
19
- } from "../chunk-PXTBSGN4.js";
19
+ } from "../chunk-YACQ3MHF.js";
20
20
 
21
21
  // src/dashboard/server.ts
22
22
  import express from "express";
@@ -726,9 +726,9 @@ var TasksController = class {
726
726
  try {
727
727
  await db.refresh();
728
728
  const attributes = getAttributes(req);
729
- const { repo, task_code, title } = attributes;
729
+ const { repo, task_code, title, owner } = attributes;
730
730
  if (!repo || !task_code || !title) return res.status(400).json(jsonApiError("Required fields missing", 400));
731
- if (db.tasks.isTaskCodeDuplicate("", repo, task_code))
731
+ if (db.tasks.isTaskCodeDuplicate(owner || "", repo, task_code))
732
732
  return res.status(400).json(jsonApiError("Duplicate task_code", 400));
733
733
  const id = randomUUID2();
734
734
  await db.withWrite(() => {
@@ -1320,7 +1320,8 @@ try {
1320
1320
  } catch {
1321
1321
  }
1322
1322
  var app = express();
1323
- var PORT = process.env.PORT || 3456;
1323
+ var PORT = Number(process.env.PORT) || 3456;
1324
+ var HOST = process.env.DASHBOARD_HOST || "127.0.0.1";
1324
1325
  app.use(express.json({ limit: process.env.DASHBOARD_JSON_LIMIT || "50mb" }));
1325
1326
  app.use((req, res, next) => {
1326
1327
  const start = Date.now();
@@ -1399,8 +1400,17 @@ if (process.env.DASHBOARD_ENABLE_MCP === "true") {
1399
1400
  mcpClient.start().catch((e) => logger.error("MCP Client failed", { error: e.message }));
1400
1401
  }
1401
1402
  function startServer() {
1402
- const server = app.listen(PORT, () => {
1403
- console.log(`${(/* @__PURE__ */ new Date()).toISOString()} DASHBOARD_STARTING v${pkg2.version} on port ${PORT}`);
1403
+ const server = app.listen(PORT, HOST, () => {
1404
+ const addr = server.address();
1405
+ const bindAddr = typeof addr === "string" ? addr : addr?.address ?? HOST;
1406
+ if (bindAddr !== "127.0.0.1" && bindAddr !== "::1") {
1407
+ logger.warn("Dashboard bound to non-loopback address \u2014 access is exposed on the network", {
1408
+ address: bindAddr,
1409
+ port: PORT,
1410
+ suggestion: "Set DASHBOARD_HOST=127.0.0.1 to restrict to localhost"
1411
+ });
1412
+ }
1413
+ console.log(`${(/* @__PURE__ */ new Date()).toISOString()} DASHBOARD_STARTING v${pkg2.version} on ${bindAddr}:${PORT}`);
1404
1414
  });
1405
1415
  server.on("error", (err) => {
1406
1416
  if (err.code === "EADDRINUSE") {
@@ -63,7 +63,7 @@ import {
63
63
  toContextSlug,
64
64
  updateSessionFromInitialize,
65
65
  updateSessionRoots
66
- } from "../chunk-PXTBSGN4.js";
66
+ } from "../chunk-YACQ3MHF.js";
67
67
 
68
68
  // src/mcp/server.ts
69
69
  import readline from "readline";
@@ -230,10 +230,10 @@ function hasMetadataLikeTitle(title) {
230
230
  const normalized = title.trim();
231
231
  return /^\[[^\]]{0,200}(agent:|role:|model:|\d{4}-\d{2}-\d{2}|source_)[^\]]*\]/i.test(normalized);
232
232
  }
233
- function resolveMemorySupersedes(value, db2) {
233
+ function resolveMemorySupersedes(value, db2, owner, repo) {
234
234
  if (!value) return null;
235
235
  if (UUID_REGEX.test(value)) return value;
236
- const memory = db2.memories.getByCode(value);
236
+ const memory = db2.memories.getByCode(value, owner, repo);
237
237
  if (!memory) throw new Error(`supersedes: memory with code '${value}' not found`);
238
238
  return memory.id;
239
239
  }
@@ -246,7 +246,7 @@ async function storeSingleMemory(params, db2, vectors2) {
246
246
  const now = (/* @__PURE__ */ new Date()).toISOString();
247
247
  const createdAtTime = new Date(now).getTime();
248
248
  const expires_at = params.ttlDays != null ? new Date(createdAtTime + params.ttlDays * 864e5).toISOString() : null;
249
- const resolvedSupersedes = resolveMemorySupersedes(params.supersedes, db2);
249
+ const resolvedSupersedes = resolveMemorySupersedes(params.supersedes, db2, params.scope.owner, params.scope.repo);
250
250
  if (!resolvedSupersedes && params.type !== "task_archive") {
251
251
  const conflict = await db2.memoryVectors.checkConflicts(
252
252
  params.content,
@@ -341,7 +341,7 @@ async function handleMemoryStore(params, db2, vectors2) {
341
341
  }
342
342
  const createdAtTime = new Date(now).getTime();
343
343
  const expires_at = mem.ttlDays != null ? new Date(createdAtTime + mem.ttlDays * 864e5).toISOString() : null;
344
- const resolvedSupersedes = resolveMemorySupersedes(mem.supersedes, db2);
344
+ const resolvedSupersedes = resolveMemorySupersedes(mem.supersedes, db2, mem.scope.owner, mem.scope.repo);
345
345
  if (!resolvedSupersedes && mem.type !== "task_archive") {
346
346
  const conflict = await db2.memoryVectors.checkConflicts(
347
347
  mem.content,
@@ -451,10 +451,10 @@ function hasMetadataLikeTitle2(title) {
451
451
  const normalized = title.trim();
452
452
  return /^\[[^\]]{0,200}(agent:|role:|model:|\d{4}-\d{2}-\d{2}|source_)[^\]]*\]/i.test(normalized);
453
453
  }
454
- function resolveMemorySupersedes2(value, db2) {
454
+ function resolveMemorySupersedes2(value, db2, owner, repo) {
455
455
  if (!value) return null;
456
456
  if (UUID_REGEX2.test(value)) return value;
457
- const memory = db2.memories.getByCode(value);
457
+ const memory = db2.memories.getByCode(value, owner, repo);
458
458
  if (!memory) throw new Error(`supersedes: memory with code '${value}' not found`);
459
459
  return memory.id;
460
460
  }
@@ -462,7 +462,7 @@ async function handleMemoryUpdate(params, db2, vectors2) {
462
462
  const validated = MemoryUpdateSchema.parse(params);
463
463
  let resolvedId = validated.id;
464
464
  if (!resolvedId && validated.code) {
465
- const byCode = db2.memories.getByCode(validated.code);
465
+ const byCode = db2.memories.getByCode(validated.code, validated.owner, validated.repo);
466
466
  if (!byCode) throw new Error(`Memory not found: ${validated.code}`);
467
467
  resolvedId = byCode.id;
468
468
  } else if (!resolvedId) {
@@ -491,7 +491,8 @@ async function handleMemoryUpdate(params, db2, vectors2) {
491
491
  if (validated.agent !== void 0) updates.agent = validated.agent;
492
492
  if (validated.role !== void 0) updates.role = validated.role;
493
493
  if (validated.status !== void 0) updates.status = validated.status;
494
- if (validated.supersedes !== void 0) updates.supersedes = resolveMemorySupersedes2(validated.supersedes, db2);
494
+ if (validated.supersedes !== void 0)
495
+ updates.supersedes = resolveMemorySupersedes2(validated.supersedes, db2, existing.scope.owner, existing.scope.repo);
495
496
  if (validated.tags !== void 0) updates.tags = validated.tags;
496
497
  if (validated.metadata !== void 0) updates.metadata = validated.metadata;
497
498
  if (validated.is_global !== void 0) updates.is_global = validated.is_global;
@@ -941,16 +942,7 @@ Comments & History:
941
942
  }
942
943
  async function handleTaskList(args, storage) {
943
944
  const validated = TaskListSchema.parse(args);
944
- const {
945
- owner,
946
- repo,
947
- status = "backlog,pending,in_progress,blocked",
948
- phase,
949
- query,
950
- limit,
951
- offset,
952
- structured: isStructuredRequest = false
953
- } = validated;
945
+ const { owner, repo, status, phase, query, limit, offset, structured: isStructuredRequest = false } = validated;
954
946
  let statuses = [];
955
947
  if (status !== "all") {
956
948
  statuses = status.split(",").map((s) => s.trim()).filter(Boolean);
@@ -1685,18 +1677,18 @@ async function executeSamplingTool(toolName, rawInput, db2, vectors2, owner) {
1685
1677
  // src/mcp/tools/memory.delete.ts
1686
1678
  async function handleMemoryDelete(params, db2, vectors2, onProgress) {
1687
1679
  const validated = MemoryDeleteSchema.parse(params);
1688
- const { id, ids, code, codes, repo, structured } = validated;
1680
+ const { id, ids, code, codes, owner, repo, structured } = validated;
1689
1681
  const resolvedIds = [];
1690
1682
  if (ids) resolvedIds.push(...ids);
1691
1683
  if (id) resolvedIds.push(id);
1692
1684
  if (code) {
1693
- const entry = db2.memories.getByCode(code);
1685
+ const entry = db2.memories.getByCode(code, owner, repo);
1694
1686
  if (!entry) throw new Error(`Memory not found: ${code}`);
1695
1687
  resolvedIds.push(entry.id);
1696
1688
  }
1697
1689
  if (codes) {
1698
1690
  for (const c of codes) {
1699
- const entry = db2.memories.getByCode(c);
1691
+ const entry = db2.memories.getByCode(c, owner, repo);
1700
1692
  if (!entry) throw new Error(`Memory not found: ${c}`);
1701
1693
  resolvedIds.push(entry.id);
1702
1694
  }
@@ -1760,7 +1752,7 @@ async function handleMemoryAcknowledge(params, db2) {
1760
1752
  const validated = MemoryAcknowledgeSchema.parse(params);
1761
1753
  let memoryId = validated.memory_id;
1762
1754
  if (!memoryId && validated.code) {
1763
- const byCode = db2.memories.getByCode(validated.code);
1755
+ const byCode = db2.memories.getByCode(validated.code, validated.owner, validated.repo);
1764
1756
  if (!byCode) throw new Error(`Memory not found: ${validated.code}`);
1765
1757
  memoryId = byCode.id;
1766
1758
  } else if (!memoryId) {
@@ -1798,12 +1790,12 @@ async function handleMemoryAcknowledge(params, db2) {
1798
1790
  // src/mcp/tools/memory.detail.ts
1799
1791
  async function handleMemoryDetail(args, storage) {
1800
1792
  const validated = MemoryDetailSchema.parse(args);
1801
- const { id, code } = validated;
1793
+ const { id, code, owner, repo } = validated;
1802
1794
  let memory;
1803
1795
  if (id) {
1804
1796
  memory = storage.memories.getById(id);
1805
1797
  } else if (code) {
1806
- memory = storage.memories.getByCode(code);
1798
+ memory = storage.memories.getByCode(code, owner, repo);
1807
1799
  }
1808
1800
  if (!memory) {
1809
1801
  throw new Error(`Memory not found: ${id || code}`);
@@ -1832,10 +1824,10 @@ async function handleMemoryDetail(args, storage) {
1832
1824
  // src/mcp/tools/standard.store.ts
1833
1825
  import { randomUUID as randomUUID3 } from "crypto";
1834
1826
  var UUID_REGEX4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1835
- function resolveStandardParentId(value, db2) {
1827
+ function resolveStandardParentId(value, db2, owner, repo) {
1836
1828
  if (!value) return null;
1837
1829
  if (UUID_REGEX4.test(value)) return value;
1838
- const standard = db2.standards.getByCode(value);
1830
+ const standard = db2.standards.getByCode(value, owner, repo);
1839
1831
  if (!standard) throw new Error(`parent_id: standard with code '${value}' not found`);
1840
1832
  return standard.id;
1841
1833
  }
@@ -1877,7 +1869,7 @@ async function storeSingleStandard(params, db2, vectors2) {
1877
1869
  code: generateNextCode(params.owner, params.repo || "__global__", "standard", db2),
1878
1870
  title: params.name,
1879
1871
  content: params.content,
1880
- parent_id: resolveStandardParentId(params.parent_id, db2),
1872
+ parent_id: resolveStandardParentId(params.parent_id, db2, params.owner, params.repo),
1881
1873
  context: toContextSlug(params.context || "general"),
1882
1874
  version: params.version || "1.0.0",
1883
1875
  language: params.language || null,
@@ -1960,7 +1952,7 @@ async function handleStandardStore(params, db2, vectors2) {
1960
1952
  code,
1961
1953
  title: std.name,
1962
1954
  content: std.content,
1963
- parent_id: resolveStandardParentId(std.parent_id, db2),
1955
+ parent_id: resolveStandardParentId(std.parent_id, db2, validated.owner, validated.repo),
1964
1956
  context: toContextSlug(std.context || "general"),
1965
1957
  version: std.version || "1.0.0",
1966
1958
  language: std.language || null,
@@ -2242,10 +2234,10 @@ async function handleStandardSearch(params, db2, vectors2) {
2242
2234
 
2243
2235
  // src/mcp/tools/standard.update.ts
2244
2236
  var UUID_REGEX5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
2245
- function resolveStandardParentId2(value, db2) {
2237
+ function resolveStandardParentId2(value, db2, owner, repo) {
2246
2238
  if (!value) return null;
2247
2239
  if (UUID_REGEX5.test(value)) return value;
2248
- const standard = db2.standards.getByCode(value);
2240
+ const standard = db2.standards.getByCode(value, owner, repo);
2249
2241
  if (!standard) throw new Error(`parent_id: standard with code '${value}' not found`);
2250
2242
  return standard.id;
2251
2243
  }
@@ -2253,7 +2245,7 @@ async function handleStandardUpdate(params, db2, vectors2) {
2253
2245
  const validated = StandardUpdateSchema.parse(params);
2254
2246
  let resolvedId = validated.id;
2255
2247
  if (!resolvedId && validated.code) {
2256
- const byCode = db2.standards.getByCode(validated.code);
2248
+ const byCode = db2.standards.getByCode(validated.code, validated.owner, validated.repo);
2257
2249
  if (!byCode) throw new Error(`Coding standard not found: ${validated.code}`);
2258
2250
  resolvedId = byCode.id;
2259
2251
  } else if (!resolvedId) {
@@ -2266,7 +2258,8 @@ async function handleStandardUpdate(params, db2, vectors2) {
2266
2258
  const updates = {};
2267
2259
  if (validated.name !== void 0) updates.title = validated.name;
2268
2260
  if (validated.content !== void 0) updates.content = validated.content;
2269
- if (validated.parent_id !== void 0) updates.parent_id = resolveStandardParentId2(validated.parent_id, db2);
2261
+ if (validated.parent_id !== void 0)
2262
+ updates.parent_id = resolveStandardParentId2(validated.parent_id, db2, existing.owner, existing.repo ?? void 0);
2270
2263
  if (validated.context !== void 0) updates.context = validated.context;
2271
2264
  if (validated.version !== void 0) updates.version = validated.version;
2272
2265
  if (validated.language !== void 0) updates.language = validated.language;
@@ -2308,7 +2301,7 @@ async function handleStandardUpdate(params, db2, vectors2) {
2308
2301
  // src/mcp/tools/standard.detail.ts
2309
2302
  async function handleStandardDetail(args, storage) {
2310
2303
  const validated = StandardDetailSchema.parse(args);
2311
- const standard = validated.id ? storage.standards.getById(validated.id) : storage.standards.getByCode(validated.code);
2304
+ const standard = validated.id ? storage.standards.getById(validated.id) : storage.standards.getByCode(validated.code, validated.owner, validated.repo);
2312
2305
  if (!standard) {
2313
2306
  const identifier = validated.id ?? validated.code;
2314
2307
  throw new Error(`Coding standard not found: ${identifier}`);
@@ -2342,18 +2335,18 @@ async function handleStandardDetail(args, storage) {
2342
2335
  // src/mcp/tools/standard.delete.ts
2343
2336
  async function handleStandardDelete(params, db2, vectors2) {
2344
2337
  const validated = StandardDeleteSchema.parse(params);
2345
- const { id, ids, code, codes, repo, structured } = validated;
2338
+ const { id, ids, code, codes, owner, repo, structured } = validated;
2346
2339
  const resolvedIds = [];
2347
2340
  if (ids) resolvedIds.push(...ids);
2348
2341
  if (id) resolvedIds.push(id);
2349
2342
  if (code) {
2350
- const entry = db2.standards.getByCode(code);
2343
+ const entry = db2.standards.getByCode(code, owner, repo);
2351
2344
  if (!entry) throw new Error(`Coding standard not found: ${code}`);
2352
2345
  resolvedIds.push(entry.id);
2353
2346
  }
2354
2347
  if (codes) {
2355
2348
  for (const c of codes) {
2356
- const entry = db2.standards.getByCode(c);
2349
+ const entry = db2.standards.getByCode(c, owner, repo);
2357
2350
  if (!entry) throw new Error(`Coding standard not found: ${c}`);
2358
2351
  resolvedIds.push(entry.id);
2359
2352
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vheins/local-memory-mcp",
3
- "version": "0.18.8",
3
+ "version": "0.18.10",
4
4
  "description": "MCP Local Memory Service for coding copilot agents",
5
5
  "mcpName": "io.github.vheins/local-memory-mcp",
6
6
  "type": "module",