@vheins/local-memory-mcp 0.14.10 → 0.15.1

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.14.10") {
85
- pkgVersion = "0.14.10";
84
+ if ("0.15.1") {
85
+ pkgVersion = "0.15.1";
86
86
  } else {
87
87
  let searchDir = __dirname2;
88
88
  for (let i = 0; i < 5; i++) {
@@ -585,6 +585,11 @@ var MigrationManager = class {
585
585
  name: "changed_files",
586
586
  table: "tasks",
587
587
  definition: "ALTER TABLE tasks ADD COLUMN changed_files TEXT"
588
+ },
589
+ {
590
+ name: "suggested_skills",
591
+ table: "tasks",
592
+ definition: "ALTER TABLE tasks ADD COLUMN suggested_skills TEXT"
588
593
  }
589
594
  ];
590
595
  for (const col of columnsToAdd) {
@@ -1074,6 +1079,7 @@ var BaseEntity = class {
1074
1079
  commit_id: r.commit_id || null,
1075
1080
  changed_files: this.safeJSONParse(r.changed_files, []),
1076
1081
  tags: this.safeJSONParse(r.tags, []),
1082
+ suggested_skills: this.safeJSONParse(r.suggested_skills, []),
1077
1083
  metadata: this.safeJSONParse(r.metadata, {}),
1078
1084
  parent_id: r.parent_id || null,
1079
1085
  depends_on: r.depends_on || null,
@@ -1615,9 +1621,9 @@ var TaskEntity = class extends BaseEntity {
1615
1621
  this.run(
1616
1622
  `INSERT INTO tasks (
1617
1623
  id, repo, task_code, phase, title, description, status, priority,
1618
- agent, role, doc_path, created_at, updated_at, finished_at, canceled_at, tags, metadata, parent_id, depends_on, est_tokens, in_progress_at,
1624
+ agent, role, doc_path, created_at, updated_at, finished_at, canceled_at, tags, suggested_skills, metadata, parent_id, depends_on, est_tokens, in_progress_at,
1619
1625
  commit_id, changed_files
1620
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1626
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1621
1627
  [
1622
1628
  task.id,
1623
1629
  task.repo,
@@ -1635,6 +1641,7 @@ var TaskEntity = class extends BaseEntity {
1635
1641
  task.finished_at || null,
1636
1642
  task.canceled_at || null,
1637
1643
  task.tags ? JSON.stringify(task.tags) : null,
1644
+ task.suggested_skills ? JSON.stringify(task.suggested_skills) : null,
1638
1645
  task.metadata ? JSON.stringify(task.metadata) : null,
1639
1646
  task.parent_id || null,
1640
1647
  task.depends_on || null,
@@ -1663,6 +1670,7 @@ var TaskEntity = class extends BaseEntity {
1663
1670
  "finished_at",
1664
1671
  "canceled_at",
1665
1672
  "tags",
1673
+ "suggested_skills",
1666
1674
  "metadata",
1667
1675
  "parent_id",
1668
1676
  "depends_on",
@@ -1673,7 +1681,7 @@ var TaskEntity = class extends BaseEntity {
1673
1681
  ]);
1674
1682
  Object.keys(updates).forEach((key) => {
1675
1683
  if (VALID_COLUMNS2.has(key) && anyUpdates[key] !== void 0) {
1676
- if (key === "tags" || key === "metadata" || key === "changed_files") {
1684
+ if (key === "tags" || key === "metadata" || key === "changed_files" || key === "suggested_skills") {
1677
1685
  fields.push(`${key} = ?`);
1678
1686
  values.push(JSON.stringify(anyUpdates[key]));
1679
1687
  } else {
@@ -1702,7 +1710,13 @@ var TaskEntity = class extends BaseEntity {
1702
1710
  WHERE t.id = ?`,
1703
1711
  [id]
1704
1712
  );
1705
- return row ? { ...this.rowToTask(row), comments: this.all("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at DESC, id DESC", [id]) } : null;
1713
+ return row ? {
1714
+ ...this.rowToTask(row),
1715
+ comments: this.all(
1716
+ "SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at DESC, id DESC",
1717
+ [id]
1718
+ )
1719
+ } : null;
1706
1720
  }
1707
1721
  getTasksByIds(ids) {
1708
1722
  if (ids.length === 0) return [];
@@ -1743,7 +1757,13 @@ var TaskEntity = class extends BaseEntity {
1743
1757
  WHERE t.repo = ? AND t.task_code = ?`,
1744
1758
  [repo, taskCode]
1745
1759
  );
1746
- return row ? { ...this.rowToTask(row), comments: this.all("SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at DESC, id DESC", [row.id]) } : null;
1760
+ return row ? {
1761
+ ...this.rowToTask(row),
1762
+ comments: this.all(
1763
+ "SELECT * FROM task_comments WHERE task_id = ? ORDER BY created_at DESC, id DESC",
1764
+ [row.id]
1765
+ )
1766
+ } : null;
1747
1767
  }
1748
1768
  getTasksByRepo(repo, status, limit, offset, search) {
1749
1769
  let query = `
@@ -1904,9 +1924,9 @@ var TaskEntity = class extends BaseEntity {
1904
1924
  this.run(
1905
1925
  `INSERT INTO tasks (
1906
1926
  id, repo, task_code, phase, title, description, status, priority,
1907
- agent, role, doc_path, created_at, updated_at, finished_at, canceled_at, tags, metadata, parent_id, depends_on, est_tokens, in_progress_at,
1927
+ agent, role, doc_path, created_at, updated_at, finished_at, canceled_at, tags, suggested_skills, metadata, parent_id, depends_on, est_tokens, in_progress_at,
1908
1928
  commit_id, changed_files
1909
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1929
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
1910
1930
  [
1911
1931
  task.id,
1912
1932
  task.repo,
@@ -1924,6 +1944,7 @@ var TaskEntity = class extends BaseEntity {
1924
1944
  task.finished_at || null,
1925
1945
  task.canceled_at || null,
1926
1946
  task.tags ? JSON.stringify(task.tags) : null,
1947
+ task.suggested_skills ? JSON.stringify(task.suggested_skills) : null,
1927
1948
  task.metadata ? JSON.stringify(task.metadata) : null,
1928
1949
  task.parent_id || null,
1929
1950
  task.depends_on || null,
@@ -3350,10 +3371,15 @@ var MemoryStoreSchema = z.object({
3350
3371
  is_global: z.boolean().default(false),
3351
3372
  structured: z.boolean().default(false),
3352
3373
  memories: z.array(SingleMemorySchema).min(1).optional()
3353
- }).refine((data) => {
3354
- if (data.memories) return true;
3355
- return !!(data.type && data.title && data.content && data.importance && data.agent && data.model && data.scope);
3356
- }, { message: "Either 'memories' array or single memory fields (type, title, content, importance, agent, model, scope) must be provided" });
3374
+ }).refine(
3375
+ (data) => {
3376
+ if (data.memories) return true;
3377
+ return !!(data.type && data.title && data.content && data.importance && data.agent && data.model && data.scope);
3378
+ },
3379
+ {
3380
+ message: "Either 'memories' array or single memory fields (type, title, content, importance, agent, model, scope) must be provided"
3381
+ }
3382
+ );
3357
3383
  var MemoryUpdateSchema = z.object({
3358
3384
  id: z.string().uuid().optional(),
3359
3385
  code: z.string().max(20).optional(),
@@ -3438,7 +3464,7 @@ var MemorySynthesizeSchema = z.object({
3438
3464
  var TaskStatusSchema = z.enum(["backlog", "pending", "in_progress", "completed", "canceled", "blocked"]);
3439
3465
  var TaskPrioritySchema = z.number().min(1).max(5);
3440
3466
  var SingleTaskCreateSchema = z.object({
3441
- task_code: z.string().min(1),
3467
+ task_code: z.string().min(1).optional(),
3442
3468
  phase: z.string().min(1),
3443
3469
  title: z.string().min(3).max(100),
3444
3470
  description: z.string().min(1),
@@ -3448,6 +3474,7 @@ var SingleTaskCreateSchema = z.object({
3448
3474
  role: z.string().optional(),
3449
3475
  doc_path: z.string().optional(),
3450
3476
  tags: z.array(z.string()).optional(),
3477
+ suggested_skills: z.array(z.string()).optional(),
3451
3478
  metadata: z.record(z.string(), z.any()).optional(),
3452
3479
  parent_id: z.string().optional(),
3453
3480
  depends_on: z.string().optional(),
@@ -3466,6 +3493,7 @@ var TaskCreateSchema = z.object({
3466
3493
  role: z.string().optional(),
3467
3494
  doc_path: z.string().optional(),
3468
3495
  tags: z.array(z.string()).optional(),
3496
+ suggested_skills: z.array(z.string()).optional(),
3469
3497
  metadata: z.record(z.string(), z.any()).optional(),
3470
3498
  parent_id: z.string().optional(),
3471
3499
  depends_on: z.string().optional(),
@@ -3476,9 +3504,9 @@ var TaskCreateSchema = z.object({
3476
3504
  }).refine(
3477
3505
  (data) => {
3478
3506
  if (data.tasks) return true;
3479
- return !!(data.task_code && data.phase && data.title && data.description);
3507
+ return !!(data.phase && data.title && data.description);
3480
3508
  },
3481
- { message: "Either 'tasks' array or single task fields (task_code, phase, title, description) must be provided" }
3509
+ { message: "Either 'tasks' array or single task fields (phase, title, description) must be provided" }
3482
3510
  );
3483
3511
  var TaskCreateInteractiveSchema = SingleTaskCreateSchema.partial().extend({
3484
3512
  repo: z.string().min(1).transform(normalizeRepo).optional(),
@@ -3501,6 +3529,7 @@ var TaskUpdateSchema = z.object({
3501
3529
  doc_path: z.string().optional(),
3502
3530
  tags: z.array(z.string()).optional(),
3503
3531
  metadata: z.record(z.string(), z.any()).optional(),
3532
+ suggested_skills: z.array(z.string()).optional(),
3504
3533
  parent_id: z.string().optional(),
3505
3534
  depends_on: z.string().optional(),
3506
3535
  est_tokens: z.number().int().min(0).optional(),
@@ -3657,13 +3686,15 @@ var StandardStoreSchema = z.object({
3657
3686
  model: z.string().optional(),
3658
3687
  structured: z.boolean().default(false),
3659
3688
  standards: z.array(SingleStandardSchema).min(1).optional()
3660
- }).refine((data) => {
3661
- if (data.standards) return true;
3662
- return !!(data.name && data.content && data.tags && data.metadata);
3663
- }, { message: "Either 'standards' array or single standard fields (name, content, tags, metadata) must be provided" }).refine(
3664
- (data) => data.is_global !== false || !!data.repo,
3665
- { message: "repo is required for repo-specific standards" }
3666
- );
3689
+ }).refine(
3690
+ (data) => {
3691
+ if (data.standards) return true;
3692
+ return !!(data.name && data.content && data.tags && data.metadata);
3693
+ },
3694
+ { message: "Either 'standards' array or single standard fields (name, content, tags, metadata) must be provided" }
3695
+ ).refine((data) => data.is_global !== false || !!data.repo, {
3696
+ message: "repo is required for repo-specific standards"
3697
+ });
3667
3698
  var StandardUpdateSchema = z.object({
3668
3699
  id: z.string().uuid().optional(),
3669
3700
  code: z.string().max(20).optional(),
@@ -3919,7 +3950,10 @@ var TOOL_DEFINITIONS = [
3919
3950
  description: "If true, this memory is shared across all repositories"
3920
3951
  },
3921
3952
  ttlDays: { type: "number", minimum: 1 },
3922
- supersedes: { type: "string", description: "Optional memory ID (UUID) or memory code to supersede. Resolved before storing." },
3953
+ supersedes: {
3954
+ type: "string",
3955
+ description: "Optional memory ID (UUID) or memory code to supersede. Resolved before storing."
3956
+ },
3923
3957
  memories: {
3924
3958
  type: "array",
3925
3959
  items: {
@@ -4384,7 +4418,10 @@ var TOOL_DEFINITIONS = [
4384
4418
  type: "string",
4385
4419
  description: "Optional parent task ID (UUID) or parent task code (e.g. TASK-001). Resolved to UUID before storing."
4386
4420
  },
4387
- depends_on: { type: "string", description: "Optional task ID (UUID) or task code (e.g. TASK-001). Resolved to UUID before storing." },
4421
+ depends_on: {
4422
+ type: "string",
4423
+ description: "Optional task ID (UUID) or task code (e.g. TASK-001). Resolved to UUID before storing."
4424
+ },
4388
4425
  est_tokens: { type: "number", minimum: 0, description: "Estimated tokens budget for this task" },
4389
4426
  tasks: {
4390
4427
  type: "array",
@@ -4415,7 +4452,10 @@ var TOOL_DEFINITIONS = [
4415
4452
  type: "string",
4416
4453
  description: "Optional parent task ID (UUID) or parent task code (e.g. TASK-001). Resolved to UUID before storing."
4417
4454
  },
4418
- depends_on: { type: "string", description: "Optional task ID (UUID) or task code (e.g. TASK-001). Resolved to UUID before storing." },
4455
+ depends_on: {
4456
+ type: "string",
4457
+ description: "Optional task ID (UUID) or task code (e.g. TASK-001). Resolved to UUID before storing."
4458
+ },
4419
4459
  est_tokens: { type: "number", minimum: 0 }
4420
4460
  },
4421
4461
  required: ["task_code", "phase", "title", "description"]
@@ -4490,7 +4530,10 @@ var TOOL_DEFINITIONS = [
4490
4530
  type: "string",
4491
4531
  description: "Optional parent task ID (UUID) or parent task code (e.g. TASK-001). Resolved to UUID before storing."
4492
4532
  },
4493
- depends_on: { type: "string", description: "Optional task ID (UUID) or task code (e.g. TASK-001). Resolved to UUID before storing." },
4533
+ depends_on: {
4534
+ type: "string",
4535
+ description: "Optional task ID (UUID) or task code (e.g. TASK-001). Resolved to UUID before storing."
4536
+ },
4494
4537
  est_tokens: {
4495
4538
  type: "number",
4496
4539
  minimum: 0,
@@ -4658,7 +4701,11 @@ var TOOL_DEFINITIONS = [
4658
4701
  type: "object",
4659
4702
  properties: {
4660
4703
  repo: { type: "string", description: "Repository name" },
4661
- query: { type: "string", minLength: 1, description: "Search keyword matching task code, title, or description" },
4704
+ query: {
4705
+ type: "string",
4706
+ minLength: 1,
4707
+ description: "Search keyword matching task code, title, or description"
4708
+ },
4662
4709
  status: { type: "string", description: "Optional status filter (single or comma-separated)" },
4663
4710
  phase: { type: "string", description: "Filter by phase (e.g., 'research', 'implementation')" },
4664
4711
  priority: { type: "number", minimum: 1, maximum: 5, description: "Filter by priority (1-5)" },
@@ -16,7 +16,7 @@ import {
16
16
  handleTaskClaim,
17
17
  listResources,
18
18
  logger
19
- } from "../chunk-BBPQUVAA.js";
19
+ } from "../chunk-NTU2HEEH.js";
20
20
 
21
21
  // src/dashboard/server.ts
22
22
  import express from "express";
@@ -60,7 +60,7 @@ import {
60
60
  toContextSlug,
61
61
  updateSessionFromInitialize,
62
62
  updateSessionRoots
63
- } from "../chunk-BBPQUVAA.js";
63
+ } from "../chunk-NTU2HEEH.js";
64
64
 
65
65
  // src/mcp/server.ts
66
66
  import readline from "readline";
@@ -189,19 +189,44 @@ function invalidCompletionParams(message) {
189
189
 
190
190
  // src/mcp/tools/memory.store.ts
191
191
  import { randomUUID } from "crypto";
192
+
193
+ // src/mcp/utils/code-generator.ts
194
+ var ENTITY_CONFIG = {
195
+ task: { prefix: "TASK", table: "tasks", column: "task_code" },
196
+ memory: { prefix: "MEM", table: "memories", column: "code" },
197
+ standard: { prefix: "STD", table: "coding_standards", column: "code" }
198
+ };
199
+ function generateNextCode(repo, entityType, storage, batchCodes) {
200
+ const config = ENTITY_CONFIG[entityType];
201
+ const pattern = `${config.prefix}-%`;
202
+ const offset = config.prefix.length + 2;
203
+ const row = storage.db.prepare(
204
+ `
205
+ SELECT MAX(CAST(SUBSTR(${config.column}, ?) AS INTEGER)) as max_seq
206
+ FROM ${config.table}
207
+ WHERE repo = ? AND ${config.column} LIKE ?
208
+ `
209
+ ).get(offset, repo, pattern);
210
+ let nextSeq = (row?.max_seq ?? 0) + 1;
211
+ if (batchCodes) {
212
+ for (const code of batchCodes) {
213
+ if (code.startsWith(`${config.prefix}-`)) {
214
+ const num = parseInt(code.slice(config.prefix.length + 1), 10);
215
+ if (!isNaN(num) && num >= nextSeq) {
216
+ nextSeq = num + 1;
217
+ }
218
+ }
219
+ }
220
+ }
221
+ return `${config.prefix}-${String(nextSeq).padStart(3, "0")}`;
222
+ }
223
+
224
+ // src/mcp/tools/memory.store.ts
192
225
  var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
193
226
  function hasMetadataLikeTitle(title) {
194
227
  const normalized = title.trim();
195
228
  return /^\[[^\]]{0,200}(agent:|role:|model:|\d{4}-\d{2}-\d{2}|source_)[^\]]*\]/i.test(normalized);
196
229
  }
197
- function generateShortCode() {
198
- const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
199
- let code = "";
200
- for (let i = 0; i < 6; i++) {
201
- code += chars.charAt(Math.floor(Math.random() * chars.length));
202
- }
203
- return code;
204
- }
205
230
  function resolveMemorySupersedes(value, db2) {
206
231
  if (!value) return null;
207
232
  if (UUID_REGEX.test(value)) return value;
@@ -252,7 +277,7 @@ async function storeSingleMemory(params, db2, vectors2) {
252
277
  }
253
278
  const entry = {
254
279
  id: randomUUID(),
255
- code: params.code || generateShortCode(),
280
+ code: params.code || generateNextCode(params.scope.repo, "memory", db2),
256
281
  type: params.type,
257
282
  title: params.title,
258
283
  content: params.content,
@@ -303,6 +328,7 @@ async function handleMemoryStore(params, db2, vectors2) {
303
328
  const now = (/* @__PURE__ */ new Date()).toISOString();
304
329
  const entries = [];
305
330
  const storedCodes = [];
331
+ const batchCodes = /* @__PURE__ */ new Set();
306
332
  for (const mem of validated.memories) {
307
333
  if (hasMetadataLikeTitle(mem.title)) {
308
334
  throw new Error(
@@ -313,13 +339,7 @@ async function handleMemoryStore(params, db2, vectors2) {
313
339
  const expires_at = mem.ttlDays != null ? new Date(createdAtTime + mem.ttlDays * 864e5).toISOString() : null;
314
340
  const resolvedSupersedes = resolveMemorySupersedes(mem.supersedes, db2);
315
341
  if (!resolvedSupersedes && mem.type !== "task_archive") {
316
- const conflict = await db2.memoryVectors.checkConflicts(
317
- mem.content,
318
- mem.scope.repo,
319
- mem.type,
320
- vectors2,
321
- 0.55
322
- );
342
+ const conflict = await db2.memoryVectors.checkConflicts(mem.content, mem.scope.repo, mem.type, vectors2, 0.55);
323
343
  if (conflict) {
324
344
  return createMcpResponse(
325
345
  {
@@ -343,7 +363,8 @@ async function handleMemoryStore(params, db2, vectors2) {
343
363
  if (mem.scope.language && !tags.includes(mem.scope.language.toLowerCase())) {
344
364
  tags.push(mem.scope.language.toLowerCase());
345
365
  }
346
- const code = mem.code || generateShortCode();
366
+ const code = mem.code || generateNextCode(mem.scope.repo, "memory", db2, batchCodes);
367
+ batchCodes.add(code);
347
368
  entries.push({
348
369
  id: randomUUID(),
349
370
  code,
@@ -380,7 +401,12 @@ async function handleMemoryStore(params, db2, vectors2) {
380
401
  }
381
402
  const codesStr = storedCodes.length > 0 ? `: ${storedCodes.join(", ")}` : "";
382
403
  return createMcpResponse(
383
- { success: true, repo: validated.memories[0]?.scope.repo, createdCount: validated.memories.length, codes: storedCodes },
404
+ {
405
+ success: true,
406
+ repo: validated.memories[0]?.scope.repo,
407
+ createdCount: validated.memories.length,
408
+ codes: storedCodes
409
+ },
384
410
  `Stored ${validated.memories.length} memories${codesStr}.`,
385
411
  { includeSerializedStructuredContent: validated.structured }
386
412
  );
@@ -756,16 +782,18 @@ function capitalize2(str) {
756
782
  // src/mcp/tools/task.manage.ts
757
783
  import { randomUUID as randomUUID2 } from "crypto";
758
784
  var UUID_REGEX3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
759
- function resolveParentId(value, repo, storage) {
785
+ function resolveParentId(value, repo, storage, localCodeMap) {
760
786
  if (!value) return null;
761
787
  if (UUID_REGEX3.test(value)) return value;
788
+ if (localCodeMap?.has(value)) return localCodeMap.get(value);
762
789
  const parent = storage.tasks.getTaskByCode(repo, value);
763
790
  if (!parent) throw new Error(`parent_id: task with code '${value}' not found in repo '${repo}'`);
764
791
  return parent.id;
765
792
  }
766
- function resolveDependsOn(value, repo, storage) {
793
+ function resolveDependsOn(value, repo, storage, localCodeMap) {
767
794
  if (!value) return null;
768
795
  if (UUID_REGEX3.test(value)) return value;
796
+ if (localCodeMap?.has(value)) return localCodeMap.get(value);
769
797
  const task = storage.tasks.getTaskByCode(repo, value);
770
798
  if (!task) throw new Error(`depends_on: task with code '${value}' not found in repo '${repo}'`);
771
799
  return task.id;
@@ -955,28 +983,38 @@ async function handleTaskCreate(args, storage) {
955
983
  const tasksToInsert = [];
956
984
  const now2 = (/* @__PURE__ */ new Date()).toISOString();
957
985
  const codesInRequest = /* @__PURE__ */ new Set();
986
+ const batchCodes = /* @__PURE__ */ new Set();
987
+ for (const taskData of bulkTasks) {
988
+ if (!taskData.task_code) {
989
+ taskData.task_code = generateNextCode(repo, "task", storage, batchCodes);
990
+ }
991
+ batchCodes.add(taskData.task_code);
992
+ }
958
993
  const allCodes = bulkTasks.map((t) => t.task_code);
959
994
  const existingCodes = storage.tasks.getExistingTaskCodes(repo, allCodes);
960
995
  const initialStats = storage.taskStats.getTaskStats(repo);
961
996
  let pendingInRequestCount = 0;
997
+ const localCodeMap = /* @__PURE__ */ new Map();
962
998
  for (const taskData of bulkTasks) {
963
- if (codesInRequest.has(taskData.task_code)) {
964
- throw new Error(`Duplicate task_code in request: '${taskData.task_code}'`);
999
+ localCodeMap.set(taskData.task_code, randomUUID2());
1000
+ }
1001
+ for (const taskData of bulkTasks) {
1002
+ const code = taskData.task_code;
1003
+ if (codesInRequest.has(code)) {
1004
+ throw new Error(`Duplicate task_code in request: '${code}'`);
965
1005
  }
966
- if (existingCodes.has(taskData.task_code)) {
967
- throw new Error(`Duplicate task_code: '${taskData.task_code}' already exists in repository '${repo}'`);
1006
+ if (existingCodes.has(code)) {
1007
+ throw new Error(`Duplicate task_code: '${code}' already exists in repository '${repo}'`);
968
1008
  }
969
- codesInRequest.add(taskData.task_code);
1009
+ codesInRequest.add(code);
970
1010
  const normalizedStatus = taskData.status || "backlog";
971
1011
  if (normalizedStatus !== "backlog" && normalizedStatus !== "pending") {
972
- throw new Error(
973
- `New tasks must be 'backlog' or 'pending'. Task '${taskData.task_code}' has status '${normalizedStatus}'.`
974
- );
1012
+ throw new Error(`New tasks must be 'backlog' or 'pending'. Task '${code}' has status '${normalizedStatus}'.`);
975
1013
  }
976
1014
  if (normalizedStatus === "pending") {
977
1015
  if (initialStats.todo + pendingInRequestCount >= 10) {
978
1016
  throw new Error(
979
- `Cannot create task '${taskData.task_code}' as 'pending'. Maximum of 10 pending tasks reached. Please use status 'backlog' for new tasks instead.`
1017
+ `Cannot create task '${code}' as 'pending'. Maximum of 10 pending tasks reached. Please use status 'backlog' for new tasks instead.`
980
1018
  );
981
1019
  }
982
1020
  }
@@ -986,10 +1024,11 @@ async function handleTaskCreate(args, storage) {
986
1024
  if (!tags.includes(phaseTag2)) {
987
1025
  tags.push(phaseTag2);
988
1026
  }
1027
+ const taskId2 = localCodeMap.get(code);
989
1028
  const task2 = {
990
- id: randomUUID2(),
1029
+ id: taskId2,
991
1030
  repo,
992
- task_code: taskData.task_code,
1031
+ task_code: code,
993
1032
  phase: taskData.phase,
994
1033
  title: taskData.title,
995
1034
  description: taskData.description,
@@ -1005,14 +1044,15 @@ async function handleTaskCreate(args, storage) {
1005
1044
  canceled_at: statusTimestamps2.canceled_at,
1006
1045
  est_tokens: taskData.est_tokens ?? 0,
1007
1046
  tags,
1047
+ suggested_skills: taskData.suggested_skills || [],
1008
1048
  commit_id: null,
1009
1049
  changed_files: [],
1010
1050
  metadata: taskData.metadata || {},
1011
- parent_id: resolveParentId(taskData.parent_id, repo, storage),
1012
- depends_on: resolveDependsOn(taskData.depends_on, repo, storage)
1051
+ parent_id: resolveParentId(taskData.parent_id, repo, storage, localCodeMap),
1052
+ depends_on: resolveDependsOn(taskData.depends_on, repo, storage, localCodeMap)
1013
1053
  };
1014
1054
  tasksToInsert.push(task2);
1015
- createdTasks.push(task2.task_code);
1055
+ createdTasks.push(code);
1016
1056
  if (normalizedStatus === "pending") {
1017
1057
  pendingInRequestCount++;
1018
1058
  }
@@ -1040,11 +1080,12 @@ async function handleTaskCreate(args, storage) {
1040
1080
  depends_on,
1041
1081
  est_tokens
1042
1082
  } = singleTask;
1043
- if (!task_code || !phase || !title || !description) {
1044
- throw new Error("Missing required fields for single task creation (task_code, phase, title, description)");
1083
+ if (!phase || !title || !description) {
1084
+ throw new Error("Missing required fields for single task creation (phase, title, description)");
1045
1085
  }
1046
- if (storage.tasks.isTaskCodeDuplicate(repo, task_code)) {
1047
- throw new Error(`Duplicate task_code: '${task_code}' already exists in repository '${repo}'`);
1086
+ const resolvedCode = task_code || generateNextCode(repo, "task", storage);
1087
+ if (storage.tasks.isTaskCodeDuplicate(repo, resolvedCode)) {
1088
+ throw new Error(`Duplicate task_code: '${resolvedCode}' already exists in repository '${repo}'`);
1048
1089
  }
1049
1090
  if (status !== "backlog" && status !== "pending" && status !== void 0) {
1050
1091
  throw new Error("New tasks must be created with status 'backlog' or 'pending'.");
@@ -1068,7 +1109,7 @@ async function handleTaskCreate(args, storage) {
1068
1109
  const task = {
1069
1110
  id: taskId,
1070
1111
  repo,
1071
- task_code,
1112
+ task_code: resolvedCode,
1072
1113
  phase,
1073
1114
  title,
1074
1115
  description,
@@ -1084,6 +1125,7 @@ async function handleTaskCreate(args, storage) {
1084
1125
  canceled_at: statusTimestamps.canceled_at,
1085
1126
  est_tokens: est_tokens ?? 0,
1086
1127
  tags: finalTags,
1128
+ suggested_skills: singleTask.suggested_skills || [],
1087
1129
  commit_id: null,
1088
1130
  changed_files: [],
1089
1131
  metadata: metadata || {},
@@ -1150,11 +1192,6 @@ function buildMissingTaskSchema(task) {
1150
1192
  description: "Name of the repository for this task.",
1151
1193
  minLength: 1
1152
1194
  });
1153
- addRequiredStringField(properties, required, task, "task_code", {
1154
- title: "Task Code",
1155
- description: "Unique task code in this repository.",
1156
- minLength: 1
1157
- });
1158
1195
  addRequiredStringField(properties, required, task, "phase", {
1159
1196
  title: "Phase",
1160
1197
  description: "Project phase or milestone for this task.",
@@ -1759,14 +1796,6 @@ async function handleMemoryDetail(args, storage) {
1759
1796
  // src/mcp/tools/standard.store.ts
1760
1797
  import { randomUUID as randomUUID3 } from "crypto";
1761
1798
  var UUID_REGEX4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
1762
- function generateShortCode2() {
1763
- const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ0123456789";
1764
- let code = "";
1765
- for (let i = 0; i < 6; i++) {
1766
- code += chars.charAt(Math.floor(Math.random() * chars.length));
1767
- }
1768
- return code;
1769
- }
1770
1799
  function resolveStandardParentId(value, db2) {
1771
1800
  if (!value) return null;
1772
1801
  if (UUID_REGEX4.test(value)) return value;
@@ -1808,7 +1837,7 @@ async function storeSingleStandard(params, db2, vectors2) {
1808
1837
  const now = (/* @__PURE__ */ new Date()).toISOString();
1809
1838
  const entry = {
1810
1839
  id: randomUUID3(),
1811
- code: generateShortCode2(),
1840
+ code: generateNextCode(params.repo || "__global__", "standard", db2),
1812
1841
  title: params.name,
1813
1842
  content: params.content,
1814
1843
  parent_id: resolveStandardParentId(params.parent_id, db2),
@@ -1851,6 +1880,8 @@ async function handleStandardStore(params, db2, vectors2) {
1851
1880
  if (validated.standards) {
1852
1881
  const entries = [];
1853
1882
  const storedCodes = [];
1883
+ const batchCodes = /* @__PURE__ */ new Set();
1884
+ const standardRepo = validated.repo || "__global__";
1854
1885
  for (const std of validated.standards) {
1855
1886
  const incomingVersion = std.version || "1.0.0";
1856
1887
  const incomingLanguage = std.language ?? null;
@@ -1883,7 +1914,8 @@ async function handleStandardStore(params, db2, vectors2) {
1883
1914
  );
1884
1915
  }
1885
1916
  const now = (/* @__PURE__ */ new Date()).toISOString();
1886
- const code = generateShortCode2();
1917
+ const code = generateNextCode(standardRepo, "standard", db2, batchCodes);
1918
+ batchCodes.add(code);
1887
1919
  entries.push({
1888
1920
  id: randomUUID3(),
1889
1921
  code,
@@ -2349,6 +2381,8 @@ async function handleTaskGet(args, storage) {
2349
2381
  ];
2350
2382
  if (task.phase) lines.push(`Phase: ${task.phase}`);
2351
2383
  if (task.description) lines.push(`Description: ${task.description}`);
2384
+ if (task.suggested_skills && task.suggested_skills.length > 0)
2385
+ lines.push(`Suggested Skills: ${task.suggested_skills.join(", ")}`);
2352
2386
  if (task.metadata) lines.push(`Metadata: ${JSON.stringify(task.metadata)}`);
2353
2387
  lines.push(`Created: ${task.created_at}`);
2354
2388
  if (task.updated_at) lines.push(`Updated: ${task.updated_at}`);
@@ -14,14 +14,15 @@ category: planning
14
14
  tags: [architecture, system-design, components, data-flow, adr]
15
15
  ---
16
16
 
17
- ## FSM
17
+ ## Architecture Design
18
18
 
19
- Entry=S0 → S1 → S2 Exit=done
19
+ Entry=S0 → S1 → S2 → S3 Exit=done
20
20
  Guard: S(N) req S(N-1)✅
21
21
 
22
22
  S0 | review tech_stack & requirements | — | component list, data flow map | —
23
23
  S1 | design: component diagram(blocks+responsibilities) + data flow(information movement) + ADRs(rationale) + scalability/reliability(growth+failure) + security(identity,protection,boundaries) | S0✅ | design decisions | —
24
24
  S2 | document artifacts | S1✅ | architecture docs | design/architecture/
25
+ S3 | verify: validate component completeness, data flow coherence, ADR traceability, security coverage, scalability assumptions | S2✅ | verified | —
25
26
 
26
27
  ## Chain
27
28
 
@@ -11,15 +11,16 @@ version: "1.0.0"
11
11
  tags: [workflow, task-creation, planning, mcp]
12
12
  ---
13
13
 
14
- ## FSM
14
+ ## Create Task
15
15
 
16
- Entry=S0 → S1 → S2 → S3 Exit=created
16
+ Entry=S0 → S1 → S2 → S3 → S4 Exit=created
17
17
  Guard: S(N) req S(N-1)✅; NO code/edit/delete — MCP tools ONLY (allowed: task-create, task-list, task-detail, task-update, memory-store, memory-search, standard-search, standard-store, handoff-list, handoff-update, read)
18
18
 
19
19
  S0 | pre_analysis: memory-search(architecture/history) + standard-search(if task leads to code/test/refactor/migrate decisions) + handoff-list(pending; close stale that describe completed work) + read code(verify paths+impl) + task-list dedup(DO NOT duplicate; link via parent_id/depends_on) | — | context | —
20
20
  S1 | design tasks: atomic(1 logical change), layered(DB/Service/State/UI), context(paths+symbols+APIs), min 1 pos+1 neg test | S0✅ | task specs | —
21
- S2 | assign attributes: task_code(FEAT/FIX/REFACTOR-XXX), phase(Discovery|Implementation|Testing), priority(1=Low..5=Critical), strict description format | S1✅ | task attrs | —
21
+ S2 | assign attributes: task_code(optional — auto-generated as TASK-xxx if omitted), phase(Discovery|Implementation|Testing), priority(1=Low..5=Critical), strict description format | S1✅ | task attrs | —
22
22
  S3 | create via task-create(bulk max 500) + log decisions via memory-store(arch/feature changes; skip simple bugs) | S2✅ | MCP tasks created | —
23
+ S4 | verify: validate task count, description format compliance, parent/depends_on integrity, no duplicates | S3✅ | verified | —
23
24
  G1 | blueprint? | src=idea-to-blueprint | → route blueprint flow | —
24
25
  G2 | sprint? | src=.agents/documents/tasks/sprints/ | → route sprint flow | —
25
26