@vheins/local-memory-mcp 0.18.7 → 0.18.9

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.7") {
85
- pkgVersion = "0.18.7";
84
+ if ("0.18.9") {
85
+ pkgVersion = "0.18.9";
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 {
@@ -2099,10 +2086,27 @@ var TaskEntity = class extends BaseEntity {
2099
2086
  );
2100
2087
  return new Set(rows.map((r) => r.task_code));
2101
2088
  }
2089
+ /**
2090
+ * Bulk inserts tasks into the database within a single transaction.
2091
+ * Pre-deduplicates against existing task_codes before the transaction.
2092
+ * Rows whose task_code already exists in the DB are silently skipped (skip-and-continue).
2093
+ * The per-row INSERT has no try/catch since all rows are pre-validated.
2094
+ *
2095
+ * @param tasks - Array of tasks to insert
2096
+ * @returns Number of tasks actually inserted (excluding skipped duplicates)
2097
+ */
2102
2098
  bulkInsertTasks(tasks) {
2099
+ if (tasks.length === 0) return 0;
2100
+ const owner = tasks[0].owner || "";
2101
+ const repo = tasks[0].repo;
2102
+ const codes = tasks.map((t) => t.task_code);
2103
+ const existingCodes = this.getExistingTaskCodes(owner, repo, codes);
2103
2104
  return this.transaction(() => {
2104
2105
  let count = 0;
2105
2106
  for (const task of tasks) {
2107
+ if (existingCodes.has(task.task_code)) {
2108
+ continue;
2109
+ }
2106
2110
  this.run(
2107
2111
  `INSERT INTO tasks (
2108
2112
  id, repo, owner, task_code, phase, title, description, status, priority,
@@ -3854,10 +3858,20 @@ var TaskUpdateSchema = z.object({
3854
3858
  }).refine((data) => Object.keys(data).length > 2, {
3855
3859
  message: "At least one field besides repo and id/ids must be provided for update"
3856
3860
  });
3861
+ var TaskStatusValues = TaskStatusSchema.options;
3862
+ var TaskStatusListSchema = z.string().refine(
3863
+ (val) => {
3864
+ if (val === "all") return true;
3865
+ const parts = val.split(",").map((s) => s.trim()).filter(Boolean);
3866
+ if (parts.length === 0) return false;
3867
+ return parts.every((p) => TaskStatusValues.includes(p));
3868
+ },
3869
+ { message: "status must be 'all' or a comma-separated list of valid TaskStatus values" }
3870
+ );
3857
3871
  var TaskListSchema = z.object({
3858
3872
  owner: z.string().min(1),
3859
3873
  repo: z.string().min(1).transform(normalizeRepo),
3860
- status: z.string().optional(),
3874
+ status: TaskStatusListSchema.default("backlog,pending,in_progress,blocked"),
3861
3875
  phase: z.string().optional(),
3862
3876
  query: z.string().optional(),
3863
3877
  limit: z.number().min(1).max(100).default(15),
@@ -4076,7 +4090,7 @@ var TOOL_DEFINITIONS = [
4076
4090
  properties: {
4077
4091
  owner: {
4078
4092
  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."
4093
+ description: "Organization/namespace (e.g., GitHub org or username)"
4080
4094
  },
4081
4095
  repo: { type: "string", description: "Repository/project name. Optional when a single MCP root is active." },
4082
4096
  objective: { type: "string", minLength: 5, description: "Question or synthesis objective." },
@@ -4124,10 +4138,7 @@ var TOOL_DEFINITIONS = [
4124
4138
  inputSchema: {
4125
4139
  type: "object",
4126
4140
  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
- },
4141
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4131
4142
  repo: {
4132
4143
  type: "string",
4133
4144
  description: "Repository name. Optional when it can be inferred from MCP roots or elicited from the user."
@@ -4200,10 +4211,7 @@ var TOOL_DEFINITIONS = [
4200
4211
  inputSchema: {
4201
4212
  type: "object",
4202
4213
  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
- },
4214
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4207
4215
  repo: { type: "string", description: "Repository name" },
4208
4216
  id: { type: "string", format: "uuid", description: "Task ID (optional if task_code is provided)" },
4209
4217
  task_code: { type: "string", description: "Task code (e.g. TASK-001) (optional if id is provided)" },
@@ -4266,10 +4274,7 @@ var TOOL_DEFINITIONS = [
4266
4274
  scope: {
4267
4275
  type: "object",
4268
4276
  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
- },
4277
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4273
4278
  repo: { type: "string", description: "Repository/project name" },
4274
4279
  branch: { type: "string", description: "Git branch this memory relates to" },
4275
4280
  folder: { type: "string", description: "Subdirectory within the repo" },
@@ -4318,10 +4323,7 @@ var TOOL_DEFINITIONS = [
4318
4323
  scope: {
4319
4324
  type: "object",
4320
4325
  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
- },
4326
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4325
4327
  repo: { type: "string", description: "Repository/project name" },
4326
4328
  branch: { type: "string", description: "Git branch this memory relates to" },
4327
4329
  folder: { type: "string", description: "Subdirectory within the repo" },
@@ -4462,10 +4464,7 @@ var TOOL_DEFINITIONS = [
4462
4464
  description: "Search keyword to match against memory titles and content"
4463
4465
  },
4464
4466
  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
- },
4467
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4469
4468
  repo: { type: "string", description: "Repository/project name" },
4470
4469
  current_tags: {
4471
4470
  type: "array",
@@ -4493,10 +4492,7 @@ var TOOL_DEFINITIONS = [
4493
4492
  scope: {
4494
4493
  type: "object",
4495
4494
  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
- },
4495
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4500
4496
  repo: { type: "string", description: "Repository/project name" },
4501
4497
  branch: { type: "string", description: "Git branch filter" },
4502
4498
  folder: { type: "string", description: "Subdirectory filter" },
@@ -4552,10 +4548,7 @@ var TOOL_DEFINITIONS = [
4552
4548
  inputSchema: {
4553
4549
  type: "object",
4554
4550
  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
- },
4551
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4559
4552
  repo: { type: "string", description: "Repository/project name" },
4560
4553
  signals: {
4561
4554
  type: "array",
@@ -4684,10 +4677,7 @@ var TOOL_DEFINITIONS = [
4684
4677
  inputSchema: {
4685
4678
  type: "object",
4686
4679
  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
- },
4680
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4691
4681
  repo: { type: "string", description: "Repository/project name (required)" },
4692
4682
  limit: {
4693
4683
  type: "number",
@@ -4761,10 +4751,7 @@ var TOOL_DEFINITIONS = [
4761
4751
  inputSchema: {
4762
4752
  type: "object",
4763
4753
  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
- },
4754
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4768
4755
  repo: { type: "string", description: "Repository/project name" },
4769
4756
  task_code: { type: "string", description: "Unique task code (e.g. TASK-001) (Required for single task)" },
4770
4757
  phase: { type: "string", description: "Project phase (Required for single task)" },
@@ -4877,10 +4864,7 @@ var TOOL_DEFINITIONS = [
4877
4864
  inputSchema: {
4878
4865
  type: "object",
4879
4866
  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
- },
4867
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4884
4868
  repo: { type: "string", description: "Repository name" },
4885
4869
  id: { type: "string", format: "uuid", description: "Task ID (for single update)" },
4886
4870
  ids: { type: "array", items: { type: "string", format: "uuid" }, description: "Task IDs (for bulk update)" },
@@ -4973,10 +4957,7 @@ var TOOL_DEFINITIONS = [
4973
4957
  inputSchema: {
4974
4958
  type: "object",
4975
4959
  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
- },
4960
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4980
4961
  repo: { type: "string", description: "Repository name" },
4981
4962
  id: {
4982
4963
  type: "string",
@@ -5005,7 +4986,7 @@ var TOOL_DEFINITIONS = [
5005
4986
  {
5006
4987
  name: "task-list",
5007
4988
  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.",
4989
+ 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
4990
  annotations: {
5010
4991
  readOnlyHint: true,
5011
4992
  idempotentHint: true,
@@ -5014,17 +4995,14 @@ var TOOL_DEFINITIONS = [
5014
4995
  inputSchema: {
5015
4996
  type: "object",
5016
4997
  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
- },
4998
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5021
4999
  repo: {
5022
5000
  type: "string",
5023
5001
  description: "Repository/project name (required)"
5024
5002
  },
5025
5003
  status: {
5026
5004
  type: "string",
5027
- default: "in_progress,pending",
5005
+ default: "backlog,pending,in_progress,blocked",
5028
5006
  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
5007
  },
5030
5008
  phase: {
@@ -5094,10 +5072,7 @@ var TOOL_DEFINITIONS = [
5094
5072
  inputSchema: {
5095
5073
  type: "object",
5096
5074
  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
- },
5075
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5101
5076
  repo: { type: "string", description: "Repository/project name" },
5102
5077
  query: {
5103
5078
  type: "string",
@@ -5155,10 +5130,7 @@ var TOOL_DEFINITIONS = [
5155
5130
  inputSchema: {
5156
5131
  type: "object",
5157
5132
  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
- },
5133
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5162
5134
  repo: { type: "string", description: "Repository/project name" },
5163
5135
  from_agent: { type: "string", description: "Agent creating the handoff" },
5164
5136
  to_agent: { type: "string", description: "Optional target agent" },
@@ -5233,10 +5205,7 @@ var TOOL_DEFINITIONS = [
5233
5205
  inputSchema: {
5234
5206
  type: "object",
5235
5207
  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
- },
5208
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5240
5209
  repo: { type: "string", description: "Repository/project name" },
5241
5210
  status: { type: "string", enum: ["pending", "accepted", "rejected", "expired"] },
5242
5211
  from_agent: { type: "string" },
@@ -5286,10 +5255,7 @@ var TOOL_DEFINITIONS = [
5286
5255
  inputSchema: {
5287
5256
  type: "object",
5288
5257
  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
- },
5258
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5293
5259
  repo: { type: "string", description: "Repository/project name" },
5294
5260
  task_id: {
5295
5261
  type: "string",
@@ -5333,10 +5299,7 @@ var TOOL_DEFINITIONS = [
5333
5299
  inputSchema: {
5334
5300
  type: "object",
5335
5301
  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
- },
5302
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5340
5303
  repo: { type: "string", description: "Repository/project name" },
5341
5304
  agent: { type: "string", description: "Optional agent filter" },
5342
5305
  active_only: { type: "boolean", description: "When true, return only unreleased claims" },
@@ -5385,10 +5348,7 @@ var TOOL_DEFINITIONS = [
5385
5348
  inputSchema: {
5386
5349
  type: "object",
5387
5350
  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
- },
5351
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5392
5352
  repo: { type: "string", description: "Repository name" },
5393
5353
  task_id: {
5394
5354
  type: "string",
@@ -5426,10 +5386,7 @@ var TOOL_DEFINITIONS = [
5426
5386
  inputSchema: {
5427
5387
  type: "object",
5428
5388
  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
- },
5389
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5433
5390
  name: { type: "string", minLength: 3, maxLength: 255, description: "Human-readable standard name" },
5434
5391
  content: {
5435
5392
  type: "string",
@@ -6413,7 +6370,7 @@ async function handleTaskClaim(args, storage) {
6413
6370
  const { owner, repo, task_id, task_code, agent, role, metadata, structured } = validated;
6414
6371
  let taskId = task_id;
6415
6372
  let resolvedTaskCode;
6416
- let task = null;
6373
+ let task;
6417
6374
  if (taskId) {
6418
6375
  task = storage.tasks.getTaskById(taskId);
6419
6376
  if (!task || task.repo !== repo) {
@@ -16,7 +16,7 @@ import {
16
16
  handleTaskClaim,
17
17
  listResources,
18
18
  logger
19
- } from "../chunk-OLPQSNH4.js";
19
+ } from "../chunk-XZHWHX2U.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-OLPQSNH4.js";
66
+ } from "../chunk-XZHWHX2U.js";
67
67
 
68
68
  // src/mcp/server.ts
69
69
  import readline from "readline";
@@ -941,16 +941,7 @@ Comments & History:
941
941
  }
942
942
  async function handleTaskList(args, storage) {
943
943
  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;
944
+ const { owner, repo, status, phase, query, limit, offset, structured: isStructuredRequest = false } = validated;
954
945
  let statuses = [];
955
946
  if (status !== "all") {
956
947
  statuses = status.split(",").map((s) => s.trim()).filter(Boolean);
@@ -1307,6 +1298,14 @@ async function handleTaskUpdate(args, storage, vectors2) {
1307
1298
  if (updates.est_tokens === void 0) {
1308
1299
  throw new Error("est_tokens is required when changing task status to completed");
1309
1300
  }
1301
+ const children = storage.tasks.getChildrenByParentId(targetId);
1302
+ const incompleteChildren = children.filter((c) => c.status !== "completed");
1303
+ if (incompleteChildren.length > 0) {
1304
+ const childList = incompleteChildren.map((c) => `[${c.task_code}] ${c.title} (${c.status})`).join("; ");
1305
+ throw new Error(
1306
+ `Cannot complete task [${existingTask.task_code}] "${existingTask.title}" \u2014 it has ${incompleteChildren.length} incomplete child task(s). Complete the following child task(s) first: ${childList}`
1307
+ );
1308
+ }
1310
1309
  }
1311
1310
  if (updates.task_code && storage.tasks.isTaskCodeDuplicate(owner, repo, updates.task_code, targetId)) {
1312
1311
  throw new Error(`Duplicate task_code: '${updates.task_code}' already exists`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vheins/local-memory-mcp",
3
- "version": "0.18.7",
3
+ "version": "0.18.9",
4
4
  "description": "MCP Local Memory Service for coding copilot agents",
5
5
  "mcpName": "io.github.vheins/local-memory-mcp",
6
6
  "type": "module",