@hasna/todos 0.11.93 → 0.11.95

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.
Files changed (37) hide show
  1. package/dist/cli/cloud-router.d.ts +12 -1
  2. package/dist/cli/cloud-router.d.ts.map +1 -1
  3. package/dist/cli/commands/plan-template-commands.d.ts.map +1 -1
  4. package/dist/cli/commands/task-commands.d.ts.map +1 -1
  5. package/dist/cli/index.js +885 -76
  6. package/dist/contracts.js +56 -50
  7. package/dist/db/storage-tombstones.d.ts +1 -1
  8. package/dist/db/storage-tombstones.d.ts.map +1 -1
  9. package/dist/db/templates.d.ts.map +1 -1
  10. package/dist/index.js +170 -56
  11. package/dist/lib/template-semantics.d.ts +8 -0
  12. package/dist/lib/template-semantics.d.ts.map +1 -0
  13. package/dist/mcp/index.js +605 -61
  14. package/dist/mcp/tools/task-crud.d.ts.map +1 -1
  15. package/dist/mcp/tools/task-meta-tools.d.ts.map +1 -1
  16. package/dist/mcp/tools/task-project-tools.d.ts.map +1 -1
  17. package/dist/mcp.js +1 -1
  18. package/dist/registry.js +56 -50
  19. package/dist/release-provenance.json +5 -5
  20. package/dist/sdk/index.js +35 -0
  21. package/dist/sdk/v1.generated.d.ts +105 -1
  22. package/dist/sdk/v1.generated.d.ts.map +1 -1
  23. package/dist/server/index.js +611 -67
  24. package/dist/server/openapi.d.ts +496 -0
  25. package/dist/server/openapi.d.ts.map +1 -1
  26. package/dist/server/v1.d.ts.map +1 -1
  27. package/dist/storage/interfaces.d.ts +3 -2
  28. package/dist/storage/interfaces.d.ts.map +1 -1
  29. package/dist/storage/postgres-adapter.d.ts.map +1 -1
  30. package/dist/storage/postgres-sync.d.ts +1 -1
  31. package/dist/storage/postgres-sync.d.ts.map +1 -1
  32. package/dist/storage/shadow-outbox.d.ts.map +1 -1
  33. package/dist/storage/shadow.d.ts +1 -1
  34. package/dist/storage/shadow.d.ts.map +1 -1
  35. package/dist/storage/sqlite-snapshot.d.ts.map +1 -1
  36. package/dist/storage.js +169 -55
  37. package/package.json +1 -1
package/dist/contracts.js CHANGED
@@ -6483,6 +6483,50 @@ var init_recurrence = __esm(() => {
6483
6483
  };
6484
6484
  });
6485
6485
 
6486
+ // src/lib/template-semantics.ts
6487
+ function resolveTemplateVariables(templateVars, provided) {
6488
+ const merged = { ...provided };
6489
+ for (const variable of templateVars) {
6490
+ if (merged[variable.name] === undefined && variable.default !== undefined) {
6491
+ merged[variable.name] = variable.default;
6492
+ }
6493
+ }
6494
+ const missing = templateVars.filter((variable) => variable.required && merged[variable.name] === undefined).map((variable) => variable.name);
6495
+ if (missing.length > 0) {
6496
+ throw new Error(`Missing required template variable(s): ${missing.join(", ")}`);
6497
+ }
6498
+ return merged;
6499
+ }
6500
+ function substituteTemplateVariables(text, variables) {
6501
+ let result = text;
6502
+ for (const [key, value] of Object.entries(variables)) {
6503
+ result = result.replace(new RegExp(`\\{${key}\\}`, "g"), value);
6504
+ }
6505
+ return result;
6506
+ }
6507
+ function evaluateTemplateCondition(condition, variables) {
6508
+ if (!condition || condition.trim() === "")
6509
+ return true;
6510
+ const trimmed = condition.trim();
6511
+ const equal = trimmed.match(/^\{([^}]+)\}\s*==\s*(.+)$/);
6512
+ if (equal)
6513
+ return (variables[equal[1]] ?? "") === equal[2].trim();
6514
+ const unequal = trimmed.match(/^\{([^}]+)\}\s*!=\s*(.+)$/);
6515
+ if (unequal)
6516
+ return (variables[unequal[1]] ?? "") !== unequal[2].trim();
6517
+ const falsy = trimmed.match(/^!\{([^}]+)\}$/);
6518
+ if (falsy) {
6519
+ const value = variables[falsy[1]];
6520
+ return !value || value === "" || value === "false";
6521
+ }
6522
+ const truthy = trimmed.match(/^\{([^}]+)\}$/);
6523
+ if (truthy) {
6524
+ const value = variables[truthy[1]];
6525
+ return !!value && value !== "" && value !== "false";
6526
+ }
6527
+ return true;
6528
+ }
6529
+
6486
6530
  // src/db/templates.ts
6487
6531
  function rowToTemplate(row) {
6488
6532
  return {
@@ -6558,6 +6602,14 @@ function deleteTemplate(id, db) {
6558
6602
  payload: template,
6559
6603
  version: template.version
6560
6604
  }, d);
6605
+ for (const task of getTemplateTasks(resolved, d)) {
6606
+ recordStorageTombstone({
6607
+ object_type: "template_tasks",
6608
+ object_id: task.id,
6609
+ payload: task,
6610
+ version: 1
6611
+ }, d);
6612
+ }
6561
6613
  return d.run("DELETE FROM task_templates WHERE id = ?", [resolved]).changes > 0;
6562
6614
  }
6563
6615
  function updateTemplate(id, updates, db) {
@@ -6689,34 +6741,7 @@ function getTemplateTasks(templateId, db) {
6689
6741
  return rows.map(rowToTemplateTask);
6690
6742
  }
6691
6743
  function evaluateCondition(condition, variables) {
6692
- if (!condition || condition.trim() === "")
6693
- return true;
6694
- const trimmed = condition.trim();
6695
- const eqMatch = trimmed.match(/^\{([^}]+)\}\s*==\s*(.+)$/);
6696
- if (eqMatch) {
6697
- const varName = eqMatch[1];
6698
- const expected = eqMatch[2].trim();
6699
- return (variables[varName] ?? "") === expected;
6700
- }
6701
- const neqMatch = trimmed.match(/^\{([^}]+)\}\s*!=\s*(.+)$/);
6702
- if (neqMatch) {
6703
- const varName = neqMatch[1];
6704
- const expected = neqMatch[2].trim();
6705
- return (variables[varName] ?? "") !== expected;
6706
- }
6707
- const falsyMatch = trimmed.match(/^!\{([^}]+)\}$/);
6708
- if (falsyMatch) {
6709
- const varName = falsyMatch[1];
6710
- const val = variables[varName];
6711
- return !val || val === "" || val === "false";
6712
- }
6713
- const truthyMatch = trimmed.match(/^\{([^}]+)\}$/);
6714
- if (truthyMatch) {
6715
- const varName = truthyMatch[1];
6716
- const val = variables[varName];
6717
- return !!val && val !== "" && val !== "false";
6718
- }
6719
- return true;
6744
+ return evaluateTemplateCondition(condition, variables);
6720
6745
  }
6721
6746
  function exportTemplate(id, db) {
6722
6747
  const d = db || getDatabase();
@@ -6789,29 +6814,10 @@ function listTemplateVersions(id, db) {
6789
6814
  return d.query("SELECT * FROM template_versions WHERE template_id = ? ORDER BY version DESC").all(resolved);
6790
6815
  }
6791
6816
  function resolveVariables(templateVars, provided) {
6792
- const merged = { ...provided };
6793
- for (const v of templateVars) {
6794
- if (merged[v.name] === undefined && v.default !== undefined) {
6795
- merged[v.name] = v.default;
6796
- }
6797
- }
6798
- const missing = [];
6799
- for (const v of templateVars) {
6800
- if (v.required && merged[v.name] === undefined) {
6801
- missing.push(v.name);
6802
- }
6803
- }
6804
- if (missing.length > 0) {
6805
- throw new Error(`Missing required template variable(s): ${missing.join(", ")}`);
6806
- }
6807
- return merged;
6817
+ return resolveTemplateVariables(templateVars, provided);
6808
6818
  }
6809
6819
  function substituteVars(text, variables) {
6810
- let result = text;
6811
- for (const [key, val] of Object.entries(variables)) {
6812
- result = result.replace(new RegExp(`\\{${key}\\}`, "g"), val);
6813
- }
6814
- return result;
6820
+ return substituteTemplateVariables(text, variables);
6815
6821
  }
6816
6822
  function tasksFromTemplate(templateId, projectId, variables, taskListId, db, _visitedTemplateIds) {
6817
6823
  const d = db || getDatabase();
@@ -10756,7 +10762,7 @@ var init_tasks = __esm(() => {
10756
10762
  // package.json
10757
10763
  var package_default = {
10758
10764
  name: "@hasna/todos",
10759
- version: "0.11.93",
10765
+ version: "0.11.95",
10760
10766
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
10761
10767
  type: "module",
10762
10768
  main: "dist/index.js",
@@ -1,5 +1,5 @@
1
1
  import type { Database } from "bun:sqlite";
2
- export type StorageTombstoneObjectType = "tasks" | "projects" | "project_machine_paths" | "plans" | "agents" | "task_lists" | "templates" | "audit_history";
2
+ export type StorageTombstoneObjectType = "tasks" | "projects" | "project_machine_paths" | "plans" | "agents" | "task_lists" | "templates" | "template_tasks" | "audit_history";
3
3
  export interface StorageTombstone {
4
4
  id: string;
5
5
  object_type: StorageTombstoneObjectType;
@@ -1 +1 @@
1
- {"version":3,"file":"storage-tombstones.d.ts","sourceRoot":"","sources":["../../src/db/storage-tombstones.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,MAAM,MAAM,0BAA0B,GAClC,OAAO,GACP,UAAU,GACV,uBAAuB,GACvB,OAAO,GACP,QAAQ,GACR,YAAY,GACZ,WAAW,GACX,eAAe,CAAC;AAEpB,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,0BAA0B,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,0BAA0B,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,2BAA2B,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,CA2B1G;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,0BAA0B,EACtC,QAAQ,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,GAAG,IAAI,CAMzB;AAED,wBAAgB,qBAAqB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,EAAE,CAKvE;AAED,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE,YAAY,GAAG,YAAY,CAAC,EAC9D,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,GAChC,OAAO,CAOT;AAgCD,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAMnE"}
1
+ {"version":3,"file":"storage-tombstones.d.ts","sourceRoot":"","sources":["../../src/db/storage-tombstones.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,MAAM,MAAM,0BAA0B,GAClC,OAAO,GACP,UAAU,GACV,uBAAuB,GACvB,OAAO,GACP,QAAQ,GACR,YAAY,GACZ,WAAW,GACX,gBAAgB,GAChB,eAAe,CAAC;AAEpB,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,0BAA0B,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,0BAA0B,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,2BAA2B,EAAE,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,CA2B1G;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,0BAA0B,EACtC,QAAQ,EAAE,MAAM,EAChB,EAAE,CAAC,EAAE,QAAQ,GACZ,gBAAgB,GAAG,IAAI,CAMzB;AAED,wBAAgB,qBAAqB,CAAC,EAAE,CAAC,EAAE,QAAQ,GAAG,gBAAgB,EAAE,CAKvE;AAED,wBAAgB,2BAA2B,CACzC,SAAS,EAAE,IAAI,CAAC,gBAAgB,EAAE,YAAY,GAAG,YAAY,CAAC,EAC9D,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,GAChC,OAAO,CAOT;AAgCD,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,GAAG,IAAI,CAMnE"}
@@ -1 +1 @@
1
- {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/db/templates.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"templates.d.ts","sourceRoot":"","sources":["../../src/db/templates.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -6483,6 +6483,50 @@ var init_recurrence = __esm(() => {
6483
6483
  };
6484
6484
  });
6485
6485
 
6486
+ // src/lib/template-semantics.ts
6487
+ function resolveTemplateVariables(templateVars, provided) {
6488
+ const merged = { ...provided };
6489
+ for (const variable of templateVars) {
6490
+ if (merged[variable.name] === undefined && variable.default !== undefined) {
6491
+ merged[variable.name] = variable.default;
6492
+ }
6493
+ }
6494
+ const missing = templateVars.filter((variable) => variable.required && merged[variable.name] === undefined).map((variable) => variable.name);
6495
+ if (missing.length > 0) {
6496
+ throw new Error(`Missing required template variable(s): ${missing.join(", ")}`);
6497
+ }
6498
+ return merged;
6499
+ }
6500
+ function substituteTemplateVariables(text, variables) {
6501
+ let result = text;
6502
+ for (const [key, value] of Object.entries(variables)) {
6503
+ result = result.replace(new RegExp(`\\{${key}\\}`, "g"), value);
6504
+ }
6505
+ return result;
6506
+ }
6507
+ function evaluateTemplateCondition(condition, variables) {
6508
+ if (!condition || condition.trim() === "")
6509
+ return true;
6510
+ const trimmed = condition.trim();
6511
+ const equal = trimmed.match(/^\{([^}]+)\}\s*==\s*(.+)$/);
6512
+ if (equal)
6513
+ return (variables[equal[1]] ?? "") === equal[2].trim();
6514
+ const unequal = trimmed.match(/^\{([^}]+)\}\s*!=\s*(.+)$/);
6515
+ if (unequal)
6516
+ return (variables[unequal[1]] ?? "") !== unequal[2].trim();
6517
+ const falsy = trimmed.match(/^!\{([^}]+)\}$/);
6518
+ if (falsy) {
6519
+ const value = variables[falsy[1]];
6520
+ return !value || value === "" || value === "false";
6521
+ }
6522
+ const truthy = trimmed.match(/^\{([^}]+)\}$/);
6523
+ if (truthy) {
6524
+ const value = variables[truthy[1]];
6525
+ return !!value && value !== "" && value !== "false";
6526
+ }
6527
+ return true;
6528
+ }
6529
+
6486
6530
  // src/db/templates.ts
6487
6531
  function rowToTemplate(row) {
6488
6532
  return {
@@ -6558,6 +6602,14 @@ function deleteTemplate(id, db) {
6558
6602
  payload: template,
6559
6603
  version: template.version
6560
6604
  }, d);
6605
+ for (const task of getTemplateTasks(resolved, d)) {
6606
+ recordStorageTombstone({
6607
+ object_type: "template_tasks",
6608
+ object_id: task.id,
6609
+ payload: task,
6610
+ version: 1
6611
+ }, d);
6612
+ }
6561
6613
  return d.run("DELETE FROM task_templates WHERE id = ?", [resolved]).changes > 0;
6562
6614
  }
6563
6615
  function updateTemplate(id, updates, db) {
@@ -6689,34 +6741,7 @@ function getTemplateTasks(templateId, db) {
6689
6741
  return rows.map(rowToTemplateTask);
6690
6742
  }
6691
6743
  function evaluateCondition(condition, variables) {
6692
- if (!condition || condition.trim() === "")
6693
- return true;
6694
- const trimmed = condition.trim();
6695
- const eqMatch = trimmed.match(/^\{([^}]+)\}\s*==\s*(.+)$/);
6696
- if (eqMatch) {
6697
- const varName = eqMatch[1];
6698
- const expected = eqMatch[2].trim();
6699
- return (variables[varName] ?? "") === expected;
6700
- }
6701
- const neqMatch = trimmed.match(/^\{([^}]+)\}\s*!=\s*(.+)$/);
6702
- if (neqMatch) {
6703
- const varName = neqMatch[1];
6704
- const expected = neqMatch[2].trim();
6705
- return (variables[varName] ?? "") !== expected;
6706
- }
6707
- const falsyMatch = trimmed.match(/^!\{([^}]+)\}$/);
6708
- if (falsyMatch) {
6709
- const varName = falsyMatch[1];
6710
- const val = variables[varName];
6711
- return !val || val === "" || val === "false";
6712
- }
6713
- const truthyMatch = trimmed.match(/^\{([^}]+)\}$/);
6714
- if (truthyMatch) {
6715
- const varName = truthyMatch[1];
6716
- const val = variables[varName];
6717
- return !!val && val !== "" && val !== "false";
6718
- }
6719
- return true;
6744
+ return evaluateTemplateCondition(condition, variables);
6720
6745
  }
6721
6746
  function exportTemplate(id, db) {
6722
6747
  const d = db || getDatabase();
@@ -6789,29 +6814,10 @@ function listTemplateVersions(id, db) {
6789
6814
  return d.query("SELECT * FROM template_versions WHERE template_id = ? ORDER BY version DESC").all(resolved);
6790
6815
  }
6791
6816
  function resolveVariables(templateVars, provided) {
6792
- const merged = { ...provided };
6793
- for (const v of templateVars) {
6794
- if (merged[v.name] === undefined && v.default !== undefined) {
6795
- merged[v.name] = v.default;
6796
- }
6797
- }
6798
- const missing = [];
6799
- for (const v of templateVars) {
6800
- if (v.required && merged[v.name] === undefined) {
6801
- missing.push(v.name);
6802
- }
6803
- }
6804
- if (missing.length > 0) {
6805
- throw new Error(`Missing required template variable(s): ${missing.join(", ")}`);
6806
- }
6807
- return merged;
6817
+ return resolveTemplateVariables(templateVars, provided);
6808
6818
  }
6809
6819
  function substituteVars(text, variables) {
6810
- let result = text;
6811
- for (const [key, val] of Object.entries(variables)) {
6812
- result = result.replace(new RegExp(`\\{${key}\\}`, "g"), val);
6813
- }
6814
- return result;
6820
+ return substituteTemplateVariables(text, variables);
6815
6821
  }
6816
6822
  function tasksFromTemplate(templateId, projectId, variables, taskListId, db, _visitedTemplateIds) {
6817
6823
  const d = db || getDatabase();
@@ -10861,7 +10867,7 @@ var init_dispatches = __esm(() => {
10861
10867
  // package.json
10862
10868
  var package_default = {
10863
10869
  name: "@hasna/todos",
10864
- version: "0.11.93",
10870
+ version: "0.11.95",
10865
10871
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
10866
10872
  type: "module",
10867
10873
  main: "dist/index.js",
@@ -23215,6 +23221,21 @@ var TEMPLATE_COLUMNS = [
23215
23221
  "machine_id",
23216
23222
  "synced_at"
23217
23223
  ];
23224
+ var TEMPLATE_TASK_COLUMNS = [
23225
+ "id",
23226
+ "template_id",
23227
+ "position",
23228
+ "title_pattern",
23229
+ "description",
23230
+ "priority",
23231
+ "tags",
23232
+ "task_type",
23233
+ "condition",
23234
+ "include_template_id",
23235
+ "depends_on_positions",
23236
+ "metadata",
23237
+ "created_at"
23238
+ ];
23218
23239
  var TASK_COLUMNS = [
23219
23240
  "id",
23220
23241
  "short_id",
@@ -23283,7 +23304,7 @@ var AUDIT_COLUMNS = [
23283
23304
  "created_at",
23284
23305
  "machine_id"
23285
23306
  ];
23286
- var JSON_COLUMNS = new Set(["tags", "metadata", "permissions", "capabilities", "variables"]);
23307
+ var JSON_COLUMNS = new Set(["tags", "metadata", "permissions", "capabilities", "variables", "depends_on_positions"]);
23287
23308
  var BOOLEAN_COLUMNS = new Set(["requires_approval"]);
23288
23309
  function exportSqliteTodosStorageSnapshot(db) {
23289
23310
  const d = db ?? getDatabase();
@@ -23297,6 +23318,7 @@ function exportSqliteTodosStorageSnapshot(db) {
23297
23318
  agents: listAgents({ include_archived: true }, d),
23298
23319
  taskLists: listTaskLists(undefined, d),
23299
23320
  templates: listTemplates(d),
23321
+ templateTasks: listTemplates(d).flatMap((template) => getTemplateTasks(template.id, d)),
23300
23322
  auditHistory: getRecentActivity(Number.MAX_SAFE_INTEGER, d),
23301
23323
  tombstones: listStorageTombstones(d)
23302
23324
  };
@@ -23346,6 +23368,7 @@ function importSqliteTodosStorageSnapshot(snapshot, db) {
23346
23368
  applyRows("task_lists", "task_lists", TASK_LIST_COLUMNS, snapshot.taskLists, "updated_at");
23347
23369
  applyRows("plans", "plans", PLAN_COLUMNS, snapshot.plans, "updated_at");
23348
23370
  applyRows("templates", "task_templates", TEMPLATE_COLUMNS, snapshot.templates);
23371
+ applyRows("template_tasks", "template_tasks", TEMPLATE_TASK_COLUMNS, snapshot.templateTasks ?? []);
23349
23372
  applyRows("tasks", "tasks", TASK_COLUMNS, sortedTasks2(snapshot.tasks), "updated_at", (row, changed) => {
23350
23373
  if (changed && Array.isArray(row["tags"]) && typeof row["id"] === "string") {
23351
23374
  replaceTaskTags(row["id"], row["tags"].filter((tag) => typeof tag === "string"), d);
@@ -23450,6 +23473,8 @@ function tableForTombstone(objectType) {
23450
23473
  return "task_lists";
23451
23474
  if (objectType === "templates")
23452
23475
  return "task_templates";
23476
+ if (objectType === "template_tasks")
23477
+ return "template_tasks";
23453
23478
  return "task_history";
23454
23479
  }
23455
23480
  function listRows(db, table, columns) {
@@ -23868,6 +23893,7 @@ function snapshotEntries(snapshot) {
23868
23893
  ...snapshot.agents.map((payload) => entry("agents", payload, snapshot.exportedAt)),
23869
23894
  ...snapshot.taskLists.map((payload) => entry("task_lists", payload, snapshot.exportedAt)),
23870
23895
  ...snapshot.templates.map((payload) => entry("templates", payload, snapshot.exportedAt)),
23896
+ ...(snapshot.templateTasks ?? []).map((payload) => entry("template_tasks", payload, snapshot.exportedAt)),
23871
23897
  ...snapshot.auditHistory.map((payload) => entry("audit_history", payload, snapshot.exportedAt)),
23872
23898
  ...(snapshot.tombstones ?? []).map((tombstone) => ({
23873
23899
  type: tombstone.object_type,
@@ -23919,6 +23945,7 @@ function rowsToSnapshot(rows) {
23919
23945
  agents: [],
23920
23946
  taskLists: [],
23921
23947
  templates: [],
23948
+ templateTasks: [],
23922
23949
  auditHistory: [],
23923
23950
  tombstones: []
23924
23951
  };
@@ -23953,6 +23980,8 @@ function rowsToSnapshot(rows) {
23953
23980
  snapshot.taskLists.push(payload);
23954
23981
  else if (row.object_type === "templates")
23955
23982
  snapshot.templates.push(payload);
23983
+ else if (row.object_type === "template_tasks")
23984
+ snapshot.templateTasks.push(payload);
23956
23985
  else if (row.object_type === "audit_history")
23957
23986
  snapshot.auditHistory.push(payload);
23958
23987
  }
@@ -24115,10 +24144,13 @@ function createPostgresTodosStorageAdapter(options) {
24115
24144
  get: (id) => store.get("templates", id),
24116
24145
  list: async () => (await store.list("templates")).sort((a, b) => a.name.localeCompare(b.name)),
24117
24146
  update: (id, input) => updateTemplate2(id, input, store),
24118
- delete: (id, context) => store.delete("templates", id, context),
24147
+ delete: (id, context) => deleteTemplate2(id, store, context),
24119
24148
  getWithTasks: async (id) => {
24120
24149
  const template = await store.get("templates", id);
24121
- return template ? { ...template, tasks: [] } : null;
24150
+ if (!template)
24151
+ return null;
24152
+ const tasks = (await store.list("template_tasks")).filter((task2) => task2.template_id === id).sort((left, right) => left.position - right.position || left.id.localeCompare(right.id));
24153
+ return { ...template, tasks };
24122
24154
  }
24123
24155
  },
24124
24156
  audit: {
@@ -24406,6 +24438,57 @@ class PostgresJsonRecordStore {
24406
24438
  }
24407
24439
  return value;
24408
24440
  }
24441
+ async createTemplateWithTasks(template, tasks, context = {}) {
24442
+ await this.ensureSchema();
24443
+ const records = [
24444
+ { object_type: "templates", object_id: template.id, payload: template, updated_at: template.created_at, version: template.version },
24445
+ ...tasks.map((task2) => ({ object_type: "template_tasks", object_id: task2.id, payload: task2, updated_at: task2.created_at, version: 1 }))
24446
+ ];
24447
+ const result = await this.options.client.query(`/* todos:create-template-with-tasks-atomic */ WITH input AS (
24448
+ SELECT value->>'object_type' AS object_type,
24449
+ value->>'object_id' AS object_id,
24450
+ value->'payload' AS payload,
24451
+ value->>'updated_at' AS updated_at,
24452
+ COALESCE((value->>'version')::integer, 1) AS version
24453
+ FROM jsonb_array_elements($2::jsonb) AS value
24454
+ ) INSERT INTO ${this.tableName} (
24455
+ service, object_type, object_id, payload, updated_at,
24456
+ deleted_at, source_machine_id, version
24457
+ ) SELECT $1, object_type, object_id, payload, updated_at::timestamptz,
24458
+ NULL, $3, version
24459
+ FROM input
24460
+ ON CONFLICT (service, object_type, object_id) DO UPDATE SET
24461
+ payload = EXCLUDED.payload,
24462
+ updated_at = EXCLUDED.updated_at,
24463
+ deleted_at = NULL,
24464
+ source_machine_id = EXCLUDED.source_machine_id,
24465
+ version = EXCLUDED.version
24466
+ WHERE ${this.tableName}.updated_at IS NULL
24467
+ OR ${this.tableName}.updated_at < EXCLUDED.updated_at
24468
+ OR (${this.tableName}.updated_at = EXCLUDED.updated_at
24469
+ AND COALESCE(${this.tableName}.version, 0) <= COALESCE(EXCLUDED.version, 0))
24470
+ RETURNING object_type, object_id`, [this.service, jsonbParam(records), this.machineId(context)]);
24471
+ if (result.rows.length !== records.length) {
24472
+ throw new Error("Template checklist write was rejected before completion; no partial template was committed");
24473
+ }
24474
+ }
24475
+ async deleteTemplateWithTasks(id, context = {}) {
24476
+ await this.ensureSchema();
24477
+ const timestamp2 = new Date().toISOString();
24478
+ const result = await this.options.client.query(`/* todos:delete-template-with-tasks-atomic */ WITH target AS (
24479
+ SELECT 1 FROM ${this.tableName}
24480
+ WHERE service = $1 AND object_type = 'templates' AND object_id = $2 AND deleted_at IS NULL
24481
+ ) UPDATE ${this.tableName} AS record SET
24482
+ deleted_at = $3::timestamptz,
24483
+ updated_at = $3::timestamptz,
24484
+ source_machine_id = COALESCE($4, record.source_machine_id),
24485
+ version = COALESCE(record.version, 0) + 1
24486
+ WHERE record.service = $1 AND record.deleted_at IS NULL AND EXISTS (SELECT 1 FROM target)
24487
+ AND (record.object_type = 'templates' AND record.object_id = $2
24488
+ OR record.object_type = 'template_tasks' AND record.payload->>'template_id' = $2)
24489
+ RETURNING record.object_type`, [this.service, id, timestamp2, this.machineId(context)]);
24490
+ return result.rows.some((row) => row.object_type === "templates");
24491
+ }
24409
24492
  async completeTask(id, agentId, options) {
24410
24493
  await this.ensureSchema();
24411
24494
  const operationTimestamp = new Date().toISOString();
@@ -24746,7 +24829,7 @@ async function updateTask2(id, input, store) {
24746
24829
  tags: input.tags ?? existing.tags,
24747
24830
  metadata: input.metadata ?? existing.metadata,
24748
24831
  requires_approval: input.requires_approval ?? existing.requires_approval,
24749
- task_list_id: input.task_list_id ?? existing.task_list_id
24832
+ task_list_id: input.task_list_id !== undefined ? input.task_list_id : existing.task_list_id
24750
24833
  };
24751
24834
  await store.upsert("tasks", task2);
24752
24835
  return task2;
@@ -25170,7 +25253,7 @@ async function updateTaskList2(id, input, store) {
25170
25253
  }
25171
25254
  async function createTemplate2(input, store, context) {
25172
25255
  const timestamp2 = new Date().toISOString();
25173
- return store.upsert("templates", {
25256
+ const template = {
25174
25257
  id: randomUUID3(),
25175
25258
  name: input.name,
25176
25259
  title_pattern: input.title_pattern,
@@ -25185,7 +25268,30 @@ async function createTemplate2(input, store, context) {
25185
25268
  created_at: timestamp2,
25186
25269
  machine_id: store.machineId(context),
25187
25270
  synced_at: null
25188
- }, context);
25271
+ };
25272
+ const tasks = buildTemplateTasks(template.id, input.tasks ?? [], timestamp2);
25273
+ await store.createTemplateWithTasks(template, tasks, context);
25274
+ return template;
25275
+ }
25276
+ function buildTemplateTasks(templateId, inputs, timestamp2) {
25277
+ return inputs.map((input, position) => ({
25278
+ id: randomUUID3(),
25279
+ template_id: templateId,
25280
+ position,
25281
+ title_pattern: input.title_pattern,
25282
+ description: input.description ?? null,
25283
+ priority: input.priority ?? "medium",
25284
+ tags: input.tags ?? [],
25285
+ task_type: input.task_type ?? null,
25286
+ condition: input.condition ?? null,
25287
+ include_template_id: input.include_template_id ?? null,
25288
+ depends_on_positions: input.depends_on ?? [],
25289
+ metadata: input.metadata ?? {},
25290
+ created_at: timestamp2
25291
+ }));
25292
+ }
25293
+ async function deleteTemplate2(id, store, context) {
25294
+ return store.deleteTemplateWithTasks(id, context);
25189
25295
  }
25190
25296
  async function updateTemplate2(id, input, store) {
25191
25297
  const template = await store.get("templates", id);
@@ -25241,6 +25347,7 @@ async function exportSnapshot(store) {
25241
25347
  agents: await store.list("agents"),
25242
25348
  taskLists: await store.list("task_lists"),
25243
25349
  templates: await store.list("templates"),
25350
+ templateTasks: await store.list("template_tasks"),
25244
25351
  auditHistory: await store.list("audit_history"),
25245
25352
  tombstones: await store.listTombstones()
25246
25353
  };
@@ -25265,6 +25372,7 @@ async function importSnapshot(snapshot, store, context) {
25265
25372
  ...snapshot.agents.map((row) => ["agents", row]),
25266
25373
  ...snapshot.taskLists.map((row) => ["task_lists", row]),
25267
25374
  ...snapshot.templates.map((row) => ["templates", row]),
25375
+ ...(snapshot.templateTasks ?? []).map((row) => ["template_tasks", row]),
25268
25376
  ...snapshot.auditHistory.map((row) => ["audit_history", row])
25269
25377
  ];
25270
25378
  for (const [type, row] of entries) {
@@ -25410,6 +25518,7 @@ var SNAPSHOT_TO_OBJECT_TYPE = {
25410
25518
  agents: "agents",
25411
25519
  taskLists: "task_lists",
25412
25520
  templates: "templates",
25521
+ templateTasks: "template_tasks",
25413
25522
  auditHistory: "audit_history"
25414
25523
  };
25415
25524
 
@@ -25601,6 +25710,7 @@ function emptySnapshot() {
25601
25710
  agents: [],
25602
25711
  taskLists: [],
25603
25712
  templates: [],
25713
+ templateTasks: [],
25604
25714
  auditHistory: [],
25605
25715
  tombstones: []
25606
25716
  };
@@ -26057,6 +26167,9 @@ class TodosShadowOutbox {
26057
26167
  case "templates":
26058
26168
  snapshot.templates.push(record);
26059
26169
  break;
26170
+ case "template_tasks":
26171
+ snapshot.templateTasks.push(record);
26172
+ break;
26060
26173
  case "audit_history":
26061
26174
  snapshot.auditHistory.push(record);
26062
26175
  break;
@@ -26123,6 +26236,7 @@ function emptySnapshot2() {
26123
26236
  agents: [],
26124
26237
  taskLists: [],
26125
26238
  templates: [],
26239
+ templateTasks: [],
26126
26240
  auditHistory: [],
26127
26241
  tombstones: []
26128
26242
  };
@@ -0,0 +1,8 @@
1
+ import type { TemplateVariable } from "../types/index.js";
2
+ /** Resolve template variables consistently for local and remote execution. */
3
+ export declare function resolveTemplateVariables(templateVars: TemplateVariable[], provided?: Record<string, string>): Record<string, string>;
4
+ /** Replace only variables which have been resolved, leaving unrelated literals intact. */
5
+ export declare function substituteTemplateVariables(text: string, variables: Record<string, string>): string;
6
+ /** Evaluate the intentionally small, documented template-condition language. */
7
+ export declare function evaluateTemplateCondition(condition: string, variables: Record<string, string>): boolean;
8
+ //# sourceMappingURL=template-semantics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"template-semantics.d.ts","sourceRoot":"","sources":["../../src/lib/template-semantics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAE1D,8EAA8E;AAC9E,wBAAgB,wBAAwB,CACtC,YAAY,EAAE,gBAAgB,EAAE,EAChC,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAgBxB;AAED,0FAA0F;AAC1F,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAMnG;AAED,gFAAgF;AAChF,wBAAgB,yBAAyB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,OAAO,CAkBvG"}