@hasna/todos 0.11.70 → 0.11.72

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.
package/dist/index.js CHANGED
@@ -1198,6 +1198,11 @@ var init_migrations = __esm(() => {
1198
1198
  CREATE INDEX IF NOT EXISTS idx_storage_tombstones_object ON storage_tombstones(object_type, object_id);
1199
1199
  CREATE INDEX IF NOT EXISTS idx_storage_tombstones_updated ON storage_tombstones(updated_at);
1200
1200
  INSERT OR IGNORE INTO _migrations (id) VALUES (63);
1201
+ `,
1202
+ `
1203
+ ALTER TABLE plans ADD COLUMN slug TEXT;
1204
+ CREATE INDEX IF NOT EXISTS idx_plans_slug ON plans(slug);
1205
+ INSERT OR IGNORE INTO _migrations (id) VALUES (64);
1201
1206
  `
1202
1207
  ];
1203
1208
  });
@@ -1221,6 +1226,29 @@ function runMigrations(db) {
1221
1226
  }
1222
1227
  ensureSchema(db);
1223
1228
  }
1229
+ function planSlugBase(value) {
1230
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "plan";
1231
+ }
1232
+ function backfillPlanSlugs(db) {
1233
+ try {
1234
+ const rows = db.query("SELECT id, project_id, name, slug FROM plans ORDER BY created_at ASC, id ASC").all();
1235
+ const used = new Set;
1236
+ for (const row of rows) {
1237
+ const scope = row.project_id ?? "__global__";
1238
+ const base = planSlugBase(row.slug || row.name);
1239
+ let candidate = base;
1240
+ let suffix = 2;
1241
+ while (used.has(`${scope}:${candidate}`)) {
1242
+ candidate = `${base}-${suffix}`;
1243
+ suffix += 1;
1244
+ }
1245
+ used.add(`${scope}:${candidate}`);
1246
+ if (row.slug !== candidate) {
1247
+ db.run("UPDATE plans SET slug = ? WHERE id = ?", [candidate, row.id]);
1248
+ }
1249
+ }
1250
+ } catch {}
1251
+ }
1224
1252
  function ensureSchema(db) {
1225
1253
  const ensureColumn = (table, column, type) => {
1226
1254
  try {
@@ -1270,7 +1298,8 @@ function ensureSchema(db) {
1270
1298
  )`);
1271
1299
  ensureTable("plans", `
1272
1300
  CREATE TABLE plans (
1273
- id TEXT PRIMARY KEY, project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
1301
+ id TEXT PRIMARY KEY, slug TEXT,
1302
+ project_id TEXT REFERENCES projects(id) ON DELETE CASCADE,
1274
1303
  task_list_id TEXT, agent_id TEXT,
1275
1304
  name TEXT NOT NULL, description TEXT,
1276
1305
  status TEXT NOT NULL DEFAULT 'active' CHECK(status IN ('active', 'completed', 'archived')),
@@ -1785,8 +1814,10 @@ function ensureSchema(db) {
1785
1814
  ensureColumn("agents", "org_id", "TEXT");
1786
1815
  ensureColumn("agents", "capabilities", "TEXT DEFAULT '[]'");
1787
1816
  ensureColumn("projects", "org_id", "TEXT");
1817
+ ensureColumn("plans", "slug", "TEXT");
1788
1818
  ensureColumn("plans", "task_list_id", "TEXT");
1789
1819
  ensureColumn("plans", "agent_id", "TEXT");
1820
+ backfillPlanSlugs(db);
1790
1821
  ensureColumn("task_templates", "variables", "TEXT DEFAULT '[]'");
1791
1822
  ensureColumn("task_templates", "version", "INTEGER NOT NULL DEFAULT 1");
1792
1823
  ensureColumn("template_tasks", "condition", "TEXT");
@@ -1901,6 +1932,8 @@ function ensureSchema(db) {
1901
1932
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_tags_name ON tags(name)");
1902
1933
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_project ON plans(project_id)");
1903
1934
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_status ON plans(status)");
1935
+ ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_slug ON plans(slug)");
1936
+ ensureIndex("CREATE UNIQUE INDEX IF NOT EXISTS idx_plans_scope_slug ON plans(COALESCE(project_id, ''), slug) WHERE slug IS NOT NULL");
1904
1937
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_task_list ON plans(task_list_id)");
1905
1938
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_plans_agent ON plans(agent_id)");
1906
1939
  ensureIndex("CREATE INDEX IF NOT EXISTS idx_task_history_task ON task_history(task_id)");
@@ -2734,6 +2767,9 @@ function clearExpiredLocks(db) {
2734
2767
  const cutoff = lockExpiryCutoff();
2735
2768
  db.run("UPDATE tasks SET locked_by = NULL, locked_at = NULL WHERE locked_at IS NOT NULL AND locked_at < ?", [cutoff]);
2736
2769
  }
2770
+ function slugifyRef(value) {
2771
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
2772
+ }
2737
2773
  function resolvePartialId(db, table, partialId) {
2738
2774
  if (!ALLOWED_TABLES.has(table)) {
2739
2775
  throw new Error(`Invalid table name: ${table}`);
@@ -2760,6 +2796,16 @@ function resolvePartialId(db, table, partialId) {
2760
2796
  if (slugRow)
2761
2797
  return slugRow.id;
2762
2798
  }
2799
+ if (table === "plans") {
2800
+ const slug = slugifyRef(partialId);
2801
+ if (slug) {
2802
+ const slugRows = db.query("SELECT id FROM plans WHERE slug = ?").all(slug);
2803
+ if (slugRows.length === 1)
2804
+ return slugRows[0].id;
2805
+ if (slugRows.length > 1)
2806
+ return null;
2807
+ }
2808
+ }
2763
2809
  if (table === "projects") {
2764
2810
  const nameRow = db.query("SELECT id FROM projects WHERE lower(name) = ?").get(partialId.toLowerCase());
2765
2811
  if (nameRow)
@@ -7985,8 +8031,6 @@ function routeEnabledForTask(task, taskList) {
7985
8031
  const explicit = booleanField(task.metadata.route_enabled);
7986
8032
  if (explicit !== undefined)
7987
8033
  return explicit;
7988
- if (task.tags.includes("auto:route") || task.tags.includes("route:enabled"))
7989
- return true;
7990
8034
  const taskListDefault = taskList ? booleanField(taskList.metadata.route_enabled) : undefined;
7991
8035
  if (taskListDefault !== undefined)
7992
8036
  return taskListDefault;
@@ -8008,8 +8052,26 @@ function workflowPointersFromMetadata(metadata) {
8008
8052
  function compactWorkflowPointers(pointers) {
8009
8053
  return Object.fromEntries(Object.entries(pointers).filter(([, value]) => typeof value === "string" && value.length > 0));
8010
8054
  }
8011
- function classifyProjectKind(path) {
8012
- return path.includes("/hasna/opensource/") ? "open-source" : "unknown";
8055
+ function metadataStringField(record, keys) {
8056
+ if (!record)
8057
+ return;
8058
+ for (const key of keys) {
8059
+ const value = record[key];
8060
+ if (typeof value === "string" && value.trim())
8061
+ return value.trim();
8062
+ }
8063
+ return;
8064
+ }
8065
+ function projectKindFromMetadata(...records) {
8066
+ for (const record of records) {
8067
+ const value = metadataStringField(record ?? undefined, ["project_kind", "projectKind", "source_kind", "sourceKind"]);
8068
+ if (value)
8069
+ return value;
8070
+ }
8071
+ return null;
8072
+ }
8073
+ function classifyProjectKind(_path, metadata) {
8074
+ return projectKindFromMetadata(metadata);
8013
8075
  }
8014
8076
  function isWorktreePath(path) {
8015
8077
  return path.includes("/.codewith/worktrees/") || path.includes("/.worktrees/");
@@ -8082,7 +8144,6 @@ function taskEventMetadata(task) {
8082
8144
  metadata.project_canonical_path = projectPath;
8083
8145
  }
8084
8146
  if (projectPath) {
8085
- metadata.project_kind = classifyProjectKind(projectPath);
8086
8147
  metadata.project_is_worktree = isWorktreePath(projectPath);
8087
8148
  metadata.working_dir = task.working_dir ?? projectPath;
8088
8149
  }
@@ -8094,6 +8155,10 @@ function taskEventMetadata(task) {
8094
8155
  metadata.task_list_project_id = taskList.project_id;
8095
8156
  metadata.task_list_is_project_default = Boolean(project?.task_list_id && taskList.slug === project.task_list_id);
8096
8157
  }
8158
+ const projectKind = projectKindFromMetadata(task.metadata, taskList?.metadata);
8159
+ if (projectKind) {
8160
+ metadata.project_kind = classifyProjectKind(projectPath ?? "", { project_kind: projectKind });
8161
+ }
8097
8162
  const routeEnabled = routeEnabledForTask(task, taskList);
8098
8163
  if (routeEnabled !== undefined) {
8099
8164
  metadata.route_enabled = routeEnabled;
@@ -10736,15 +10801,59 @@ init_database();
10736
10801
  // src/db/plans.ts
10737
10802
  init_types();
10738
10803
  init_database();
10804
+ function planSlugBase2(value) {
10805
+ return slugify(value) || "plan";
10806
+ }
10807
+ function normalizePlanSlug(value) {
10808
+ const slug = slugify(value);
10809
+ if (!slug)
10810
+ throw new Error("Invalid plan slug");
10811
+ return slug;
10812
+ }
10813
+ function plansBySlug(slug, db, projectId) {
10814
+ if (projectId !== undefined) {
10815
+ if (projectId === null) {
10816
+ return db.query("SELECT * FROM plans WHERE slug = ? AND project_id IS NULL ORDER BY created_at ASC, id ASC").all(slug);
10817
+ }
10818
+ return db.query("SELECT * FROM plans WHERE slug = ? AND project_id = ? ORDER BY created_at ASC, id ASC").all(slug, projectId);
10819
+ }
10820
+ return db.query("SELECT * FROM plans WHERE slug = ? ORDER BY created_at ASC, id ASC").all(slug);
10821
+ }
10822
+ function planSlugExists(slug, projectId, db, excludeId) {
10823
+ const rows = plansBySlug(slug, db, projectId);
10824
+ return rows.some((plan) => plan.id !== excludeId);
10825
+ }
10826
+ function nextPlanSlug(base, projectId, db, excludeId) {
10827
+ let candidate = base;
10828
+ let suffix = 2;
10829
+ while (planSlugExists(candidate, projectId, db, excludeId)) {
10830
+ candidate = `${base}-${suffix}`;
10831
+ suffix += 1;
10832
+ }
10833
+ return candidate;
10834
+ }
10835
+ function resolveCreateSlug(input, projectId, db) {
10836
+ if (input.slug !== undefined) {
10837
+ const slug = normalizePlanSlug(input.slug);
10838
+ if (planSlugExists(slug, projectId, db)) {
10839
+ throw new Error(`Plan slug already exists in this scope: ${slug}`);
10840
+ }
10841
+ return slug;
10842
+ }
10843
+ return nextPlanSlug(planSlugBase2(input.name), projectId, db);
10844
+ }
10739
10845
  function createPlan(input, db) {
10740
10846
  const d = db || getDatabase();
10741
10847
  const id = uuid();
10742
10848
  const timestamp = now();
10849
+ const projectId = input.project_id || null;
10850
+ const slug = resolveCreateSlug(input, projectId, d);
10743
10851
  const machineId = currentStorageMachineId(d);
10744
- d.run(`INSERT INTO plans (id, project_id, task_list_id, agent_id, name, description, status, created_at, updated_at, machine_id)
10745
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
10852
+ d.run(`INSERT INTO plans (id, slug, project_id, task_list_id, agent_id, name, description, status, created_at, updated_at, machine_id)
10853
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
10746
10854
  id,
10747
- input.project_id || null,
10855
+ slug,
10856
+ projectId,
10748
10857
  input.task_list_id || null,
10749
10858
  input.agent_id || null,
10750
10859
  input.name,
@@ -10779,6 +10888,14 @@ function updatePlan(id, input, db) {
10779
10888
  sets.push("name = ?");
10780
10889
  params.push(input.name);
10781
10890
  }
10891
+ if (input.slug !== undefined) {
10892
+ const slug = normalizePlanSlug(input.slug);
10893
+ if (planSlugExists(slug, plan.project_id, d, id)) {
10894
+ throw new Error(`Plan slug already exists in this scope: ${slug}`);
10895
+ }
10896
+ sets.push("slug = ?");
10897
+ params.push(slug);
10898
+ }
10782
10899
  if (input.description !== undefined) {
10783
10900
  sets.push("description = ?");
10784
10901
  params.push(input.description);
@@ -12632,7 +12749,7 @@ var dataKeys = [
12632
12749
  var insertColumns = {
12633
12750
  projects: ["id", "name", "path", "description", "task_list_id", "task_prefix", "task_counter", "created_at", "updated_at", "machine_id", "synced_at"],
12634
12751
  task_lists: ["id", "project_id", "slug", "name", "description", "metadata", "created_at", "updated_at", "machine_id", "synced_at"],
12635
- plans: ["id", "project_id", "task_list_id", "agent_id", "name", "description", "status", "created_at", "updated_at", "machine_id", "synced_at"],
12752
+ plans: ["id", "slug", "project_id", "task_list_id", "agent_id", "name", "description", "status", "created_at", "updated_at", "machine_id", "synced_at"],
12636
12753
  tasks: [
12637
12754
  "id",
12638
12755
  "short_id",
@@ -12924,6 +13041,36 @@ function prepareValue(column, value) {
12924
13041
  return JSON.stringify(value ?? (column === "tags" || column === "files_changed" ? [] : {}));
12925
13042
  return value === undefined ? null : value;
12926
13043
  }
13044
+ function slugifyPlanValue(value) {
13045
+ return typeof value === "string" ? value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") : "";
13046
+ }
13047
+ function planSlugBase3(plan) {
13048
+ return slugifyPlanValue(plan.slug) || slugifyPlanValue(plan.name) || "plan";
13049
+ }
13050
+ function planSlugScope(projectId) {
13051
+ return typeof projectId === "string" && projectId ? projectId : "__global__";
13052
+ }
13053
+ function planSlugKey(projectId, slug) {
13054
+ return `${planSlugScope(projectId)}:${slug}`;
13055
+ }
13056
+ function normalizeBridgePlanSlugs(plans, db) {
13057
+ const existingRows = db.query("SELECT id, project_id, slug FROM plans WHERE slug IS NOT NULL").all();
13058
+ const existingIds = new Set(existingRows.map((row) => row.id));
13059
+ const used = new Set(existingRows.filter((row) => row.slug).map((row) => planSlugKey(row.project_id, row.slug)));
13060
+ return plans.map((plan) => {
13061
+ if (existingIds.has(plan.id))
13062
+ return plan;
13063
+ const base = planSlugBase3(plan);
13064
+ let candidate = base;
13065
+ let suffix = 2;
13066
+ while (used.has(planSlugKey(plan.project_id, candidate))) {
13067
+ candidate = `${base}-${suffix}`;
13068
+ suffix += 1;
13069
+ }
13070
+ used.add(planSlugKey(plan.project_id, candidate));
13071
+ return { ...plan, slug: candidate };
13072
+ });
13073
+ }
12927
13074
  function insertRecord(db, tableKey, row) {
12928
13075
  const table = tableByKey[tableKey];
12929
13076
  const columns = insertColumns[tableKey];
@@ -13060,6 +13207,7 @@ function importLocalBridgeBundle(bundle, options = {}, db) {
13060
13207
  const conflictStrategy = options.conflictStrategy ?? "skip";
13061
13208
  const data = {
13062
13209
  ...bundle.data,
13210
+ plans: normalizeBridgePlanSlugs(bundle.data.plans, d),
13063
13211
  tasks: sortedTasks(bundle.data.tasks),
13064
13212
  saved_views: bundle.data.saved_views ?? [],
13065
13213
  task_boards: bundle.data.task_boards ?? [],
@@ -13253,6 +13401,7 @@ function createAgentProjectDemoBundle() {
13253
13401
  });
13254
13402
  data.plans.push({
13255
13403
  id: ids.plan,
13404
+ slug: "ship-local-demo-workflow",
13256
13405
  project_id: ids.project,
13257
13406
  task_list_id: ids.list,
13258
13407
  agent_id: "demo-agent",
@@ -23380,9 +23529,17 @@ async function updateProject2(id, input, store) {
23380
23529
  }
23381
23530
  async function createPlan2(input, store, context) {
23382
23531
  const timestamp2 = new Date().toISOString();
23532
+ const projectId = input.project_id ?? context?.projectId ?? null;
23533
+ const slug = await resolvePostgresPlanSlug({
23534
+ name: input.name,
23535
+ slug: input.slug,
23536
+ projectId,
23537
+ store
23538
+ });
23383
23539
  return store.upsert("plans", {
23384
23540
  id: randomUUID3(),
23385
- project_id: input.project_id ?? context?.projectId ?? null,
23541
+ slug,
23542
+ project_id: projectId,
23386
23543
  task_list_id: input.task_list_id ?? context?.taskListId ?? null,
23387
23544
  agent_id: input.agent_id ?? context?.agentId ?? null,
23388
23545
  name: input.name,
@@ -23396,7 +23553,17 @@ async function createPlan2(input, store, context) {
23396
23553
  }
23397
23554
  async function updatePlan2(id, input, store) {
23398
23555
  const plan = await requireRecord("plans", id, store);
23399
- return store.upsert("plans", { ...plan, ...definedPatch(input), updated_at: new Date().toISOString() });
23556
+ const patch = definedPatch(input);
23557
+ if (input.slug !== undefined) {
23558
+ patch.slug = await resolvePostgresPlanSlug({
23559
+ name: plan.name,
23560
+ slug: input.slug,
23561
+ projectId: plan.project_id,
23562
+ store,
23563
+ excludeId: id
23564
+ });
23565
+ }
23566
+ return store.upsert("plans", { ...plan, ...patch, updated_at: new Date().toISOString() });
23400
23567
  }
23401
23568
  async function registerAgent2(input, store, context) {
23402
23569
  const existing = (await store.list("agents")).find((agent2) => agent2.name === input.name && agent2.status !== "archived");
@@ -23649,8 +23816,38 @@ function matchesOne(value, expected) {
23649
23816
  function priorityRank(priority) {
23650
23817
  return { critical: 0, high: 1, medium: 2, low: 3 }[priority];
23651
23818
  }
23819
+ function slugifyRaw(value) {
23820
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
23821
+ }
23652
23822
  function slugify2(value) {
23653
- return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "todos";
23823
+ return slugifyRaw(value) || "todos";
23824
+ }
23825
+ function normalizePlanSlug2(value) {
23826
+ const slug = slugifyRaw(value);
23827
+ if (!slug)
23828
+ throw new Error("Invalid plan slug");
23829
+ return slug;
23830
+ }
23831
+ function planSlugBase4(value) {
23832
+ return slugifyRaw(value) || "plan";
23833
+ }
23834
+ async function resolvePostgresPlanSlug(options) {
23835
+ const plans = await options.store.list("plans");
23836
+ const used = new Set(plans.filter((plan) => plan.project_id === options.projectId && plan.id !== options.excludeId && plan.slug).map((plan) => plan.slug));
23837
+ if (options.slug !== undefined) {
23838
+ const slug = normalizePlanSlug2(options.slug);
23839
+ if (used.has(slug))
23840
+ throw new Error(`Plan slug already exists in this scope: ${slug}`);
23841
+ return slug;
23842
+ }
23843
+ const base = planSlugBase4(options.name);
23844
+ let candidate = base;
23845
+ let suffix = 2;
23846
+ while (used.has(candidate)) {
23847
+ candidate = `${base}-${suffix}`;
23848
+ suffix += 1;
23849
+ }
23850
+ return candidate;
23654
23851
  }
23655
23852
  function definedPatch(value) {
23656
23853
  return Object.fromEntries(Object.entries(value).filter(([, entry2]) => entry2 !== undefined));
@@ -24826,6 +25023,496 @@ function clean2(value) {
24826
25023
  const trimmed = value?.trim();
24827
25024
  return trimmed ? trimmed : undefined;
24828
25025
  }
25026
+ // src/lib/task-route-sources.ts
25027
+ init_database();
25028
+ import { Database as Database3 } from "bun:sqlite";
25029
+ import { createHash as createHash10 } from "crypto";
25030
+ import { existsSync as existsSync9, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
25031
+ import { basename as basename2, dirname as dirname7, join as join10, resolve as resolve10 } from "path";
25032
+ init_redaction();
25033
+
25034
+ // src/lib/task-routing.ts
25035
+ init_database();
25036
+ function machineLocalPath(project, db) {
25037
+ const machineId = process.env["TODOS_MACHINE_ID"];
25038
+ if (!machineId)
25039
+ return null;
25040
+ try {
25041
+ const row = db.query("SELECT path FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(project.id, machineId);
25042
+ return row?.path ?? null;
25043
+ } catch {
25044
+ return null;
25045
+ }
25046
+ }
25047
+ function resolveProject(task2, db) {
25048
+ const project = task2.project_id ? getProject(task2.project_id, db) : null;
25049
+ const projectPath = project ? machineLocalPath(project, db) ?? project.path : task2.working_dir;
25050
+ return { project, projectPath: projectPath ?? null };
25051
+ }
25052
+ function resolveTaskList(task2, project, db) {
25053
+ if (task2.task_list_id) {
25054
+ return getTaskList(task2.task_list_id, db) ?? (project ? getTaskListBySlug(task2.task_list_id, project.id, db) : null);
25055
+ }
25056
+ if (project?.task_list_id) {
25057
+ return getTaskListBySlug(project.task_list_id, project.id, db);
25058
+ }
25059
+ return null;
25060
+ }
25061
+ function isTerminal2(status) {
25062
+ return status === "completed" || status === "cancelled" || status === "failed";
25063
+ }
25064
+ function routeConcurrencyKey(task2, project, taskList, projectPath) {
25065
+ if (project?.id)
25066
+ return `project:${project.id}`;
25067
+ if (taskList?.id)
25068
+ return `task-list:${taskList.id}`;
25069
+ if (projectPath)
25070
+ return `path:${projectPath}`;
25071
+ return `task:${task2.id}`;
25072
+ }
25073
+ function getTaskRouteState(taskOrId, db) {
25074
+ const d = db || getDatabase();
25075
+ const task2 = typeof taskOrId === "string" ? getTask(taskOrId, d) : taskOrId;
25076
+ if (!task2)
25077
+ throw new Error(`Task not found: ${taskOrId}`);
25078
+ const { project, projectPath } = resolveProject(task2, d);
25079
+ const taskList = resolveTaskList(task2, project, d);
25080
+ const automation = routingAutomationMetadata(task2, taskList) ?? {};
25081
+ const routeEnabled = routeEnabledForTask(task2, taskList) === true;
25082
+ const tagOptIn = task2.tags.includes("auto:route") || task2.tags.includes("route:enabled");
25083
+ const projectKind = projectKindFromMetadata(task2.metadata, taskList?.metadata);
25084
+ const locked = Boolean(task2.locked_by && !isLockExpired(task2.locked_at));
25085
+ const blockers = getBlockingDeps(task2.id, d);
25086
+ const blocked = blockers.length > 0;
25087
+ const terminal = isTerminal2(task2.status);
25088
+ const requiresApproval = automation.requires_approval === true || task2.requires_approval === true;
25089
+ const approvalRequired = automation.approval_required === true;
25090
+ const approved = Boolean(task2.approved_by);
25091
+ const gates = {
25092
+ route_enabled: routeEnabled,
25093
+ tag_opt_in: tagOptIn,
25094
+ no_auto: automation.no_auto === true,
25095
+ manual: automation.manual === true,
25096
+ manual_required: automation.manual_required === true,
25097
+ requires_approval: requiresApproval,
25098
+ approval_required: approvalRequired,
25099
+ approved,
25100
+ locked,
25101
+ blocked,
25102
+ terminal
25103
+ };
25104
+ const reasons = [];
25105
+ if (task2.status !== "pending")
25106
+ reasons.push("task_not_pending");
25107
+ if (terminal)
25108
+ reasons.push("task_terminal");
25109
+ if (!routeEnabled)
25110
+ reasons.push("route_not_enabled");
25111
+ if (locked)
25112
+ reasons.push("task_locked");
25113
+ if (blocked)
25114
+ reasons.push("task_blocked");
25115
+ if (gates.no_auto)
25116
+ reasons.push("no_auto");
25117
+ if (gates.manual)
25118
+ reasons.push("manual");
25119
+ if (gates.manual_required)
25120
+ reasons.push("manual_required");
25121
+ if (requiresApproval && !approved)
25122
+ reasons.push("requires_approval");
25123
+ if (approvalRequired && !approved)
25124
+ reasons.push("approval_required");
25125
+ if (automation.allowed === false)
25126
+ reasons.push("automation_disallowed");
25127
+ return {
25128
+ schema_version: TODOS_TASK_ROUTE_STATE_SCHEMA_VERSION,
25129
+ task_id: task2.id,
25130
+ task_short_id: task2.short_id,
25131
+ status: task2.status,
25132
+ eligible: reasons.length === 0,
25133
+ reasons,
25134
+ blockers: blockers.map((blocker) => ({
25135
+ id: blocker.id,
25136
+ short_id: blocker.short_id,
25137
+ title: blocker.title,
25138
+ status: blocker.status
25139
+ })),
25140
+ gates,
25141
+ automation: Object.keys(automation).length > 0 ? automation : null,
25142
+ route: {
25143
+ project_id: project?.id ?? task2.project_id,
25144
+ project_path: projectPath,
25145
+ working_dir: task2.working_dir ?? projectPath,
25146
+ project_kind: projectKind,
25147
+ task_list_id: taskList?.id ?? task2.task_list_id,
25148
+ task_list_slug: taskList?.slug ?? null,
25149
+ task_list_name: taskList?.name ?? null,
25150
+ concurrency_key: routeConcurrencyKey(task2, project, taskList, projectPath)
25151
+ },
25152
+ pointers: workflowPointersFromMetadata(task2.metadata)
25153
+ };
25154
+ }
25155
+ function setTaskWorkflowPointers(taskId, input, db) {
25156
+ const d = db || getDatabase();
25157
+ const task2 = getTask(taskId, d);
25158
+ if (!task2)
25159
+ throw new Error(`Task not found: ${taskId}`);
25160
+ const previous = workflowPointersFromMetadata(task2.metadata);
25161
+ const next = compactWorkflowPointers({
25162
+ current_workflow_invocation_id: pointerPatch(previous.current_workflow_invocation_id, input, "current_workflow_invocation_id"),
25163
+ current_run_id: pointerPatch(previous.current_run_id, input, "current_run_id"),
25164
+ latest_manifest_path: pointerPatch(previous.latest_manifest_path, input, "latest_manifest_path"),
25165
+ latest_evaluation_path: pointerPatch(previous.latest_evaluation_path, input, "latest_evaluation_path"),
25166
+ workflow_state: pointerPatch(previous.workflow_state, input, "workflow_state")
25167
+ });
25168
+ const timestamp2 = now();
25169
+ const {
25170
+ current_workflow_invocation_id,
25171
+ current_run_id,
25172
+ latest_manifest_path,
25173
+ latest_evaluation_path,
25174
+ workflow_state,
25175
+ workflow_invocation,
25176
+ ...baseMetadata
25177
+ } = task2.metadata;
25178
+ const metadata = {
25179
+ ...baseMetadata,
25180
+ ...next,
25181
+ workflow_invocation: {
25182
+ schema_version: TASK_WORKFLOW_POINTER_SCHEMA_VERSION,
25183
+ ...next,
25184
+ updated_at: timestamp2,
25185
+ updated_by: input.actor ?? null
25186
+ }
25187
+ };
25188
+ return updateTask(task2.id, { version: task2.version, metadata }, d);
25189
+ }
25190
+ function pointerPatch(previous, input, key) {
25191
+ if (!Object.prototype.hasOwnProperty.call(input, key))
25192
+ return previous;
25193
+ const value = input[key];
25194
+ if (value === undefined)
25195
+ return previous;
25196
+ return typeof value === "string" && value.trim() ? value : undefined;
25197
+ }
25198
+
25199
+ // src/lib/task-route-sources.ts
25200
+ var TASK_ROUTE_SOURCE_DISCOVERY_SCHEMA_VERSION = "todos.task_route_sources.v1";
25201
+ var TODO_STORE_RELATIVE_PATH = join10(".hasna", "todos", "todos.db");
25202
+ var ROOT_SCAN_MAX_DEPTH = 5;
25203
+ var SKIPPED_SCAN_DIRS = new Set([
25204
+ ".git",
25205
+ ".hg",
25206
+ ".svn",
25207
+ "node_modules",
25208
+ "dist",
25209
+ "build",
25210
+ ".next",
25211
+ ".turbo",
25212
+ ".cache"
25213
+ ]);
25214
+ function normalizePath3(input) {
25215
+ return resolve10(input);
25216
+ }
25217
+ function sourceStoreId(sourceDbPath) {
25218
+ const digest = createHash10("sha256").update(sourceDbPath).digest("hex").slice(0, 16);
25219
+ return `sqlite:${digest}`;
25220
+ }
25221
+ function inferSourceRepoPath(sourceDbPath) {
25222
+ const normalized = normalizePath3(sourceDbPath);
25223
+ if (normalized.endsWith(TODO_STORE_RELATIVE_PATH)) {
25224
+ return dirname7(dirname7(dirname7(normalized)));
25225
+ }
25226
+ return dirname7(normalized);
25227
+ }
25228
+ function createStoreRef(sourceDbPath) {
25229
+ const normalized = normalizePath3(sourceDbPath);
25230
+ return {
25231
+ source_store_id: sourceStoreId(normalized),
25232
+ source_repo_path: inferSourceRepoPath(normalized),
25233
+ source_db_path: normalized
25234
+ };
25235
+ }
25236
+ function normalizePatterns(patterns) {
25237
+ return (patterns ?? []).map((pattern) => pattern.trim()).filter(Boolean);
25238
+ }
25239
+ function escapeRegExp(value) {
25240
+ return value.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
25241
+ }
25242
+ function globPatternToRegExp(pattern) {
25243
+ let source9 = "";
25244
+ for (const char of pattern) {
25245
+ if (char === "*")
25246
+ source9 += ".*";
25247
+ else if (char === "?")
25248
+ source9 += ".";
25249
+ else
25250
+ source9 += escapeRegExp(char);
25251
+ }
25252
+ return new RegExp(`^${source9}$`);
25253
+ }
25254
+ function matchesPattern3(value, pattern) {
25255
+ const normalizedValue = value.replace(/\\/g, "/");
25256
+ const normalizedPattern = pattern.replace(/\\/g, "/");
25257
+ if (normalizedPattern.includes("*") || normalizedPattern.includes("?")) {
25258
+ return globPatternToRegExp(normalizedPattern).test(normalizedValue);
25259
+ }
25260
+ return normalizedValue.includes(normalizedPattern);
25261
+ }
25262
+ function storeMatchesAny(ref, patterns) {
25263
+ if (patterns.length === 0)
25264
+ return false;
25265
+ const paths = [ref.source_db_path, ref.source_repo_path].filter((value) => Boolean(value));
25266
+ const values = paths.flatMap((value) => [value, basename2(value)]);
25267
+ return patterns.some((pattern) => values.some((value) => matchesPattern3(value, pattern)));
25268
+ }
25269
+ function shouldIncludeStore(ref, include, exclude) {
25270
+ const included = include.length === 0 || storeMatchesAny(ref, include);
25271
+ return included && !storeMatchesAny(ref, exclude);
25272
+ }
25273
+ function discoverStoresUnderRoot(sourceRoot) {
25274
+ const rootPath = normalizePath3(sourceRoot);
25275
+ const errors = [];
25276
+ const stores = [];
25277
+ if (!existsSync9(rootPath)) {
25278
+ const ref = createStoreRef(join10(rootPath, TODO_STORE_RELATIVE_PATH));
25279
+ errors.push({
25280
+ ...ref,
25281
+ code: "SOURCE_ROOT_MISSING",
25282
+ message: `Source root does not exist: ${rootPath}`
25283
+ });
25284
+ return { stores, errors };
25285
+ }
25286
+ let rootStat;
25287
+ try {
25288
+ rootStat = statSync3(rootPath);
25289
+ } catch (error) {
25290
+ const ref = createStoreRef(join10(rootPath, TODO_STORE_RELATIVE_PATH));
25291
+ errors.push({
25292
+ ...ref,
25293
+ code: "SOURCE_ROOT_UNREADABLE",
25294
+ message: error instanceof Error ? error.message : `Unable to read source root: ${rootPath}`
25295
+ });
25296
+ return { stores, errors };
25297
+ }
25298
+ if (rootStat.isFile()) {
25299
+ stores.push(createStoreRef(rootPath));
25300
+ return { stores, errors };
25301
+ }
25302
+ function scanDirectory(dir, depth) {
25303
+ const candidate = join10(dir, TODO_STORE_RELATIVE_PATH);
25304
+ if (existsSync9(candidate)) {
25305
+ stores.push(createStoreRef(candidate));
25306
+ }
25307
+ if (depth >= ROOT_SCAN_MAX_DEPTH)
25308
+ return;
25309
+ let entries;
25310
+ try {
25311
+ entries = readdirSync2(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
25312
+ } catch (error) {
25313
+ const ref = createStoreRef(candidate);
25314
+ errors.push({
25315
+ ...ref,
25316
+ code: "SOURCE_ROOT_UNREADABLE",
25317
+ message: error instanceof Error ? error.message : `Unable to read source root: ${dir}`
25318
+ });
25319
+ return;
25320
+ }
25321
+ for (const entry2 of entries) {
25322
+ if (!entry2.isDirectory() || SKIPPED_SCAN_DIRS.has(entry2.name))
25323
+ continue;
25324
+ scanDirectory(join10(dir, entry2.name), depth + 1);
25325
+ }
25326
+ }
25327
+ scanDirectory(rootPath, 0);
25328
+ return { stores, errors };
25329
+ }
25330
+ function collectStoreRefs(input) {
25331
+ const byPath = new Map;
25332
+ const errors = [];
25333
+ for (const storePath of input.sourceStores ?? []) {
25334
+ const ref = createStoreRef(storePath);
25335
+ byPath.set(ref.source_db_path, ref);
25336
+ }
25337
+ for (const sourceRoot of input.sourceRoots ?? []) {
25338
+ const discovered = discoverStoresUnderRoot(sourceRoot);
25339
+ for (const ref of discovered.stores) {
25340
+ byPath.set(ref.source_db_path, ref);
25341
+ }
25342
+ errors.push(...discovered.errors);
25343
+ }
25344
+ return {
25345
+ stores: [...byPath.values()].sort((a, b) => a.source_db_path.localeCompare(b.source_db_path)),
25346
+ errors: errors.sort((a, b) => a.source_db_path.localeCompare(b.source_db_path))
25347
+ };
25348
+ }
25349
+ function openReadonlyStore(ref) {
25350
+ if (!existsSync9(ref.source_db_path)) {
25351
+ throw Object.assign(new Error(`Store does not exist: ${ref.source_db_path}`), { code: "STORE_MISSING" });
25352
+ }
25353
+ return new Database3(ref.source_db_path, { readonly: true, create: false });
25354
+ }
25355
+ function hasTable2(db, tableName) {
25356
+ const row = db.query("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?").get(tableName);
25357
+ return Boolean(row);
25358
+ }
25359
+ function tableColumns2(db, tableName) {
25360
+ const rows = db.query(`PRAGMA table_info(${tableName})`).all();
25361
+ return new Set(rows.map((row) => row.name));
25362
+ }
25363
+ function listPendingTasksReadonly(db) {
25364
+ if (!hasTable2(db, "tasks")) {
25365
+ throw Object.assign(new Error("Store does not contain a tasks table"), { code: "STORE_INVALID" });
25366
+ }
25367
+ const columns = tableColumns2(db, "tasks");
25368
+ const conditions = ["status = 'pending'"];
25369
+ if (columns.has("archived_at"))
25370
+ conditions.push("archived_at IS NULL");
25371
+ const rows = db.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")}
25372
+ ORDER BY CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END, created_at DESC`).all();
25373
+ return rows.map(rowToTask);
25374
+ }
25375
+ function isReadyTask(task2, db) {
25376
+ if (task2.locked_by && !isLockExpired(task2.locked_at))
25377
+ return false;
25378
+ return getBlockingDeps(task2.id, db).length === 0;
25379
+ }
25380
+ function metadataFingerprint(metadata) {
25381
+ const value = metadata.fingerprint;
25382
+ if (typeof value === "string" && value.trim())
25383
+ return value;
25384
+ if (typeof value === "number" && Number.isFinite(value))
25385
+ return String(value);
25386
+ return null;
25387
+ }
25388
+ function boundedMetadataValue(value, depth = 0) {
25389
+ if (depth > 6)
25390
+ return "[TRUNCATED]";
25391
+ if (typeof value === "string") {
25392
+ return value.length > 2000 ? `${value.slice(0, 2000)}[TRUNCATED]` : value;
25393
+ }
25394
+ if (Array.isArray(value)) {
25395
+ return value.slice(0, 50).map((item) => boundedMetadataValue(item, depth + 1));
25396
+ }
25397
+ if (value && typeof value === "object") {
25398
+ const result = {};
25399
+ for (const [key, child] of Object.entries(value).slice(0, 80)) {
25400
+ const normalized = key.toLowerCase();
25401
+ if (normalized === "comment" || normalized === "comments" || normalized === "task_comments") {
25402
+ result[key] = "[REDACTED_COMMENT]";
25403
+ continue;
25404
+ }
25405
+ result[key] = boundedMetadataValue(child, depth + 1);
25406
+ }
25407
+ return result;
25408
+ }
25409
+ return value;
25410
+ }
25411
+ function discoveryMetadata(metadata) {
25412
+ return redactValue(boundedMetadataValue(metadata));
25413
+ }
25414
+ function sourceCandidate(ref, task2, db) {
25415
+ const routeState = getTaskRouteState(task2, db);
25416
+ const autoRoute = task2.tags.includes("auto:route") || task2.tags.includes("route:enabled");
25417
+ return {
25418
+ source_store_id: ref.source_store_id,
25419
+ source_repo_path: ref.source_repo_path,
25420
+ source_db_path: ref.source_db_path,
25421
+ source_task_key: `${ref.source_store_id}:${task2.id}`,
25422
+ source_selected_by_input: true,
25423
+ task_id: task2.id,
25424
+ task_short_id: task2.short_id,
25425
+ title: task2.title,
25426
+ status: task2.status,
25427
+ priority: task2.priority,
25428
+ project_path: routeState.route.project_path ?? task2.working_dir ?? ref.source_repo_path,
25429
+ task_version: task2.version,
25430
+ task_updated_at: task2.updated_at,
25431
+ task_fingerprint: metadataFingerprint(task2.metadata),
25432
+ tags: task2.tags,
25433
+ task_intent: {
25434
+ auto_route: autoRoute
25435
+ },
25436
+ metadata: discoveryMetadata(task2.metadata),
25437
+ route_state: routeState
25438
+ };
25439
+ }
25440
+ function discoveryError(ref, code, error) {
25441
+ return {
25442
+ ...ref,
25443
+ code,
25444
+ message: error instanceof Error ? error.message : String(error)
25445
+ };
25446
+ }
25447
+ function errorCode(error) {
25448
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "STORE_MISSING") {
25449
+ return "STORE_MISSING";
25450
+ }
25451
+ if (typeof error === "object" && error !== null && "code" in error && error.code === "STORE_INVALID") {
25452
+ return "STORE_INVALID";
25453
+ }
25454
+ return "STORE_UNREADABLE";
25455
+ }
25456
+ function discoverTaskRouteSources(input) {
25457
+ const include = normalizePatterns(input.include);
25458
+ const exclude = normalizePatterns(input.exclude);
25459
+ const sourceRoots = (input.sourceRoots ?? []).map(normalizePath3).sort();
25460
+ const sourceStores = (input.sourceStores ?? []).map(normalizePath3).sort();
25461
+ const limit = Number.isFinite(input.limit ?? NaN) && (input.limit ?? 0) >= 0 ? Math.floor(input.limit ?? 0) : null;
25462
+ const collected = collectStoreRefs(input);
25463
+ const stores = [];
25464
+ const errors = [...collected.errors];
25465
+ const candidates = [];
25466
+ let totalCandidateCount = 0;
25467
+ for (const ref of collected.stores) {
25468
+ if (!shouldIncludeStore(ref, include, exclude))
25469
+ continue;
25470
+ const storeErrors = [];
25471
+ let db = null;
25472
+ try {
25473
+ db = openReadonlyStore(ref);
25474
+ const readyTasks = listPendingTasksReadonly(db).filter((task2) => isReadyTask(task2, db));
25475
+ totalCandidateCount += readyTasks.length;
25476
+ const remaining = limit === null ? readyTasks.length : Math.max(0, limit - candidates.length);
25477
+ const selectedTasks = limit === null ? readyTasks : readyTasks.slice(0, remaining);
25478
+ candidates.push(...selectedTasks.map((task2) => sourceCandidate(ref, task2, db)));
25479
+ stores.push({
25480
+ ...ref,
25481
+ status: "ok",
25482
+ candidate_count: readyTasks.length,
25483
+ returned_candidate_count: selectedTasks.length,
25484
+ errors: []
25485
+ });
25486
+ } catch (error) {
25487
+ const storeError = discoveryError(ref, errorCode(error), error);
25488
+ storeErrors.push(storeError);
25489
+ errors.push(storeError);
25490
+ stores.push({
25491
+ ...ref,
25492
+ status: storeError.code === "STORE_MISSING" ? "missing" : "error",
25493
+ candidate_count: 0,
25494
+ returned_candidate_count: 0,
25495
+ errors: storeErrors
25496
+ });
25497
+ } finally {
25498
+ db?.close();
25499
+ }
25500
+ }
25501
+ return {
25502
+ schema_version: TASK_ROUTE_SOURCE_DISCOVERY_SCHEMA_VERSION,
25503
+ sourceRoots,
25504
+ sourceStores,
25505
+ include,
25506
+ exclude,
25507
+ limit,
25508
+ total_candidate_count: totalCandidateCount,
25509
+ returned_candidate_count: candidates.length,
25510
+ truncated: limit !== null && totalCandidateCount > candidates.length,
25511
+ stores,
25512
+ candidates,
25513
+ errors
25514
+ };
25515
+ }
24829
25516
 
24830
25517
  // src/index.ts
24831
25518
  init_database();
@@ -24963,8 +25650,8 @@ function listCyclesWithStats(options = {}, db) {
24963
25650
  }
24964
25651
  // src/lib/plan-artifacts.ts
24965
25652
  init_database();
24966
- import { existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
24967
- import { join as join10, resolve as resolve10 } from "path";
25653
+ import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
25654
+ import { join as join11, resolve as resolve11 } from "path";
24968
25655
  var PLAN_MARKDOWN_SCHEMA = "hasna.todos.plan/v1";
24969
25656
  function assertSafePathSegment(value, label) {
24970
25657
  const trimmed = value.trim();
@@ -25000,12 +25687,15 @@ function projectSlugMatches(project, ref) {
25000
25687
  const normalized = slugify(ref);
25001
25688
  return Boolean(normalized) && (project.task_list_id === normalized || slugify(project.name) === normalized);
25002
25689
  }
25690
+ function planArtifactSlug(plan) {
25691
+ return slugify(plan.slug || plan.name) || "plan";
25692
+ }
25003
25693
  function resolvePlanArtifactProject(input) {
25004
25694
  const db = input.db || getDatabase();
25005
25695
  const ref = input.project_id || input.project_ref;
25006
25696
  if (!ref)
25007
25697
  throw new Error("Plan artifacts require a project id or project reference");
25008
- const byPath = getProjectByPath(resolve10(ref), db);
25698
+ const byPath = getProjectByPath(resolve11(ref), db);
25009
25699
  if (byPath)
25010
25700
  return byPath;
25011
25701
  const resolvedId = resolvePartialId(db, "projects", ref);
@@ -25022,14 +25712,27 @@ function resolvePlanArtifactProject(input) {
25022
25712
  function resolvePlanArtifactPaths(input) {
25023
25713
  const project = resolvePlanArtifactProject(input);
25024
25714
  const projectId = assertSafePathSegment(project.id, "project id");
25025
- const projectRoot = resolve10(project.path);
25026
- const directory = join10(projectRoot, ".hasna", "todos", "plans", projectId);
25715
+ const projectRoot = resolve11(project.path);
25716
+ const directory = join11(projectRoot, ".hasna", "todos", "plans", projectId);
25027
25717
  const planId = input.plan_id ? assertSafePathSegment(input.plan_id, "plan id") : null;
25718
+ const planSlug = input.plan_slug ? assertSafePathSegment(slugify(input.plan_slug), "plan slug") : null;
25719
+ const fileName = planId ? planSlug ? `${planSlug}--${planId.slice(0, 8)}.md` : `${planId}.md` : null;
25028
25720
  return {
25029
25721
  project_id: project.id,
25030
25722
  project_root: projectRoot,
25031
25723
  directory,
25032
- file_path: planId ? join10(directory, `${planId}.md`) : directory
25724
+ file_path: fileName ? join11(directory, fileName) : directory
25725
+ };
25726
+ }
25727
+ function resolvePlanArtifactCandidatePaths(plan, db) {
25728
+ return {
25729
+ primary: resolvePlanArtifactPaths({
25730
+ project_id: plan.project_id,
25731
+ plan_id: plan.id,
25732
+ plan_slug: planArtifactSlug(plan),
25733
+ db
25734
+ }),
25735
+ legacy: resolvePlanArtifactPaths({ project_id: plan.project_id, plan_id: plan.id, db })
25033
25736
  };
25034
25737
  }
25035
25738
  function buildPlanArtifactSnapshot(plan, tasks = [], artifactUpdatedAt = new Date().toISOString()) {
@@ -25046,6 +25749,7 @@ function buildPlanArtifactSnapshot(plan, tasks = [], artifactUpdatedAt = new Dat
25046
25749
  metadata: {
25047
25750
  schema: PLAN_MARKDOWN_SCHEMA,
25048
25751
  plan_id: plan.id,
25752
+ plan_slug: plan.slug ?? null,
25049
25753
  project_id: plan.project_id,
25050
25754
  task_list_id: plan.task_list_id ?? null,
25051
25755
  agent_id: plan.agent_id ?? null,
@@ -25085,6 +25789,7 @@ function renderPlanArtifactMarkdown(snapshot) {
25085
25789
  "---",
25086
25790
  `schema: ${frontmatterScalar(metadata.schema)}`,
25087
25791
  `plan_id: ${frontmatterScalar(metadata.plan_id)}`,
25792
+ `plan_slug: ${frontmatterScalar(metadata.plan_slug)}`,
25088
25793
  `project_id: ${frontmatterScalar(metadata.project_id)}`,
25089
25794
  `task_list_id: ${frontmatterScalar(metadata.task_list_id)}`,
25090
25795
  `agent_id: ${frontmatterScalar(metadata.agent_id)}`,
@@ -25130,6 +25835,7 @@ function parsePlanArtifactMarkdown(markdown) {
25130
25835
  metadata: {
25131
25836
  schema: PLAN_MARKDOWN_SCHEMA,
25132
25837
  plan_id: rawMetadata.plan_id,
25838
+ plan_slug: rawMetadata.plan_slug ?? null,
25133
25839
  project_id: rawMetadata.project_id,
25134
25840
  task_list_id: rawMetadata.task_list_id ?? null,
25135
25841
  agent_id: rawMetadata.agent_id ?? null,
@@ -25170,7 +25876,7 @@ function writePlanArtifact(plan, db) {
25170
25876
  return null;
25171
25877
  const d = db || getDatabase();
25172
25878
  const tasks = listTasks({ plan_id: plan.id, include_archived: true }, d);
25173
- const paths = resolvePlanArtifactPaths({ project_id: plan.project_id, plan_id: plan.id, db: d });
25879
+ const paths = resolvePlanArtifactCandidatePaths(plan, d).primary;
25174
25880
  const snapshot = buildPlanArtifactSnapshot(plan, tasks);
25175
25881
  mkdirSync8(paths.directory, { recursive: true });
25176
25882
  writeFileSync6(paths.file_path, renderPlanArtifactMarkdown(snapshot), "utf8");
@@ -25180,12 +25886,13 @@ function readPlanArtifact(plan, db) {
25180
25886
  if (!plan.project_id)
25181
25887
  return null;
25182
25888
  const d = db || getDatabase();
25183
- const paths = resolvePlanArtifactPaths({ project_id: plan.project_id, plan_id: plan.id, db: d });
25184
- if (!existsSync9(paths.file_path))
25889
+ const paths = resolvePlanArtifactCandidatePaths(plan, d);
25890
+ const path = existsSync10(paths.primary.file_path) ? paths.primary.file_path : existsSync10(paths.legacy.file_path) ? paths.legacy.file_path : null;
25891
+ if (!path)
25185
25892
  return null;
25186
- const markdown = readFileSync7(paths.file_path, "utf8");
25893
+ const markdown = readFileSync7(path, "utf8");
25187
25894
  return {
25188
- path: paths.file_path,
25895
+ path,
25189
25896
  markdown,
25190
25897
  ...parsePlanArtifactMarkdown(markdown)
25191
25898
  };
@@ -25194,10 +25901,11 @@ function inspectPlanArtifact(plan, db) {
25194
25901
  if (!plan.project_id)
25195
25902
  return null;
25196
25903
  const d = db || getDatabase();
25197
- const paths = resolvePlanArtifactPaths({ project_id: plan.project_id, plan_id: plan.id, db: d });
25198
- if (!existsSync9(paths.file_path)) {
25904
+ const paths = resolvePlanArtifactCandidatePaths(plan, d);
25905
+ const path = existsSync10(paths.primary.file_path) ? paths.primary.file_path : existsSync10(paths.legacy.file_path) ? paths.legacy.file_path : null;
25906
+ if (!path) {
25199
25907
  return {
25200
- path: paths.file_path,
25908
+ path: paths.primary.file_path,
25201
25909
  exists: false,
25202
25910
  parse_error: null,
25203
25911
  metadata: null,
@@ -25206,9 +25914,9 @@ function inspectPlanArtifact(plan, db) {
25206
25914
  };
25207
25915
  }
25208
25916
  try {
25209
- const artifact = parsePlanArtifactMarkdown(readFileSync7(paths.file_path, "utf8"));
25917
+ const artifact = parsePlanArtifactMarkdown(readFileSync7(path, "utf8"));
25210
25918
  return {
25211
- path: paths.file_path,
25919
+ path,
25212
25920
  exists: true,
25213
25921
  parse_error: null,
25214
25922
  metadata: artifact.metadata,
@@ -25217,7 +25925,7 @@ function inspectPlanArtifact(plan, db) {
25217
25925
  };
25218
25926
  } catch (error) {
25219
25927
  return {
25220
- path: paths.file_path,
25928
+ path,
25221
25929
  exists: true,
25222
25930
  parse_error: error instanceof Error ? error.message : String(error),
25223
25931
  metadata: null,
@@ -25229,6 +25937,9 @@ function inspectPlanArtifact(plan, db) {
25229
25937
  function comparePlanArtifact(plan, artifact, tasks) {
25230
25938
  const conflicts = [];
25231
25939
  compare("plan_id", plan.id, artifact.metadata.plan_id, conflicts);
25940
+ if (artifact.metadata.plan_slug !== null) {
25941
+ compare("plan_slug", plan.slug ?? null, artifact.metadata.plan_slug, conflicts);
25942
+ }
25232
25943
  compare("project_id", plan.project_id ?? null, artifact.metadata.project_id, conflicts);
25233
25944
  compare("name", plan.name, artifact.metadata.name, conflicts);
25234
25945
  compare("status", plan.status, artifact.metadata.status, conflicts);
@@ -26277,28 +26988,28 @@ function renderRetrospectiveMarkdown(record) {
26277
26988
  }
26278
26989
  // src/lib/project-bootstrap.ts
26279
26990
  init_database();
26280
- import { existsSync as existsSync10, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
26281
- import { basename as basename2, dirname as dirname7, resolve as resolve11 } from "path";
26991
+ import { existsSync as existsSync11, readFileSync as readFileSync8, statSync as statSync4 } from "fs";
26992
+ import { basename as basename3, dirname as dirname8, resolve as resolve12 } from "path";
26282
26993
  function safeStat(path) {
26283
26994
  try {
26284
- return statSync3(path);
26995
+ return statSync4(path);
26285
26996
  } catch {
26286
26997
  return null;
26287
26998
  }
26288
26999
  }
26289
27000
  function canonicalPath(input) {
26290
- const resolved = resolve11(input);
27001
+ const resolved = resolve12(input);
26291
27002
  const stats2 = safeStat(resolved);
26292
27003
  if (stats2?.isFile())
26293
- return dirname7(resolved);
27004
+ return dirname8(resolved);
26294
27005
  return resolved;
26295
27006
  }
26296
27007
  function findUp(start, marker) {
26297
27008
  let current = canonicalPath(start);
26298
27009
  while (true) {
26299
- if (existsSync10(resolve11(current, marker)))
27010
+ if (existsSync11(resolve12(current, marker)))
26300
27011
  return current;
26301
- const parent = dirname7(current);
27012
+ const parent = dirname8(current);
26302
27013
  if (parent === current)
26303
27014
  return null;
26304
27015
  current = parent;
@@ -26307,8 +27018,8 @@ function findUp(start, marker) {
26307
27018
  function readPackageJson2(path) {
26308
27019
  if (!path)
26309
27020
  return null;
26310
- const file = resolve11(path, "package.json");
26311
- if (!existsSync10(file))
27021
+ const file = resolve12(path, "package.json");
27022
+ if (!existsSync11(file))
26312
27023
  return null;
26313
27024
  try {
26314
27025
  const parsed = JSON.parse(readFileSync8(file, "utf-8"));
@@ -26319,9 +27030,9 @@ function readPackageJson2(path) {
26319
27030
  }
26320
27031
  function packageDisplayName(name, fallbackPath) {
26321
27032
  if (!name)
26322
- return basename2(fallbackPath);
27033
+ return basename3(fallbackPath);
26323
27034
  const withoutScope = name.startsWith("@") ? name.split("/")[1] : name;
26324
- return withoutScope || basename2(fallbackPath);
27035
+ return withoutScope || basename3(fallbackPath);
26325
27036
  }
26326
27037
  function workspaceMarker(root, rootPackage) {
26327
27038
  if (!root)
@@ -26330,7 +27041,7 @@ function workspaceMarker(root, rootPackage) {
26330
27041
  if (rootPackage?.workspaces)
26331
27042
  markers.push("package.json#workspaces");
26332
27043
  for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
26333
- if (existsSync10(resolve11(root, marker)))
27044
+ if (existsSync11(resolve12(root, marker)))
26334
27045
  markers.push(marker);
26335
27046
  }
26336
27047
  const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
@@ -26448,177 +27159,9 @@ function getProjectByPathForBootstrap(path, db) {
26448
27159
  WHERE pmp.path = ?`).get(path);
26449
27160
  return machineRow ?? null;
26450
27161
  }
26451
- // src/lib/task-routing.ts
26452
- init_database();
26453
- function classifyProjectKind2(path) {
26454
- if (!path)
26455
- return null;
26456
- return path.includes("/hasna/opensource/") ? "open-source" : "unknown";
26457
- }
26458
- function machineLocalPath(project, db) {
26459
- const machineId = process.env["TODOS_MACHINE_ID"];
26460
- if (!machineId)
26461
- return null;
26462
- try {
26463
- const row = db.query("SELECT path FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(project.id, machineId);
26464
- return row?.path ?? null;
26465
- } catch {
26466
- return null;
26467
- }
26468
- }
26469
- function resolveProject(task2, db) {
26470
- const project = task2.project_id ? getProject(task2.project_id, db) : null;
26471
- const projectPath = project ? machineLocalPath(project, db) ?? project.path : task2.working_dir;
26472
- return { project, projectPath: projectPath ?? null };
26473
- }
26474
- function resolveTaskList(task2, project, db) {
26475
- if (task2.task_list_id) {
26476
- return getTaskList(task2.task_list_id, db) ?? (project ? getTaskListBySlug(task2.task_list_id, project.id, db) : null);
26477
- }
26478
- if (project?.task_list_id) {
26479
- return getTaskListBySlug(project.task_list_id, project.id, db);
26480
- }
26481
- return null;
26482
- }
26483
- function isTerminal2(status) {
26484
- return status === "completed" || status === "cancelled" || status === "failed";
26485
- }
26486
- function routeConcurrencyKey(task2, project, taskList, projectPath) {
26487
- if (project?.id)
26488
- return `project:${project.id}`;
26489
- if (taskList?.id)
26490
- return `task-list:${taskList.id}`;
26491
- if (projectPath)
26492
- return `path:${projectPath}`;
26493
- return `task:${task2.id}`;
26494
- }
26495
- function getTaskRouteState(taskOrId, db) {
26496
- const d = db || getDatabase();
26497
- const task2 = typeof taskOrId === "string" ? getTask(taskOrId, d) : taskOrId;
26498
- if (!task2)
26499
- throw new Error(`Task not found: ${taskOrId}`);
26500
- const { project, projectPath } = resolveProject(task2, d);
26501
- const taskList = resolveTaskList(task2, project, d);
26502
- const automation = routingAutomationMetadata(task2, taskList) ?? {};
26503
- const routeEnabled = routeEnabledForTask(task2, taskList) === true;
26504
- const tagOptIn = task2.tags.includes("auto:route") || task2.tags.includes("route:enabled");
26505
- const locked = Boolean(task2.locked_by && !isLockExpired(task2.locked_at));
26506
- const blockers = getBlockingDeps(task2.id, d);
26507
- const blocked = blockers.length > 0;
26508
- const terminal = isTerminal2(task2.status);
26509
- const requiresApproval = automation.requires_approval === true || task2.requires_approval === true;
26510
- const approvalRequired = automation.approval_required === true;
26511
- const approved = Boolean(task2.approved_by);
26512
- const gates = {
26513
- route_enabled: routeEnabled,
26514
- tag_opt_in: tagOptIn,
26515
- no_auto: automation.no_auto === true,
26516
- manual: automation.manual === true,
26517
- manual_required: automation.manual_required === true,
26518
- requires_approval: requiresApproval,
26519
- approval_required: approvalRequired,
26520
- approved,
26521
- locked,
26522
- blocked,
26523
- terminal
26524
- };
26525
- const reasons = [];
26526
- if (task2.status !== "pending")
26527
- reasons.push("task_not_pending");
26528
- if (terminal)
26529
- reasons.push("task_terminal");
26530
- if (!routeEnabled)
26531
- reasons.push("route_not_enabled");
26532
- if (locked)
26533
- reasons.push("task_locked");
26534
- if (blocked)
26535
- reasons.push("task_blocked");
26536
- if (gates.no_auto)
26537
- reasons.push("no_auto");
26538
- if (gates.manual)
26539
- reasons.push("manual");
26540
- if (gates.manual_required)
26541
- reasons.push("manual_required");
26542
- if (requiresApproval && !approved)
26543
- reasons.push("requires_approval");
26544
- if (approvalRequired && !approved)
26545
- reasons.push("approval_required");
26546
- if (automation.allowed === false)
26547
- reasons.push("automation_disallowed");
26548
- return {
26549
- schema_version: TODOS_TASK_ROUTE_STATE_SCHEMA_VERSION,
26550
- task_id: task2.id,
26551
- task_short_id: task2.short_id,
26552
- status: task2.status,
26553
- eligible: reasons.length === 0,
26554
- reasons,
26555
- blockers: blockers.map((blocker) => ({
26556
- id: blocker.id,
26557
- short_id: blocker.short_id,
26558
- title: blocker.title,
26559
- status: blocker.status
26560
- })),
26561
- gates,
26562
- automation: Object.keys(automation).length > 0 ? automation : null,
26563
- route: {
26564
- project_id: project?.id ?? task2.project_id,
26565
- project_path: projectPath,
26566
- working_dir: task2.working_dir ?? projectPath,
26567
- project_kind: classifyProjectKind2(projectPath),
26568
- task_list_id: taskList?.id ?? task2.task_list_id,
26569
- task_list_slug: taskList?.slug ?? null,
26570
- task_list_name: taskList?.name ?? null,
26571
- concurrency_key: routeConcurrencyKey(task2, project, taskList, projectPath)
26572
- },
26573
- pointers: workflowPointersFromMetadata(task2.metadata)
26574
- };
26575
- }
26576
- function setTaskWorkflowPointers(taskId, input, db) {
26577
- const d = db || getDatabase();
26578
- const task2 = getTask(taskId, d);
26579
- if (!task2)
26580
- throw new Error(`Task not found: ${taskId}`);
26581
- const previous = workflowPointersFromMetadata(task2.metadata);
26582
- const next = compactWorkflowPointers({
26583
- current_workflow_invocation_id: pointerPatch(previous.current_workflow_invocation_id, input, "current_workflow_invocation_id"),
26584
- current_run_id: pointerPatch(previous.current_run_id, input, "current_run_id"),
26585
- latest_manifest_path: pointerPatch(previous.latest_manifest_path, input, "latest_manifest_path"),
26586
- latest_evaluation_path: pointerPatch(previous.latest_evaluation_path, input, "latest_evaluation_path"),
26587
- workflow_state: pointerPatch(previous.workflow_state, input, "workflow_state")
26588
- });
26589
- const timestamp2 = now();
26590
- const {
26591
- current_workflow_invocation_id,
26592
- current_run_id,
26593
- latest_manifest_path,
26594
- latest_evaluation_path,
26595
- workflow_state,
26596
- workflow_invocation,
26597
- ...baseMetadata
26598
- } = task2.metadata;
26599
- const metadata = {
26600
- ...baseMetadata,
26601
- ...next,
26602
- workflow_invocation: {
26603
- schema_version: TASK_WORKFLOW_POINTER_SCHEMA_VERSION,
26604
- ...next,
26605
- updated_at: timestamp2,
26606
- updated_by: input.actor ?? null
26607
- }
26608
- };
26609
- return updateTask(task2.id, { version: task2.version, metadata }, d);
26610
- }
26611
- function pointerPatch(previous, input, key) {
26612
- if (!Object.prototype.hasOwnProperty.call(input, key))
26613
- return previous;
26614
- const value = input[key];
26615
- if (value === undefined)
26616
- return previous;
26617
- return typeof value === "string" && value.trim() ? value : undefined;
26618
- }
26619
27162
  // src/db/api-keys.ts
26620
27163
  init_database();
26621
- import { createHash as createHash10, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual3 } from "crypto";
27164
+ import { createHash as createHash11, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual3 } from "crypto";
26622
27165
  function rowToRecord(row) {
26623
27166
  return {
26624
27167
  id: row.id,
@@ -26632,7 +27175,7 @@ function rowToRecord(row) {
26632
27175
  };
26633
27176
  }
26634
27177
  function hashApiKey(key) {
26635
- return createHash10("sha256").update(key).digest("hex");
27178
+ return createHash11("sha256").update(key).digest("hex");
26636
27179
  }
26637
27180
  function safeEqualHex(a, b) {
26638
27181
  if (a.length !== b.length)
@@ -26823,18 +27366,18 @@ var gatherTrainingData = async (options = {}) => {
26823
27366
  };
26824
27367
  // src/lib/model-config.ts
26825
27368
  init_sync_utils();
26826
- import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
26827
- import { join as join11 } from "path";
27369
+ import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
27370
+ import { join as join12 } from "path";
26828
27371
  var DEFAULT_MODEL = "gpt-4o-mini";
26829
27372
  function getConfigDir() {
26830
27373
  return getTodosGlobalDir();
26831
27374
  }
26832
27375
  function getConfigPath2() {
26833
- return join11(getConfigDir(), "config.json");
27376
+ return join12(getConfigDir(), "config.json");
26834
27377
  }
26835
27378
  function readConfig() {
26836
27379
  const configPath = getConfigPath2();
26837
- if (!existsSync11(configPath))
27380
+ if (!existsSync12(configPath))
26838
27381
  return {};
26839
27382
  try {
26840
27383
  const raw = readFileSync9(configPath, "utf-8");
@@ -26845,7 +27388,7 @@ function readConfig() {
26845
27388
  }
26846
27389
  function writeConfig(config) {
26847
27390
  const configDir = getConfigDir();
26848
- if (!existsSync11(configDir)) {
27391
+ if (!existsSync12(configDir)) {
26849
27392
  mkdirSync9(configDir, { recursive: true });
26850
27393
  }
26851
27394
  writeFileSync7(getConfigPath2(), JSON.stringify(config, null, 2) + `
@@ -27570,7 +28113,7 @@ CLI equivalent: \`${r.equivalent_cli}\`
27570
28113
  `);
27571
28114
  }
27572
28115
  // src/lib/verification-providers.ts
27573
- import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
28116
+ import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
27574
28117
  init_database();
27575
28118
  init_config();
27576
28119
  init_redaction();
@@ -27681,7 +28224,7 @@ function classifyLog(text) {
27681
28224
  async function sleep2(ms) {
27682
28225
  if (ms <= 0)
27683
28226
  return;
27684
- await new Promise((resolve12) => setTimeout(resolve12, ms));
28227
+ await new Promise((resolve13) => setTimeout(resolve13, ms));
27685
28228
  }
27686
28229
  async function runCommandProvider(provider, input) {
27687
28230
  const commandTemplate = input.command || provider.command;
@@ -27736,7 +28279,7 @@ Timed out after ${provider.timeout_ms}ms`);
27736
28279
  };
27737
28280
  }
27738
28281
  function runCiLogProvider(input) {
27739
- const text = input.log_text ?? (input.log_path && existsSync12(input.log_path) ? readFileSync10(input.log_path, "utf-8") : "");
28282
+ const text = input.log_text ?? (input.log_path && existsSync13(input.log_path) ? readFileSync10(input.log_path, "utf-8") : "");
27740
28283
  return {
27741
28284
  status: classifyLog(text),
27742
28285
  attempts: 1,
@@ -27748,7 +28291,7 @@ function runBrowserProvider(input) {
27748
28291
  if (!input.artifact_path) {
27749
28292
  return { status: "unknown", attempts: 1, exit_code: null, output_summary: "browser provider needs a screenshot or artifact path" };
27750
28293
  }
27751
- if (!existsSync12(input.artifact_path)) {
28294
+ if (!existsSync13(input.artifact_path)) {
27752
28295
  return { status: "failed", attempts: 1, exit_code: null, output_summary: `artifact not found: ${input.artifact_path}` };
27753
28296
  }
27754
28297
  return {
@@ -27913,7 +28456,7 @@ function listVerificationRecords(filter = {}, db) {
27913
28456
  // src/lib/verification-evidence.ts
27914
28457
  init_database();
27915
28458
  import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync10 } from "fs";
27916
- import { dirname as dirname8 } from "path";
28459
+ import { dirname as dirname9 } from "path";
27917
28460
  var VERIFICATION_EVIDENCE_SCHEMA = "todos.verification_evidence.v1";
27918
28461
  function getMachineId3() {
27919
28462
  return process.env["TODOS_MACHINE_ID"] || __require("os").hostname();
@@ -28033,15 +28576,15 @@ function exportVerificationEvidence(filter = {}, db) {
28033
28576
  };
28034
28577
  }
28035
28578
  function writeVerificationExport(bundle, path) {
28036
- mkdirSync10(dirname8(path), { recursive: true });
28579
+ mkdirSync10(dirname9(path), { recursive: true });
28037
28580
  writeFileSync8(path, JSON.stringify(bundle, null, 2), "utf8");
28038
28581
  }
28039
28582
  // src/lib/policy-packs.ts
28040
- import { relative as relative3, resolve as resolve12 } from "path";
28583
+ import { relative as relative3, resolve as resolve13 } from "path";
28041
28584
  init_database();
28042
28585
  init_config();
28043
- function normalizePath3(path) {
28044
- return resolve12(path);
28586
+ function normalizePath4(path) {
28587
+ return resolve13(path);
28045
28588
  }
28046
28589
  function unique4(values) {
28047
28590
  return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
@@ -28051,13 +28594,13 @@ function parseStatuses(values) {
28051
28594
  return unique4(values).filter((value) => allowed.has(value));
28052
28595
  }
28053
28596
  function configuredPacks(config = loadConfig()) {
28054
- return Object.values(config.policy_packs || {}).map((pack) => ({ ...pack, root: normalizePath3(pack.root) })).sort((a, b) => a.name.localeCompare(b.name));
28597
+ return Object.values(config.policy_packs || {}).map((pack) => ({ ...pack, root: normalizePath4(pack.root) })).sort((a, b) => a.name.localeCompare(b.name));
28055
28598
  }
28056
28599
  function defaultPolicyPack(name, root) {
28057
28600
  return {
28058
28601
  name,
28059
28602
  version: 1,
28060
- root: normalizePath3(root),
28603
+ root: normalizePath4(root),
28061
28604
  required_commands: [],
28062
28605
  prohibited_commands: ["npm install -g", "git reset --hard", "git checkout --", "rm -rf"],
28063
28606
  prohibited_paths: [],
@@ -28075,7 +28618,7 @@ function isPathInside3(root, path) {
28075
28618
  const rel = relative3(root, path);
28076
28619
  return rel === "" || !rel.startsWith("..") && !rel.startsWith("/") && !/^[A-Za-z]:/.test(rel);
28077
28620
  }
28078
- function matchesPattern3(value, pattern) {
28621
+ function matchesPattern4(value, pattern) {
28079
28622
  const normalizedValue = value.toLowerCase();
28080
28623
  const normalizedPattern = pattern.toLowerCase();
28081
28624
  if (pattern.startsWith("/") && pattern.endsWith("/") && pattern.length > 2) {
@@ -28092,14 +28635,14 @@ function matchesPattern3(value, pattern) {
28092
28635
  return normalizedValue === normalizedPattern || normalizedValue.includes(normalizedPattern);
28093
28636
  }
28094
28637
  function commandMatches(commands, pattern) {
28095
- return commands.filter((command) => matchesPattern3(command, pattern));
28638
+ return commands.filter((command) => matchesPattern4(command, pattern));
28096
28639
  }
28097
28640
  function pathMatches(paths, pattern, root) {
28098
28641
  return paths.filter((path) => {
28099
- const candidate = path.startsWith("/") ? path : resolve12(root, path);
28642
+ const candidate = path.startsWith("/") ? path : resolve13(root, path);
28100
28643
  if (!isPathInside3(root, candidate))
28101
- return matchesPattern3(path, pattern);
28102
- return matchesPattern3(path, pattern) || matchesPattern3(relative3(root, candidate), pattern);
28644
+ return matchesPattern4(path, pattern);
28645
+ return matchesPattern4(path, pattern) || matchesPattern4(relative3(root, candidate), pattern);
28103
28646
  });
28104
28647
  }
28105
28648
  function finding(id, passed, message, evidence = []) {
@@ -28178,7 +28721,7 @@ function getPolicyPack(name) {
28178
28721
  function upsertPolicyPack(input) {
28179
28722
  const config = loadConfig();
28180
28723
  const existing = config.policy_packs?.[input.name];
28181
- const root = normalizePath3(input.root || existing?.root || process.cwd());
28724
+ const root = normalizePath4(input.root || existing?.root || process.cwd());
28182
28725
  const base = existing || defaultPolicyPack(input.name, root);
28183
28726
  const timestamp2 = new Date().toISOString();
28184
28727
  const pack = {
@@ -28258,7 +28801,7 @@ function validatePolicyPack(input, db) {
28258
28801
  findings.push(finding("linked-pull-request", refs.length > 0, "at least one linked pull request is required", refs.map((ref) => ref.name)));
28259
28802
  }
28260
28803
  if (pack.branch_pattern) {
28261
- const branches = evidence.gitRefs.filter((ref) => ref.ref_type === "branch" && matchesPattern3(ref.name, pack.branch_pattern));
28804
+ const branches = evidence.gitRefs.filter((ref) => ref.ref_type === "branch" && matchesPattern4(ref.name, pack.branch_pattern));
28262
28805
  findings.push(finding("branch-pattern", branches.length > 0, `at least one linked branch must match: ${pack.branch_pattern}`, branches.map((ref) => ref.name)));
28263
28806
  }
28264
28807
  if (pack.require_approval) {
@@ -28387,21 +28930,21 @@ function resourceDiagnostics() {
28387
28930
  };
28388
28931
  }
28389
28932
  // src/lib/sandbox-profiles.ts
28390
- import { existsSync as existsSync13, readFileSync as readFileSync11, writeFileSync as writeFileSync9, mkdirSync as mkdirSync11 } from "fs";
28391
- import { join as join12, dirname as dirname9 } from "path";
28933
+ import { existsSync as existsSync14, readFileSync as readFileSync11, writeFileSync as writeFileSync9, mkdirSync as mkdirSync11 } from "fs";
28934
+ import { join as join13, dirname as dirname10 } from "path";
28392
28935
  var SANDBOX_PROFILE_VERSION = "todos.sandbox-profile.v1";
28393
28936
  function getProfilesPath() {
28394
28937
  if (process.env["TODOS_SANDBOX_PROFILES_PATH"]) {
28395
28938
  return process.env["TODOS_SANDBOX_PROFILES_PATH"];
28396
28939
  }
28397
- const localDir = join12(process.cwd(), ".todos");
28398
- const local = join12(localDir, "sandbox-profiles.json");
28399
- if (existsSync13(localDir))
28940
+ const localDir = join13(process.cwd(), ".todos");
28941
+ const local = join13(localDir, "sandbox-profiles.json");
28942
+ if (existsSync14(localDir))
28400
28943
  return local;
28401
- if (existsSync13(local))
28944
+ if (existsSync14(local))
28402
28945
  return local;
28403
28946
  const home = process.env["HOME"] || "~";
28404
- return join12(home, ".hasna", "todos", "sandbox-profiles.json");
28947
+ return join13(home, ".hasna", "todos", "sandbox-profiles.json");
28405
28948
  }
28406
28949
  var cached2 = null;
28407
28950
  function resetSandboxProfileCache() {
@@ -28433,7 +28976,7 @@ function loadSandboxProfiles() {
28433
28976
  if (cached2)
28434
28977
  return cached2;
28435
28978
  const path = getProfilesPath();
28436
- if (!existsSync13(path)) {
28979
+ if (!existsSync14(path)) {
28437
28980
  cached2 = getDefaultSandboxProfiles();
28438
28981
  return cached2;
28439
28982
  }
@@ -28446,7 +28989,7 @@ function getSandboxProfile(name) {
28446
28989
  }
28447
28990
  function saveSandboxProfiles(profiles) {
28448
28991
  const path = getProfilesPath();
28449
- mkdirSync11(dirname9(path), { recursive: true });
28992
+ mkdirSync11(dirname10(path), { recursive: true });
28450
28993
  writeFileSync9(path, JSON.stringify({ schema_version: SANDBOX_PROFILE_VERSION, profiles }, null, 2));
28451
28994
  cached2 = profiles;
28452
28995
  }
@@ -28806,9 +29349,9 @@ function getDefaultAgentAdapters() {
28806
29349
  }
28807
29350
  function resetAgentAdapterCache() {}
28808
29351
  // src/lib/git-traceability.ts
28809
- import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
29352
+ import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
28810
29353
  import { spawnSync as spawnSync2 } from "child_process";
28811
- import { resolve as resolve13 } from "path";
29354
+ import { resolve as resolve14 } from "path";
28812
29355
  var GIT_TRACEABILITY_SCHEMA_VERSION = "todos.git_traceability.v1";
28813
29356
  function runGit(args, cwd) {
28814
29357
  const result = spawnSync2("git", args, { cwd, encoding: "utf8" });
@@ -28851,8 +29394,8 @@ function inspectGitCommit(sha, cwd) {
28851
29394
  };
28852
29395
  }
28853
29396
  function loadCiSnapshot(path) {
28854
- const target = path ? resolve13(path) : resolve13(process.cwd(), ".todos", "ci-snapshot.json");
28855
- if (!existsSync14(target))
29397
+ const target = path ? resolve14(path) : resolve14(process.cwd(), ".todos", "ci-snapshot.json");
29398
+ if (!existsSync15(target))
28856
29399
  return null;
28857
29400
  try {
28858
29401
  const parsed = JSON.parse(readFileSync12(target, "utf8"));
@@ -28948,8 +29491,8 @@ function formatTraceabilityReport(report) {
28948
29491
  `);
28949
29492
  }
28950
29493
  // src/lib/mention-resolver.ts
28951
- import { existsSync as existsSync15, readdirSync as readdirSync2, readFileSync as readFileSync13, statSync as statSync4 } from "fs";
28952
- import { basename as basename3, isAbsolute, join as join13, relative as relative4, resolve as resolve14, sep as sep2 } from "path";
29494
+ import { existsSync as existsSync16, readdirSync as readdirSync3, readFileSync as readFileSync13, statSync as statSync5 } from "fs";
29495
+ import { basename as basename4, isAbsolute, join as join14, relative as relative4, resolve as resolve15, sep as sep2 } from "path";
28953
29496
  init_database();
28954
29497
  var PREFIXES = {
28955
29498
  file: "file",
@@ -29025,7 +29568,7 @@ function backlink(kind, key, label, target = key) {
29025
29568
  return { kind, key, label, target };
29026
29569
  }
29027
29570
  function normalizeWorkspace(workspace) {
29028
- return resolve14(workspace || process.cwd());
29571
+ return resolve15(workspace || process.cwd());
29029
29572
  }
29030
29573
  function isInside(root, absolutePath) {
29031
29574
  const rel = relative4(root, absolutePath);
@@ -29093,18 +29636,18 @@ function resolveFile(parsed, workspace) {
29093
29636
  resolution.warnings.push("path is empty or escapes the workspace");
29094
29637
  return resolution;
29095
29638
  }
29096
- const absolutePath = resolve14(workspace, relPath);
29639
+ const absolutePath = resolve15(workspace, relPath);
29097
29640
  if (!isInside(workspace, absolutePath)) {
29098
29641
  resolution.path = relPath;
29099
29642
  resolution.warnings.push("path escapes the workspace");
29100
29643
  return resolution;
29101
29644
  }
29102
29645
  resolution.path = relPath;
29103
- if (!existsSync15(absolutePath)) {
29646
+ if (!existsSync16(absolutePath)) {
29104
29647
  resolution.warnings.push("file does not exist in the local workspace");
29105
29648
  return resolution;
29106
29649
  }
29107
- const stats2 = statSync4(absolutePath);
29650
+ const stats2 = statSync5(absolutePath);
29108
29651
  if (!stats2.isFile()) {
29109
29652
  resolution.warnings.push("path exists but is not a file");
29110
29653
  return resolution;
@@ -29128,12 +29671,12 @@ function resolveFile(parsed, workspace) {
29128
29671
  function walkSourceFiles(root, current = root, files = []) {
29129
29672
  if (files.length >= 5000)
29130
29673
  return files;
29131
- for (const entry2 of readdirSync2(current, { withFileTypes: true })) {
29674
+ for (const entry2 of readdirSync3(current, { withFileTypes: true })) {
29132
29675
  if (entry2.name.startsWith(".") && ![".github"].includes(entry2.name)) {
29133
29676
  if (SKIP_DIRS.has(entry2.name))
29134
29677
  continue;
29135
29678
  }
29136
- const absolutePath = join13(current, entry2.name);
29679
+ const absolutePath = join14(current, entry2.name);
29137
29680
  if (entry2.isDirectory()) {
29138
29681
  if (!SKIP_DIRS.has(entry2.name))
29139
29682
  walkSourceFiles(root, absolutePath, files);
@@ -29141,8 +29684,8 @@ function walkSourceFiles(root, current = root, files = []) {
29141
29684
  }
29142
29685
  if (!entry2.isFile())
29143
29686
  continue;
29144
- const extension = `.${basename3(entry2.name).split(".").pop() || ""}`;
29145
- if (SOURCE_EXTENSIONS.has(extension) && statSync4(absolutePath).size <= 512 * 1024) {
29687
+ const extension = `.${basename4(entry2.name).split(".").pop() || ""}`;
29688
+ if (SOURCE_EXTENSIONS.has(extension) && statSync5(absolutePath).size <= 512 * 1024) {
29146
29689
  files.push(absolutePath);
29147
29690
  }
29148
29691
  }
@@ -31013,9 +31556,9 @@ function getAdapterDocsFingerprint() {
31013
31556
  }
31014
31557
  // src/lib/inbox-intake.ts
31015
31558
  init_database();
31016
- import { existsSync as existsSync16, readFileSync as readFileSync14 } from "fs";
31017
- import { basename as basename4 } from "path";
31018
- import { createHash as createHash11 } from "crypto";
31559
+ import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
31560
+ import { basename as basename5 } from "path";
31561
+ import { createHash as createHash12 } from "crypto";
31019
31562
  init_secret_redaction();
31020
31563
  var INBOX_INTAKE_SCHEMA = "todos.inbox_intake.v1";
31021
31564
  var INTAKE_SOURCE_TYPES = [
@@ -31028,7 +31571,7 @@ var INTAKE_SOURCE_TYPES = [
31028
31571
  ];
31029
31572
  var INTAKE_TRIAGE_STATUSES = ["preview", "triaged", "duplicate", "created"];
31030
31573
  function fingerprint2(text) {
31031
- return createHash11("sha256").update(text).digest("hex").slice(0, 16);
31574
+ return createHash12("sha256").update(text).digest("hex").slice(0, 16);
31032
31575
  }
31033
31576
  function loadRawContent(input) {
31034
31577
  if (input.github_url) {
@@ -31059,15 +31602,15 @@ function loadRawContent(input) {
31059
31602
  }
31060
31603
  }
31061
31604
  if (input.file_path) {
31062
- if (!existsSync16(input.file_path))
31605
+ if (!existsSync17(input.file_path))
31063
31606
  throw new Error(`File not found: ${input.file_path}`);
31064
31607
  const raw = readFileSync14(input.file_path, "utf8");
31065
- const name = basename4(input.file_path).toLowerCase();
31608
+ const name = basename5(input.file_path).toLowerCase();
31066
31609
  const source_type2 = input.source_type ?? (name.includes("ci") || name.endsWith(".log") ? "ci_log" : "file");
31067
31610
  return {
31068
31611
  raw,
31069
31612
  source_type: source_type2,
31070
- metadata: { file_path: input.file_path, file_name: basename4(input.file_path) }
31613
+ metadata: { file_path: input.file_path, file_name: basename5(input.file_path) }
31071
31614
  };
31072
31615
  }
31073
31616
  const text = input.text?.trim();
@@ -31677,7 +32220,7 @@ function formatNlIntakePreviewText(preview) {
31677
32220
  }
31678
32221
  // src/lib/issue-importers.ts
31679
32222
  init_database();
31680
- import { existsSync as existsSync17, readFileSync as readFileSync15 } from "fs";
32223
+ import { existsSync as existsSync18, readFileSync as readFileSync15 } from "fs";
31681
32224
  var ISSUE_IMPORT_SCHEMA = "todos.issue_import.v1";
31682
32225
  var ISSUE_SOURCES = ["github", "linear", "jira", "auto"];
31683
32226
  var GITHUB_LABEL_PRIORITY = {
@@ -31896,7 +32439,7 @@ function parseIssueExport(data, source9 = "auto") {
31896
32439
  return normalized;
31897
32440
  }
31898
32441
  function loadIssueExportFromFile(path) {
31899
- if (!existsSync17(path))
32442
+ if (!existsSync18(path))
31900
32443
  throw new Error(`File not found: ${path}`);
31901
32444
  return JSON.parse(readFileSync15(path, "utf8"));
31902
32445
  }
@@ -32056,8 +32599,8 @@ todos import issues ./linear.json --source linear --dry-run
32056
32599
  // src/lib/run-records.ts
32057
32600
  init_database();
32058
32601
  init_secret_redaction();
32059
- import { existsSync as existsSync18, mkdirSync as mkdirSync12, writeFileSync as writeFileSync10 } from "fs";
32060
- import { join as join14, dirname as dirname10 } from "path";
32602
+ import { existsSync as existsSync19, mkdirSync as mkdirSync12, writeFileSync as writeFileSync10 } from "fs";
32603
+ import { join as join15, dirname as dirname11 } from "path";
32061
32604
  var RUN_RECORD_SCHEMA = "todos.run_record.v1";
32062
32605
  var RUN_RECORD_STATUSES = ["active", "completed", "failed", "archived"];
32063
32606
  function parseJsonArray3(raw, fallback = []) {
@@ -32250,8 +32793,8 @@ function buildRunReplayBundle(id, db) {
32250
32793
  }
32251
32794
  function exportRunReplay(id, outputPath, db) {
32252
32795
  const bundle = buildRunReplayBundle(id, db);
32253
- const path = outputPath ?? join14(process.cwd(), ".todos", "replays", `${id.slice(0, 8)}.json`);
32254
- mkdirSync12(dirname10(path), { recursive: true });
32796
+ const path = outputPath ?? join15(process.cwd(), ".todos", "replays", `${id.slice(0, 8)}.json`);
32797
+ mkdirSync12(dirname11(path), { recursive: true });
32255
32798
  writeFileSync10(path, JSON.stringify(bundle, null, 2));
32256
32799
  const d = db || getDatabase();
32257
32800
  d.run(`UPDATE run_records SET replay_bundle = ?, updated_at = ? WHERE id = ?`, [path, now(), id]);
@@ -32290,16 +32833,16 @@ function formatRunRecordMarkdown(record) {
32290
32833
  `;
32291
32834
  }
32292
32835
  function getDefaultReplayDir() {
32293
- const local = join14(process.cwd(), ".todos", "replays");
32294
- if (existsSync18(join14(process.cwd(), ".todos")))
32836
+ const local = join15(process.cwd(), ".todos", "replays");
32837
+ if (existsSync19(join15(process.cwd(), ".todos")))
32295
32838
  return local;
32296
32839
  const home = process.env["HOME"] || "~";
32297
- return join14(home, ".hasna", "todos", "replays");
32840
+ return join15(home, ".hasna", "todos", "replays");
32298
32841
  }
32299
32842
  // src/lib/release-checks.ts
32300
32843
  init_secret_redaction();
32301
- import { existsSync as existsSync19, readFileSync as readFileSync16, readdirSync as readdirSync3, statSync as statSync5 } from "fs";
32302
- import { join as join15, relative as relative5 } from "path";
32844
+ import { existsSync as existsSync20, readFileSync as readFileSync16, readdirSync as readdirSync4, statSync as statSync6 } from "fs";
32845
+ import { join as join16, relative as relative5 } from "path";
32303
32846
  var RELEASE_CHECK_SCHEMA = "todos.release_check.v1";
32304
32847
  var FORBIDDEN_DIST_PATTERNS = [
32305
32848
  {
@@ -32312,17 +32855,17 @@ var FORBIDDEN_DIST_PATTERNS = [
32312
32855
  ];
32313
32856
  var REQUIRED_BINS = ["todos", "todos-mcp", "todos-serve"];
32314
32857
  function readPackageJson3(root) {
32315
- const path = join15(root, "package.json");
32316
- if (!existsSync19(path))
32858
+ const path = join16(root, "package.json");
32859
+ if (!existsSync20(path))
32317
32860
  throw new Error(`package.json not found in ${root}`);
32318
32861
  return JSON.parse(readFileSync16(path, "utf8"));
32319
32862
  }
32320
32863
  function walkFiles(dir, acc = []) {
32321
- if (!existsSync19(dir))
32864
+ if (!existsSync20(dir))
32322
32865
  return acc;
32323
- for (const entry2 of readdirSync3(dir)) {
32324
- const full = join15(dir, entry2);
32325
- const st = statSync5(full);
32866
+ for (const entry2 of readdirSync4(dir)) {
32867
+ const full = join16(dir, entry2);
32868
+ const st = statSync6(full);
32326
32869
  if (st.isDirectory())
32327
32870
  walkFiles(full, acc);
32328
32871
  else if (/\.(js|mjs|cjs|json|d\.ts)$/.test(entry2))
@@ -32339,8 +32882,8 @@ function auditPackageContents(root) {
32339
32882
  checks.push({ id: "files_dist", severity: "error", message: "package.json files must include dist" });
32340
32883
  }
32341
32884
  for (const pattern of files) {
32342
- const target = join15(root, pattern);
32343
- if (!existsSync19(target)) {
32885
+ const target = join16(root, pattern);
32886
+ if (!existsSync20(target)) {
32344
32887
  checks.push({ id: `files_missing_${pattern}`, severity: "error", message: `Published file path missing: ${pattern}` });
32345
32888
  }
32346
32889
  }
@@ -32354,8 +32897,8 @@ function auditPackageContents(root) {
32354
32897
  checks.push({ id: `bin_${name}`, severity: "error", message: `Missing bin entry: ${name}` });
32355
32898
  continue;
32356
32899
  }
32357
- const binPath = join15(root, rel);
32358
- if (!existsSync19(binPath)) {
32900
+ const binPath = join16(root, rel);
32901
+ if (!existsSync20(binPath)) {
32359
32902
  checks.push({ id: `bin_path_${name}`, severity: "error", message: `Bin file missing: ${rel}` });
32360
32903
  } else {
32361
32904
  checks.push({ id: `bin_ok_${name}`, severity: "info", message: `Bin present: ${name} \u2192 ${rel}` });
@@ -32372,8 +32915,8 @@ function auditPackageContents(root) {
32372
32915
  }
32373
32916
  function scanDistArtifacts(root) {
32374
32917
  const checks = [];
32375
- const distDir = join15(root, "dist");
32376
- if (!existsSync19(distDir)) {
32918
+ const distDir = join16(root, "dist");
32919
+ if (!existsSync20(distDir)) {
32377
32920
  checks.push({ id: "dist_missing", severity: "error", message: "dist/ directory not found \u2014 run bun run build" });
32378
32921
  return checks;
32379
32922
  }
@@ -32679,15 +33222,15 @@ function renderReleaseNotesMarkdown(document) {
32679
33222
  // src/lib/db-backup.ts
32680
33223
  init_database();
32681
33224
  init_migrations();
32682
- import { existsSync as existsSync20, copyFileSync, mkdirSync as mkdirSync13, readFileSync as readFileSync17, statSync as statSync6, writeFileSync as writeFileSync11, unlinkSync } from "fs";
32683
- import { dirname as dirname11, join as join16, resolve as resolve15 } from "path";
32684
- import { Database as Database3 } from "bun:sqlite";
33225
+ import { existsSync as existsSync21, copyFileSync, mkdirSync as mkdirSync13, readFileSync as readFileSync17, statSync as statSync7, writeFileSync as writeFileSync11, unlinkSync } from "fs";
33226
+ import { dirname as dirname12, join as join17, resolve as resolve16 } from "path";
33227
+ import { Database as Database4 } from "bun:sqlite";
32685
33228
  var DB_BACKUP_SCHEMA = "todos.db_backup.v1";
32686
33229
  function resolveDbPath(dbPath) {
32687
33230
  if (dbPath)
32688
- return resolve15(dbPath);
33231
+ return resolve16(dbPath);
32689
33232
  if (process.env["TODOS_DB_PATH"] && process.env["TODOS_DB_PATH"] !== ":memory:") {
32690
- return resolve15(process.env["TODOS_DB_PATH"]);
33233
+ return resolve16(process.env["TODOS_DB_PATH"]);
32691
33234
  }
32692
33235
  const db = getDatabase();
32693
33236
  const filename = db.filename;
@@ -32697,11 +33240,11 @@ function resolveDbPath(dbPath) {
32697
33240
  }
32698
33241
  function backupDatabase(outputPath, sourcePath) {
32699
33242
  const source9 = resolveDbPath(sourcePath);
32700
- if (!existsSync20(source9))
33243
+ if (!existsSync21(source9))
32701
33244
  throw new Error(`Database not found: ${source9}`);
32702
- mkdirSync13(dirname11(outputPath), { recursive: true });
33245
+ mkdirSync13(dirname12(outputPath), { recursive: true });
32703
33246
  closeDatabase();
32704
- const src = new Database3(source9);
33247
+ const src = new Database4(source9);
32705
33248
  try {
32706
33249
  src.exec("PRAGMA wal_checkpoint(TRUNCATE)");
32707
33250
  } catch {}
@@ -32709,7 +33252,7 @@ function backupDatabase(outputPath, sourcePath) {
32709
33252
  src.close();
32710
33253
  writeFileSync11(outputPath, image);
32711
33254
  const method = "file_copy";
32712
- const bytes = statSync6(outputPath).size;
33255
+ const bytes = statSync7(outputPath).size;
32713
33256
  return {
32714
33257
  schema_version: DB_BACKUP_SCHEMA,
32715
33258
  source_path: source9,
@@ -32720,14 +33263,14 @@ function backupDatabase(outputPath, sourcePath) {
32720
33263
  };
32721
33264
  }
32722
33265
  function restoreDatabase(backupPath, targetPath) {
32723
- if (!existsSync20(backupPath))
33266
+ if (!existsSync21(backupPath))
32724
33267
  throw new Error(`Backup not found: ${backupPath}`);
32725
33268
  const integrity = checkDatabaseIntegrity(backupPath);
32726
33269
  if (!integrity.ok) {
32727
33270
  throw new Error(`Backup failed integrity check: ${integrity.errors.join("; ")}`);
32728
33271
  }
32729
- const target = targetPath ? resolve15(targetPath) : resolveDbPath();
32730
- mkdirSync13(dirname11(target), { recursive: true });
33272
+ const target = targetPath ? resolve16(targetPath) : resolveDbPath();
33273
+ mkdirSync13(dirname12(target), { recursive: true });
32731
33274
  const staging = `${target}.restore.tmp`;
32732
33275
  copyFileSync(backupPath, staging);
32733
33276
  copyFileSync(staging, target);
@@ -32739,15 +33282,15 @@ function restoreDatabase(backupPath, targetPath) {
32739
33282
  schema_version: DB_BACKUP_SCHEMA,
32740
33283
  source_path: backupPath,
32741
33284
  backup_path: target,
32742
- bytes: statSync6(target).size,
33285
+ bytes: statSync7(target).size,
32743
33286
  method: "file_copy",
32744
33287
  created_at: new Date().toISOString()
32745
33288
  };
32746
33289
  }
32747
33290
  function checkDatabaseIntegrity(dbPath) {
32748
- const path = dbPath ? resolve15(dbPath) : resolveDbPath();
33291
+ const path = dbPath ? resolve16(dbPath) : resolveDbPath();
32749
33292
  const errors = [];
32750
- if (!existsSync20(path)) {
33293
+ if (!existsSync21(path)) {
32751
33294
  return {
32752
33295
  schema_version: DB_BACKUP_SCHEMA,
32753
33296
  path,
@@ -32760,7 +33303,7 @@ function checkDatabaseIntegrity(dbPath) {
32760
33303
  }
32761
33304
  let db;
32762
33305
  try {
32763
- db = new Database3(path, { readonly: true });
33306
+ db = new Database4(path, { readonly: true });
32764
33307
  } catch (e) {
32765
33308
  return {
32766
33309
  schema_version: DB_BACKUP_SCHEMA,
@@ -32810,18 +33353,18 @@ function checkDatabaseIntegrity(dbPath) {
32810
33353
  };
32811
33354
  }
32812
33355
  function compactDatabase(dbPath) {
32813
- const path = dbPath ? resolve15(dbPath) : resolveDbPath();
32814
- const before = statSync6(path).size;
32815
- const db = new Database3(path);
33356
+ const path = dbPath ? resolve16(dbPath) : resolveDbPath();
33357
+ const before = statSync7(path).size;
33358
+ const db = new Database4(path);
32816
33359
  db.exec("VACUUM");
32817
33360
  db.close();
32818
- const after = statSync6(path).size;
33361
+ const after = statSync7(path).size;
32819
33362
  closeDatabase();
32820
33363
  return { path, bytes_before: before, bytes_after: after };
32821
33364
  }
32822
33365
  function migrationDryRun(dbPath) {
32823
- const path = dbPath ? resolve15(dbPath) : resolveDbPath();
32824
- const db = new Database3(path, { readonly: true });
33366
+ const path = dbPath ? resolve16(dbPath) : resolveDbPath();
33367
+ const db = new Database4(path, { readonly: true });
32825
33368
  let current = 0;
32826
33369
  try {
32827
33370
  const row = db.query("SELECT MAX(id) as id FROM _migrations").get();
@@ -32844,13 +33387,13 @@ function migrationDryRun(dbPath) {
32844
33387
  };
32845
33388
  }
32846
33389
  function defaultBackupPath(dbPath) {
32847
- const base = dbPath ? dirname11(resolve15(dbPath)) : dirname11(resolveDbPath());
33390
+ const base = dbPath ? dirname12(resolve16(dbPath)) : dirname12(resolveDbPath());
32848
33391
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
32849
- return join16(base, "backups", `todos-${stamp}.db`);
33392
+ return join17(base, "backups", `todos-${stamp}.db`);
32850
33393
  }
32851
33394
  function readBackupManifest(backupPath) {
32852
33395
  const manifestPath = `${backupPath}.json`;
32853
- if (!existsSync20(manifestPath))
33396
+ if (!existsSync21(manifestPath))
32854
33397
  return null;
32855
33398
  try {
32856
33399
  return JSON.parse(readFileSync17(manifestPath, "utf8"));
@@ -32863,7 +33406,7 @@ function writeBackupManifest(backupPath, result) {
32863
33406
  writeFileSyncSafe(manifestPath, JSON.stringify(result, null, 2));
32864
33407
  }
32865
33408
  function writeFileSyncSafe(path, content) {
32866
- mkdirSync13(dirname11(path), { recursive: true });
33409
+ mkdirSync13(dirname12(path), { recursive: true });
32867
33410
  writeFileSync11(path, content);
32868
33411
  }
32869
33412
  // src/lib/json-schemas.ts
@@ -32927,6 +33470,7 @@ var JSON_SCHEMAS = {
32927
33470
  plan: def("plan", "todos.plan.v1", "Plan", ["schema_version", "id", "name", "status", "created_at", "updated_at"], {
32928
33471
  schema_version: { type: "string", enum: ["todos.plan.v1"] },
32929
33472
  id: { type: "string" },
33473
+ slug: { type: ["string", "null"] },
32930
33474
  project_id: { type: ["string", "null"] },
32931
33475
  name: { type: "string" },
32932
33476
  description: { type: ["string", "null"] },
@@ -33370,15 +33914,15 @@ ${SCHEMA_ENTITIES.map((e) => `- **${e}**: \`${JSON_SCHEMAS[e].schema_version}\``
33370
33914
  }
33371
33915
  function exportSchemasToDirectory(dir) {
33372
33916
  const { mkdirSync: mkdirSync14, writeFileSync: writeFileSync12 } = __require("fs");
33373
- const { join: join17 } = __require("path");
33917
+ const { join: join18 } = __require("path");
33374
33918
  mkdirSync14(dir, { recursive: true });
33375
33919
  const written = [];
33376
33920
  for (const entity of SCHEMA_ENTITIES) {
33377
- const path = join17(dir, `${entity}.${JSON_SCHEMAS[entity].schema_version.replace(/\./g, "-")}.json`);
33921
+ const path = join18(dir, `${entity}.${JSON_SCHEMAS[entity].schema_version.replace(/\./g, "-")}.json`);
33378
33922
  writeFileSync12(path, JSON.stringify(JSON_SCHEMAS[entity], null, 2));
33379
33923
  written.push(path);
33380
33924
  }
33381
- const catalogPath = join17(dir, "catalog.json");
33925
+ const catalogPath = join18(dir, "catalog.json");
33382
33926
  writeFileSync12(catalogPath, JSON.stringify({
33383
33927
  catalog_version: JSON_SCHEMA_CATALOG_VERSION,
33384
33928
  semver: SCHEMA_SEMVER,
@@ -34279,7 +34823,7 @@ function getReminderDocs() {
34279
34823
  // src/lib/import-export-bridge.ts
34280
34824
  init_database();
34281
34825
  import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, mkdirSync as mkdirSync14 } from "fs";
34282
- import { dirname as dirname12 } from "path";
34826
+ import { dirname as dirname13 } from "path";
34283
34827
  init_secret_redaction();
34284
34828
  var BUNDLE_SCHEMA = "todos.bundle.v1";
34285
34829
  var BUNDLE_TYPES = ["full_export", "tasks", "partial"];
@@ -34687,7 +35231,7 @@ function importBundle(bundle, options = {}, db) {
34687
35231
  return result;
34688
35232
  }
34689
35233
  function writeBundleFile(bundle, path) {
34690
- mkdirSync14(dirname12(path), { recursive: true });
35234
+ mkdirSync14(dirname13(path), { recursive: true });
34691
35235
  writeFileSync12(path, JSON.stringify(bundle, null, 2), "utf8");
34692
35236
  }
34693
35237
  function readBundleFile(path) {
@@ -35168,7 +35712,7 @@ function createPlanWithSteps(name, steps, opts = {}, db) {
35168
35712
  // src/lib/handoff-packets.ts
35169
35713
  init_database();
35170
35714
  import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync15 } from "fs";
35171
- import { dirname as dirname13 } from "path";
35715
+ import { dirname as dirname14 } from "path";
35172
35716
  var HANDOFF_PACKET_SCHEMA = "todos.handoff_packet.v1";
35173
35717
  function summarizeTask2(t) {
35174
35718
  return {
@@ -35342,7 +35886,7 @@ function formatHandoffPacket(packet, format = "json") {
35342
35886
  function exportHandoffPacket(input = {}, path, db) {
35343
35887
  const packet = createHandoffPacket(input, db);
35344
35888
  if (path) {
35345
- mkdirSync15(dirname13(path), { recursive: true });
35889
+ mkdirSync15(dirname14(path), { recursive: true });
35346
35890
  writeFileSync13(path, formatHandoffPacket(packet, "json"), "utf8");
35347
35891
  }
35348
35892
  return packet;
@@ -35947,7 +36491,7 @@ function generateCliReferenceMarkdown() {
35947
36491
  // src/db/builtin-templates.ts
35948
36492
  init_database();
35949
36493
  import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
35950
- import { join as join17 } from "path";
36494
+ import { join as join18 } from "path";
35951
36495
  var BUILTIN_TEMPLATE_LIBRARY_VERSION = "2026-05-21";
35952
36496
  var BUILTIN_TEMPLATE_LIBRARY_SOURCE = "bundled-local-template-library";
35953
36497
  var TEMPLATE_LIBRARY_SCHEMA = "todos.template_library.v1";
@@ -36223,7 +36767,7 @@ function writeBuiltinTemplateFiles(directory) {
36223
36767
  mkdirSync16(directory, { recursive: true });
36224
36768
  const files = [];
36225
36769
  for (const entry2 of exportBuiltinTemplateFiles()) {
36226
- const path = join17(directory, entry2.filename);
36770
+ const path = join18(directory, entry2.filename);
36227
36771
  writeFileSync14(path, `${JSON.stringify(entry2.template, null, 2)}
36228
36772
  `, "utf-8");
36229
36773
  files.push(path);
@@ -36268,7 +36812,7 @@ function initBuiltinTemplates(db) {
36268
36812
  // src/lib/template-library.ts
36269
36813
  init_database();
36270
36814
  import { writeFileSync as writeFileSync15, readFileSync as readFileSync19, mkdirSync as mkdirSync17 } from "fs";
36271
- import { dirname as dirname14 } from "path";
36815
+ import { dirname as dirname15 } from "path";
36272
36816
  function listTemplateLibrary(db) {
36273
36817
  const d = db || getDatabase();
36274
36818
  const installed = new Set(listTemplates(d).map((t) => t.name));
@@ -36310,7 +36854,7 @@ function exportTemplateLibraryCatalog(path, db) {
36310
36854
  templates: listTemplateLibrary(db)
36311
36855
  };
36312
36856
  if (path) {
36313
- mkdirSync17(dirname14(path), { recursive: true });
36857
+ mkdirSync17(dirname15(path), { recursive: true });
36314
36858
  writeFileSync15(path, JSON.stringify(catalog, null, 2), "utf8");
36315
36859
  }
36316
36860
  return catalog;
@@ -36370,11 +36914,11 @@ init_database();
36370
36914
  init_machines();
36371
36915
  import { hostname as hostname2 } from "os";
36372
36916
  var MACHINE_TOPOLOGY_SCHEMA = "todos.machine_topology.v1";
36373
- function normalizePath4(p) {
36917
+ function normalizePath5(p) {
36374
36918
  return p.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
36375
36919
  }
36376
36920
  function detectCasingMismatch(stored, canonical) {
36377
- return normalizePath4(stored) === normalizePath4(canonical) && stored !== canonical;
36921
+ return normalizePath5(stored) === normalizePath5(canonical) && stored !== canonical;
36378
36922
  }
36379
36923
  function registerLocalMachine(db) {
36380
36924
  resetMachineId();
@@ -36393,7 +36937,7 @@ function getPathOverrides(db) {
36393
36937
  project_name: project?.name ?? row.project_id.slice(0, 8),
36394
36938
  machine_id: row.machine_id,
36395
36939
  path: row.path,
36396
- path_normalized: normalizePath4(row.path),
36940
+ path_normalized: normalizePath5(row.path),
36397
36941
  casing_mismatch: detectCasingMismatch(row.path, canonical)
36398
36942
  };
36399
36943
  });
@@ -36505,10 +37049,10 @@ todos machines topology # full diagnostic report
36505
37049
  `;
36506
37050
  }
36507
37051
  // src/lib/environment-snapshots.ts
36508
- import { createHash as createHash12 } from "crypto";
36509
- import { existsSync as existsSync21, readFileSync as readFileSync20, statSync as statSync7 } from "fs";
37052
+ import { createHash as createHash13 } from "crypto";
37053
+ import { existsSync as existsSync22, readFileSync as readFileSync20, statSync as statSync8 } from "fs";
36510
37054
  import { hostname as hostname3, platform, arch } from "os";
36511
- import { dirname as dirname15, join as join18, resolve as resolve16 } from "path";
37055
+ import { dirname as dirname16, join as join19, resolve as resolve17 } from "path";
36512
37056
  import { tmpdir as tmpdir3 } from "os";
36513
37057
  init_database();
36514
37058
  init_redaction();
@@ -36530,13 +37074,13 @@ var CONFIG_FILES = [
36530
37074
  "dashboard/vite.config.ts"
36531
37075
  ];
36532
37076
  function sha2565(value) {
36533
- return createHash12("sha256").update(value).digest("hex");
37077
+ return createHash13("sha256").update(value).digest("hex");
36534
37078
  }
36535
37079
  function fileRecord(root, relativePath) {
36536
- const path = join18(root, relativePath);
36537
- if (!existsSync21(path))
37080
+ const path = join19(root, relativePath);
37081
+ if (!existsSync22(path))
36538
37082
  return null;
36539
- const stat = statSync7(path);
37083
+ const stat = statSync8(path);
36540
37084
  if (!stat.isFile())
36541
37085
  return null;
36542
37086
  const content = readFileSync20(path);
@@ -36546,7 +37090,7 @@ function manifestRecord(root, relativePath) {
36546
37090
  const base = fileRecord(root, relativePath);
36547
37091
  if (!base)
36548
37092
  return null;
36549
- const parsed = readJsonFile(join18(root, relativePath));
37093
+ const parsed = readJsonFile(join19(root, relativePath));
36550
37094
  if (!parsed)
36551
37095
  return { ...base, redacted: {} };
36552
37096
  const redacted = redactValue({
@@ -36641,15 +37185,15 @@ function commandEnv(env, includeValues) {
36641
37185
  function defaultSnapshotDir() {
36642
37186
  const dbPath = getDatabasePath();
36643
37187
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
36644
- return join18(tmpdir3(), "hasna-todos", "environment-snapshots");
36645
- return join18(dirname15(resolve16(dbPath)), "environment-snapshots");
37188
+ return join19(tmpdir3(), "hasna-todos", "environment-snapshots");
37189
+ return join19(dirname16(resolve17(dbPath)), "environment-snapshots");
36646
37190
  }
36647
37191
  function snapshotWithId(snapshot) {
36648
37192
  const digest = sha2565(JSON.stringify(snapshot)).slice(0, 24);
36649
37193
  return { id: `env_${digest}`, ...snapshot };
36650
37194
  }
36651
37195
  function captureEnvironmentSnapshot(input = {}) {
36652
- const root = resolve16(input.root || process.cwd());
37196
+ const root = resolve17(input.root || process.cwd());
36653
37197
  const env = input.env || process.env;
36654
37198
  const warnings = [];
36655
37199
  const manifests = MANIFEST_FILES.map((file) => manifestRecord(root, file)).filter((file) => Boolean(file));
@@ -36689,13 +37233,13 @@ function captureEnvironmentSnapshot(input = {}) {
36689
37233
  });
36690
37234
  }
36691
37235
  function writeEnvironmentSnapshot(snapshot, outputPath) {
36692
- const path = outputPath ? resolve16(outputPath) : join18(defaultSnapshotDir(), `${snapshot.id}.json`);
36693
- ensureDir2(dirname15(path));
37236
+ const path = outputPath ? resolve17(outputPath) : join19(defaultSnapshotDir(), `${snapshot.id}.json`);
37237
+ ensureDir2(dirname16(path));
36694
37238
  writeJsonFile(path, snapshot);
36695
37239
  return path;
36696
37240
  }
36697
37241
  function readEnvironmentSnapshot(path) {
36698
- const snapshot = readJsonFile(resolve16(path));
37242
+ const snapshot = readJsonFile(resolve17(path));
36699
37243
  if (!snapshot || snapshot.schema_version !== 1 || typeof snapshot.id !== "string") {
36700
37244
  throw new Error(`Invalid environment snapshot: ${path}`);
36701
37245
  }
@@ -36780,9 +37324,9 @@ function compareEnvironmentSnapshotFiles(leftPath, rightPath) {
36780
37324
  }
36781
37325
  // src/lib/decision-records.ts
36782
37326
  init_database();
36783
- import { createHash as createHash13 } from "crypto";
37327
+ import { createHash as createHash14 } from "crypto";
36784
37328
  import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
36785
- import { dirname as dirname16, join as join19 } from "path";
37329
+ import { dirname as dirname17, join as join20 } from "path";
36786
37330
  var DECISION_RECORD_SCHEMA = "todos.decision_record.v1";
36787
37331
  var KNOWLEDGE_SNAPSHOT_SCHEMA = "todos.knowledge_snapshot.v1";
36788
37332
  var DECISION_STATUSES = ["proposed", "accepted", "deprecated", "superseded", "rejected"];
@@ -36836,7 +37380,7 @@ function rowToDecisionRecord(row) {
36836
37380
  }
36837
37381
  function stableSnapshotHash(payload) {
36838
37382
  const { captured_at: _capturedAt, ...rest } = payload;
36839
- return createHash13("sha256").update(JSON.stringify(rest)).digest("hex");
37383
+ return createHash14("sha256").update(JSON.stringify(rest)).digest("hex");
36840
37384
  }
36841
37385
  function createDecisionRecord(input, db) {
36842
37386
  const d = db || getDatabase();
@@ -37012,8 +37556,8 @@ function exportDecisionRecord(id, outputPath, format = "markdown", db) {
37012
37556
  if (!record)
37013
37557
  throw new Error(`Decision record not found: ${id}`);
37014
37558
  const content = format === "markdown" ? formatDecisionRecordMarkdown(record) : JSON.stringify(record, null, 2);
37015
- const path = outputPath ?? join19(process.cwd(), ".todos", "decisions", `${record.short_ref}.${format === "markdown" ? "md" : "json"}`);
37016
- mkdirSync18(dirname16(path), { recursive: true });
37559
+ const path = outputPath ?? join20(process.cwd(), ".todos", "decisions", `${record.short_ref}.${format === "markdown" ? "md" : "json"}`);
37560
+ mkdirSync18(dirname17(path), { recursive: true });
37017
37561
  writeFileSync16(path, content, "utf8");
37018
37562
  return { path, content };
37019
37563
  }
@@ -37160,8 +37704,8 @@ function exportKnowledgeSnapshot(id, outputPath, format = "markdown", db) {
37160
37704
  throw new Error(`Knowledge snapshot not found: ${id}`);
37161
37705
  const content = format === "markdown" ? formatKnowledgeSnapshotMarkdown(record) : JSON.stringify(record, null, 2);
37162
37706
  const slug = record.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
37163
- const path = outputPath ?? join19(process.cwd(), ".todos", "knowledge", `${slug || record.id.slice(0, 8)}.${format === "markdown" ? "md" : "json"}`);
37164
- mkdirSync18(dirname16(path), { recursive: true });
37707
+ const path = outputPath ?? join20(process.cwd(), ".todos", "knowledge", `${slug || record.id.slice(0, 8)}.${format === "markdown" ? "md" : "json"}`);
37708
+ mkdirSync18(dirname17(path), { recursive: true });
37165
37709
  writeFileSync16(path, content, "utf8");
37166
37710
  return { path, content };
37167
37711
  }
@@ -37189,7 +37733,7 @@ Schema versions:
37189
37733
  // src/lib/report-exports.ts
37190
37734
  init_database();
37191
37735
  import { writeFileSync as writeFileSync17, mkdirSync as mkdirSync19 } from "fs";
37192
- import { dirname as dirname17 } from "path";
37736
+ import { dirname as dirname18 } from "path";
37193
37737
  init_secret_redaction();
37194
37738
  var REPORT_EXPORT_SCHEMA = "todos.report_export.v1";
37195
37739
  var REPORT_KINDS = ["project", "plan", "run", "evidence", "roadmap", "retrospective"];
@@ -37421,7 +37965,7 @@ function formatReportExport(data, format) {
37421
37965
  return format === "html" ? formatReportHtml(data) : formatReportMarkdown(data);
37422
37966
  }
37423
37967
  function writeReportExport(data, format, path) {
37424
- mkdirSync19(dirname17(path), { recursive: true });
37968
+ mkdirSync19(dirname18(path), { recursive: true });
37425
37969
  writeFileSync17(path, formatReportExport(data, format), "utf8");
37426
37970
  }
37427
37971
  function exportReport(input, db) {
@@ -37455,8 +37999,8 @@ todos report export --kind retrospective --days 14 --format markdown --out retro
37455
37999
  `;
37456
38000
  }
37457
38001
  // src/lib/command-aliases.ts
37458
- import { existsSync as existsSync22, readFileSync as readFileSync21, writeFileSync as writeFileSync18, mkdirSync as mkdirSync20 } from "fs";
37459
- import { join as join20 } from "path";
38002
+ import { existsSync as existsSync23, readFileSync as readFileSync21, writeFileSync as writeFileSync18, mkdirSync as mkdirSync20 } from "fs";
38003
+ import { join as join21 } from "path";
37460
38004
  var COMMAND_ALIASES_SCHEMA = "todos.command_aliases.v1";
37461
38005
  var RESERVED = new Set([...listTopLevelCommands(), "help", "version", "alias", "shortcuts"]);
37462
38006
  var BUILTIN_SHORTCUTS = [
@@ -37475,7 +38019,7 @@ var BUILTIN_SHORTCUTS = [
37475
38019
  { pattern: /^reports?$/, argv: ["report", "docs"], explain: "Report export documentation" }
37476
38020
  ];
37477
38021
  function aliasesPath(cwd = process.cwd()) {
37478
- return join20(cwd, ".todos", "aliases.json");
38022
+ return join21(cwd, ".todos", "aliases.json");
37479
38023
  }
37480
38024
  function emptyStore() {
37481
38025
  return { schema_version: COMMAND_ALIASES_SCHEMA, aliases: {}, updated_at: new Date(0).toISOString() };
@@ -37492,7 +38036,7 @@ function validateAliasName(name) {
37492
38036
  }
37493
38037
  function loadAliasStore(cwd) {
37494
38038
  const path = aliasesPath(cwd);
37495
- if (!existsSync22(path))
38039
+ if (!existsSync23(path))
37496
38040
  return emptyStore();
37497
38041
  const parsed = JSON.parse(readFileSync21(path, "utf8"));
37498
38042
  if (parsed.schema_version !== COMMAND_ALIASES_SCHEMA) {
@@ -37502,7 +38046,7 @@ function loadAliasStore(cwd) {
37502
38046
  }
37503
38047
  function saveAliasStore(store, cwd) {
37504
38048
  const path = aliasesPath(cwd);
37505
- mkdirSync20(join20(path, ".."), { recursive: true });
38049
+ mkdirSync20(join21(path, ".."), { recursive: true });
37506
38050
  store.updated_at = new Date().toISOString();
37507
38051
  writeFileSync18(path, JSON.stringify(store, null, 2), "utf8");
37508
38052
  }
@@ -38017,7 +38561,7 @@ function normalizeBranch(value) {
38017
38561
  }
38018
38562
  return branch;
38019
38563
  }
38020
- function normalizePath5(value) {
38564
+ function normalizePath6(value) {
38021
38565
  const path = value.trim().replace(/\\/g, "/");
38022
38566
  if (!path || path.startsWith("/") || path.includes("\x00"))
38023
38567
  return null;
@@ -38049,7 +38593,7 @@ function getGitStatus(root, branch, includeGitStatus) {
38049
38593
  const branchExists = runGit3(root, ["show-ref", "--verify", `refs/heads/${branch}`]) !== null;
38050
38594
  const status = runGit3(root, ["status", "--short"]) || "";
38051
38595
  const dirtyFiles = status.split(`
38052
- `).map((line) => line.trim()).filter(Boolean).map((line) => normalizePath5(line.replace(/^.. /, "").replace(/^.* -> /, ""))).filter((path) => Boolean(path));
38596
+ `).map((line) => line.trim()).filter(Boolean).map((line) => normalizePath6(line.replace(/^.. /, "").replace(/^.* -> /, ""))).filter((path) => Boolean(path));
38053
38597
  return { has_git: true, current_branch: currentBranch || null, branch_exists: branchExists, dirty_files: dirtyFiles };
38054
38598
  }
38055
38599
  function resolveScope2(input, db) {
@@ -38064,8 +38608,8 @@ function resolveScope2(input, db) {
38064
38608
  throw new Error("task_id or plan_id is required");
38065
38609
  }
38066
38610
  function collectPlannedFiles(tasks, explicitPaths, db) {
38067
- const fromTasks = tasks.flatMap((task2) => listTaskFiles(task2.id, db).map((file) => normalizePath5(file.path)));
38068
- const fromInput = (explicitPaths || []).map(normalizePath5);
38611
+ const fromTasks = tasks.flatMap((task2) => listTaskFiles(task2.id, db).map((file) => normalizePath6(file.path)));
38612
+ const fromInput = (explicitPaths || []).map(normalizePath6);
38069
38613
  return uniqueSorted2([...fromTasks, ...fromInput]);
38070
38614
  }
38071
38615
  function detectBranchPlanConflicts(taskIds, files, db) {
@@ -38150,18 +38694,18 @@ function createBranchWorkPlan(input, db) {
38150
38694
  }
38151
38695
  // src/lib/user-scaffolds.ts
38152
38696
  init_database();
38153
- import { existsSync as existsSync23, readFileSync as readFileSync22, writeFileSync as writeFileSync19, mkdirSync as mkdirSync21 } from "fs";
38154
- import { join as join21 } from "path";
38697
+ import { existsSync as existsSync24, readFileSync as readFileSync22, writeFileSync as writeFileSync19, mkdirSync as mkdirSync21 } from "fs";
38698
+ import { join as join22 } from "path";
38155
38699
  var USER_SCAFFOLD_SCHEMA = "todos.user_scaffold.v1";
38156
38700
  var SCAFFOLD_KINDS = ["task", "project", "plan", "checklist", "contract", "verification_policy"];
38157
38701
  function storeDir(cwd = process.cwd()) {
38158
- return join21(cwd, ".todos", "scaffolds");
38702
+ return join22(cwd, ".todos", "scaffolds");
38159
38703
  }
38160
38704
  function storePath(cwd) {
38161
- return join21(storeDir(cwd), "store.json");
38705
+ return join22(storeDir(cwd), "store.json");
38162
38706
  }
38163
38707
  function versionsDir(cwd) {
38164
- return join21(storeDir(cwd), "versions");
38708
+ return join22(storeDir(cwd), "versions");
38165
38709
  }
38166
38710
  function slugify5(name) {
38167
38711
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
@@ -38171,7 +38715,7 @@ function emptyStore2() {
38171
38715
  }
38172
38716
  function loadUserScaffoldStore(cwd) {
38173
38717
  const path = storePath(cwd);
38174
- if (!existsSync23(path))
38718
+ if (!existsSync24(path))
38175
38719
  return emptyStore2();
38176
38720
  const parsed = JSON.parse(readFileSync22(path, "utf8"));
38177
38721
  if (parsed.schema_version !== USER_SCAFFOLD_SCHEMA) {
@@ -38186,7 +38730,7 @@ function saveUserScaffoldStore(store, cwd) {
38186
38730
  }
38187
38731
  function snapshotVersion(scaffold, cwd) {
38188
38732
  mkdirSync21(versionsDir(cwd), { recursive: true });
38189
- const path = join21(versionsDir(cwd), `${scaffold.id}-v${scaffold.version}.json`);
38733
+ const path = join22(versionsDir(cwd), `${scaffold.id}-v${scaffold.version}.json`);
38190
38734
  writeFileSync19(path, JSON.stringify(scaffold, null, 2), "utf8");
38191
38735
  }
38192
38736
  function listUserScaffolds(kind, cwd) {
@@ -38433,7 +38977,7 @@ function listLinkedTemplates(db, cwd) {
38433
38977
  // src/lib/agent-workflow-demo.ts
38434
38978
  init_database();
38435
38979
  import { mkdtempSync } from "fs";
38436
- import { join as join22 } from "path";
38980
+ import { join as join23 } from "path";
38437
38981
  import { tmpdir as tmpdir4 } from "os";
38438
38982
  var AGENT_WORKFLOW_DEMO_SCHEMA = "todos.agent_workflow_demo.v1";
38439
38983
  var DEMO_DEFAULT_AGENT = "demoagent";
@@ -38449,7 +38993,7 @@ function setupEphemeralDemoDb(options = {}) {
38449
38993
  if (options.db_path) {
38450
38994
  db_path = options.db_path;
38451
38995
  } else if (options.persist) {
38452
- db_path = join22(mkdtempSync(join22(tmpdir4(), "todos-demo-")), "todos.db");
38996
+ db_path = join23(mkdtempSync(join23(tmpdir4(), "todos-demo-")), "todos.db");
38453
38997
  } else {
38454
38998
  db_path = ":memory:";
38455
38999
  }
@@ -40774,18 +41318,18 @@ function runSearchView(idOrName, db) {
40774
41318
  return { ...runSavedSearch(view.filters, view.scope, d), view };
40775
41319
  }
40776
41320
  // src/lib/claude-tasks.ts
40777
- import { existsSync as existsSync24, readFileSync as readFileSync23, readdirSync as readdirSync4, writeFileSync as writeFileSync20 } from "fs";
40778
- import { join as join23 } from "path";
41321
+ import { existsSync as existsSync25, readFileSync as readFileSync23, readdirSync as readdirSync5, writeFileSync as writeFileSync20 } from "fs";
41322
+ import { join as join24 } from "path";
40779
41323
  init_config();
40780
41324
  init_sync_utils();
40781
41325
  function getTaskListDir(taskListId) {
40782
- return join23(HOME, ".claude", "tasks", taskListId);
41326
+ return join24(HOME, ".claude", "tasks", taskListId);
40783
41327
  }
40784
41328
  function readClaudeTask(dir, filename) {
40785
- return readJsonFile(join23(dir, filename));
41329
+ return readJsonFile(join24(dir, filename));
40786
41330
  }
40787
41331
  function writeClaudeTask(dir, task2) {
40788
- writeJsonFile(join23(dir, `${task2.id}.json`), task2);
41332
+ writeJsonFile(join24(dir, `${task2.id}.json`), task2);
40789
41333
  }
40790
41334
  function toClaudeStatus(status) {
40791
41335
  if (status === "pending" || status === "in_progress" || status === "completed") {
@@ -40797,14 +41341,14 @@ function toSqliteStatus(status) {
40797
41341
  return status;
40798
41342
  }
40799
41343
  function readPrefixCounter(dir) {
40800
- const path = join23(dir, ".prefix-counter");
40801
- if (!existsSync24(path))
41344
+ const path = join24(dir, ".prefix-counter");
41345
+ if (!existsSync25(path))
40802
41346
  return 0;
40803
41347
  const val = parseInt(readFileSync23(path, "utf-8").trim(), 10);
40804
41348
  return isNaN(val) ? 0 : val;
40805
41349
  }
40806
41350
  function writePrefixCounter(dir, value) {
40807
- writeFileSync20(join23(dir, ".prefix-counter"), String(value));
41351
+ writeFileSync20(join24(dir, ".prefix-counter"), String(value));
40808
41352
  }
40809
41353
  function formatPrefixedSubject(title, prefix, counter) {
40810
41354
  const padded = String(counter).padStart(5, "0");
@@ -40831,7 +41375,7 @@ function taskToClaudeTask(task2, claudeTaskId, existingMeta) {
40831
41375
  }
40832
41376
  function pushToClaudeTaskList(taskListId, projectId, options = {}) {
40833
41377
  const dir = getTaskListDir(taskListId);
40834
- if (!existsSync24(dir))
41378
+ if (!existsSync25(dir))
40835
41379
  ensureDir2(dir);
40836
41380
  const filter = {};
40837
41381
  if (projectId)
@@ -40840,7 +41384,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
40840
41384
  const existingByTodosId = new Map;
40841
41385
  const files = listJsonFiles(dir);
40842
41386
  for (const f of files) {
40843
- const path = join23(dir, f);
41387
+ const path = join24(dir, f);
40844
41388
  const ct = readClaudeTask(dir, f);
40845
41389
  if (ct?.metadata?.["todos_id"]) {
40846
41390
  existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
@@ -40927,10 +41471,10 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
40927
41471
  }
40928
41472
  function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
40929
41473
  const dir = getTaskListDir(taskListId);
40930
- if (!existsSync24(dir)) {
41474
+ if (!existsSync25(dir)) {
40931
41475
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
40932
41476
  }
40933
- const files = readdirSync4(dir).filter((f) => f.endsWith(".json"));
41477
+ const files = readdirSync5(dir).filter((f) => f.endsWith(".json"));
40934
41478
  let pulled = 0;
40935
41479
  const errors = [];
40936
41480
  const prefer = options.prefer || "remote";
@@ -40947,7 +41491,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
40947
41491
  }
40948
41492
  for (const f of files) {
40949
41493
  try {
40950
- const filePath = join23(dir, f);
41494
+ const filePath = join24(dir, f);
40951
41495
  const ct = readClaudeTask(dir, f);
40952
41496
  if (!ct)
40953
41497
  continue;
@@ -41015,22 +41559,22 @@ function syncClaudeTaskList(taskListId, projectId, options = {}) {
41015
41559
  }
41016
41560
 
41017
41561
  // src/lib/agent-tasks.ts
41018
- import { existsSync as existsSync25 } from "fs";
41019
- import { join as join24 } from "path";
41562
+ import { existsSync as existsSync26 } from "fs";
41563
+ import { join as join25 } from "path";
41020
41564
  init_sync_utils();
41021
41565
  init_config();
41022
41566
  function agentBaseDir(agent) {
41023
41567
  const key = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
41024
- return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join24(getTodosGlobalDir(), "agents");
41568
+ return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join25(getTodosGlobalDir(), "agents");
41025
41569
  }
41026
41570
  function getTaskListDir2(agent, taskListId) {
41027
- return join24(agentBaseDir(agent), agent, taskListId);
41571
+ return join25(agentBaseDir(agent), agent, taskListId);
41028
41572
  }
41029
41573
  function readAgentTask(dir, filename) {
41030
- return readJsonFile(join24(dir, filename));
41574
+ return readJsonFile(join25(dir, filename));
41031
41575
  }
41032
41576
  function writeAgentTask(dir, task2) {
41033
- writeJsonFile(join24(dir, `${task2.id}.json`), task2);
41577
+ writeJsonFile(join25(dir, `${task2.id}.json`), task2);
41034
41578
  }
41035
41579
  function taskToAgentTask(task2, externalId, existingMeta) {
41036
41580
  return {
@@ -41055,7 +41599,7 @@ function metadataKey(agent) {
41055
41599
  }
41056
41600
  function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
41057
41601
  const dir = getTaskListDir2(agent, taskListId);
41058
- if (!existsSync25(dir))
41602
+ if (!existsSync26(dir))
41059
41603
  ensureDir2(dir);
41060
41604
  const filter = {};
41061
41605
  if (projectId)
@@ -41064,7 +41608,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
41064
41608
  const existingByTodosId = new Map;
41065
41609
  const files = listJsonFiles(dir);
41066
41610
  for (const f of files) {
41067
- const path = join24(dir, f);
41611
+ const path = join25(dir, f);
41068
41612
  const at = readAgentTask(dir, f);
41069
41613
  if (at?.metadata?.["todos_id"]) {
41070
41614
  existingByTodosId.set(at.metadata["todos_id"], { task: at, mtimeMs: getFileMtimeMs(path) });
@@ -41138,7 +41682,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
41138
41682
  }
41139
41683
  function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
41140
41684
  const dir = getTaskListDir2(agent, taskListId);
41141
- if (!existsSync25(dir)) {
41685
+ if (!existsSync26(dir)) {
41142
41686
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
41143
41687
  }
41144
41688
  const files = listJsonFiles(dir);
@@ -41157,7 +41701,7 @@ function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
41157
41701
  }
41158
41702
  for (const f of files) {
41159
41703
  try {
41160
- const filePath = join24(dir, f);
41704
+ const filePath = join25(dir, f);
41161
41705
  const at = readAgentTask(dir, f);
41162
41706
  if (!at)
41163
41707
  continue;
@@ -41295,9 +41839,9 @@ function syncWithAgents(agents, taskListIdByAgent, projectId, direction = "both"
41295
41839
  return { pushed, pulled, errors };
41296
41840
  }
41297
41841
  // src/lib/extract.ts
41298
- import { existsSync as existsSync26, readFileSync as readFileSync24, statSync as statSync8 } from "fs";
41299
- import { createHash as createHash14 } from "crypto";
41300
- import { relative as relative6, resolve as resolve17, join as join25 } from "path";
41842
+ import { existsSync as existsSync27, readFileSync as readFileSync24, statSync as statSync9 } from "fs";
41843
+ import { createHash as createHash15 } from "crypto";
41844
+ import { relative as relative6, resolve as resolve18, join as join26 } from "path";
41301
41845
  var EXTRACT_TAGS = ["TODO", "FIXME", "HACK", "XXX", "BUG", "NOTE"];
41302
41846
  var DEFAULT_EXTENSIONS = new Set([
41303
41847
  ".ts",
@@ -41361,15 +41905,15 @@ var SKIP_DIRS2 = new Set([
41361
41905
  ".parcel-cache"
41362
41906
  ]);
41363
41907
  function stableHash(value) {
41364
- return createHash14("sha256").update(value).digest("hex");
41908
+ return createHash15("sha256").update(value).digest("hex");
41365
41909
  }
41366
41910
  function normalizePathForMatch(value) {
41367
41911
  return value.replace(/\\/g, "/").replace(/^\.\//, "");
41368
41912
  }
41369
41913
  function readGitignorePatterns(basePath) {
41370
- const root = statSync8(basePath).isFile() ? resolve17(basePath, "..") : basePath;
41371
- const gitignorePath = join25(root, ".gitignore");
41372
- if (!existsSync26(gitignorePath))
41914
+ const root = statSync9(basePath).isFile() ? resolve18(basePath, "..") : basePath;
41915
+ const gitignorePath = join26(root, ".gitignore");
41916
+ if (!existsSync27(gitignorePath))
41373
41917
  return [];
41374
41918
  try {
41375
41919
  return readFileSync24(gitignorePath, "utf-8").split(`
@@ -41478,7 +42022,7 @@ function extractFromSource(source9, filePath, tags = [...EXTRACT_TAGS]) {
41478
42022
  return results;
41479
42023
  }
41480
42024
  function collectFiles(basePath, extensions, excludes, respectGitignore) {
41481
- const stat = statSync8(basePath);
42025
+ const stat = statSync9(basePath);
41482
42026
  if (stat.isFile()) {
41483
42027
  return [basePath];
41484
42028
  }
@@ -41503,7 +42047,7 @@ function collectFiles(basePath, extensions, excludes, respectGitignore) {
41503
42047
  return files.sort();
41504
42048
  }
41505
42049
  function buildCodebaseIndex(options) {
41506
- const basePath = resolve17(options.path);
42050
+ const basePath = resolve18(options.path);
41507
42051
  const tags = options.patterns || [...EXTRACT_TAGS];
41508
42052
  const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
41509
42053
  const excludes = options.exclude || [];
@@ -41511,10 +42055,10 @@ function buildCodebaseIndex(options) {
41511
42055
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
41512
42056
  const indexed = [];
41513
42057
  for (const file of files) {
41514
- const fullPath = statSync8(basePath).isFile() ? basePath : join25(basePath, file);
42058
+ const fullPath = statSync9(basePath).isFile() ? basePath : join26(basePath, file);
41515
42059
  try {
41516
42060
  const source9 = readFileSync24(fullPath, "utf-8");
41517
- const relPath = statSync8(basePath).isFile() ? relative6(resolve17(basePath, ".."), fullPath) : file;
42061
+ const relPath = statSync9(basePath).isFile() ? relative6(resolve18(basePath, ".."), fullPath) : file;
41518
42062
  indexed.push({
41519
42063
  file: relPath,
41520
42064
  checksum: stableHash(source9).slice(0, 24),
@@ -41534,7 +42078,7 @@ function buildCodebaseIndex(options) {
41534
42078
  };
41535
42079
  }
41536
42080
  function extractTodos(options, db) {
41537
- const basePath = resolve17(options.path);
42081
+ const basePath = resolve18(options.path);
41538
42082
  const tags = options.patterns || [...EXTRACT_TAGS];
41539
42083
  const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
41540
42084
  const excludes = options.exclude || [];
@@ -41542,10 +42086,10 @@ function extractTodos(options, db) {
41542
42086
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
41543
42087
  const allComments = [];
41544
42088
  for (const file of files) {
41545
- const fullPath = statSync8(basePath).isFile() ? basePath : join25(basePath, file);
42089
+ const fullPath = statSync9(basePath).isFile() ? basePath : join26(basePath, file);
41546
42090
  try {
41547
42091
  const source9 = readFileSync24(fullPath, "utf-8");
41548
- const relPath = statSync8(basePath).isFile() ? relative6(resolve17(basePath, ".."), fullPath) : file;
42092
+ const relPath = statSync9(basePath).isFile() ? relative6(resolve18(basePath, ".."), fullPath) : file;
41549
42093
  const comments = extractFromSource(source9, relPath, tags);
41550
42094
  allComments.push(...comments);
41551
42095
  } catch {}
@@ -41639,7 +42183,7 @@ async function watchSourceTodos(options, onRun) {
41639
42183
  const interval = Math.max(100, options.interval_ms || 2000);
41640
42184
  const once = options.once !== false && (!options.max_runs || options.max_runs <= 1);
41641
42185
  const maxRuns = options.max_runs ?? (once ? 1 : Number.POSITIVE_INFINITY);
41642
- const root = resolve17(options.path);
42186
+ const root = resolve18(options.path);
41643
42187
  const runs = [];
41644
42188
  let previous = new Map;
41645
42189
  for (let runNumber = 1;runNumber <= maxRuns; runNumber++) {
@@ -42249,7 +42793,7 @@ function renderWorkflowStatesMarkdown(states = listWorkflowStates()) {
42249
42793
  }
42250
42794
  // src/lib/agent-replay-simulator.ts
42251
42795
  init_redaction();
42252
- import { createHash as createHash15 } from "crypto";
42796
+ import { createHash as createHash16 } from "crypto";
42253
42797
  import { readFileSync as readFileSync25 } from "fs";
42254
42798
  function isObject(value) {
42255
42799
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
@@ -42271,7 +42815,7 @@ function stable2(value) {
42271
42815
  return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stable2(value[key])]));
42272
42816
  }
42273
42817
  function fingerprint3(value) {
42274
- return createHash15("sha256").update(JSON.stringify(stable2(value))).digest("hex");
42818
+ return createHash16("sha256").update(JSON.stringify(stable2(value))).digest("hex");
42275
42819
  }
42276
42820
  function unpackFixture(input) {
42277
42821
  if (!isObject(input))
@@ -42510,9 +43054,9 @@ function renderAgentReplaySimulationMarkdown(simulation) {
42510
43054
  }
42511
43055
  // src/lib/local-extensions.ts
42512
43056
  init_config();
42513
- import { createHash as createHash16, createVerify } from "crypto";
42514
- import { existsSync as existsSync27, readdirSync as readdirSync5, readFileSync as readFileSync26, statSync as statSync9 } from "fs";
42515
- import { basename as basename5, join as join26, resolve as resolve18 } from "path";
43057
+ import { createHash as createHash17, createVerify } from "crypto";
43058
+ import { existsSync as existsSync28, readdirSync as readdirSync6, readFileSync as readFileSync26, statSync as statSync10 } from "fs";
43059
+ import { basename as basename6, join as join27, resolve as resolve19 } from "path";
42516
43060
  init_redaction();
42517
43061
  function isObject2(value) {
42518
43062
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
@@ -42597,7 +43141,7 @@ function parseJson(path) {
42597
43141
  return JSON.parse(readFileSync26(path, "utf8"));
42598
43142
  }
42599
43143
  function sha2566(bytes) {
42600
- return `sha256:${createHash16("sha256").update(bytes).digest("hex")}`;
43144
+ return `sha256:${createHash17("sha256").update(bytes).digest("hex")}`;
42601
43145
  }
42602
43146
  function compareVersions(a, b) {
42603
43147
  const left = a.split(".").map((part) => Number.parseInt(part, 10) || 0);
@@ -42792,11 +43336,11 @@ function verifyExtensionSignature(input) {
42792
43336
  return verifier.verify(input.public_key, decodeSignature(input.signature));
42793
43337
  }
42794
43338
  function inspectExtensionSource(source9) {
42795
- const resolved = resolve18(source9);
42796
- if (!existsSync27(resolved))
43339
+ const resolved = resolve19(source9);
43340
+ if (!existsSync28(resolved))
42797
43341
  throw new Error(`extension source not found: ${source9}`);
42798
- const stat = statSync9(resolved);
42799
- const manifestPath = stat.isDirectory() ? [join26(resolved, "todos.extension.json"), join26(resolved, "extension.json")].find(existsSync27) : resolved;
43342
+ const stat = statSync10(resolved);
43343
+ const manifestPath = stat.isDirectory() ? [join27(resolved, "todos.extension.json"), join27(resolved, "extension.json")].find(existsSync28) : resolved;
42800
43344
  if (!manifestPath)
42801
43345
  throw new Error(`extension directory ${source9} is missing todos.extension.json`);
42802
43346
  const raw = readFileSync26(manifestPath);
@@ -42890,26 +43434,26 @@ function testExtensionCompatibility(sourceOrManifest) {
42890
43434
  function projectExtensionSources(projectPath) {
42891
43435
  if (!projectPath)
42892
43436
  return [];
42893
- const root = resolve18(projectPath);
43437
+ const root = resolve19(projectPath);
42894
43438
  const candidates = [
42895
- join26(root, "todos.extension.json"),
42896
- join26(root, ".todos", "todos.extension.json")
43439
+ join27(root, "todos.extension.json"),
43440
+ join27(root, ".todos", "todos.extension.json")
42897
43441
  ];
42898
- const extensionDir = join26(root, ".todos", "extensions");
42899
- if (existsSync27(extensionDir)) {
42900
- for (const entry2 of readdirSync5(extensionDir)) {
43442
+ const extensionDir = join27(root, ".todos", "extensions");
43443
+ if (existsSync28(extensionDir)) {
43444
+ for (const entry2 of readdirSync6(extensionDir)) {
42901
43445
  if (entry2.startsWith("."))
42902
43446
  continue;
42903
- const full = join26(extensionDir, entry2);
42904
- if (statSync9(full).isDirectory() || entry2.endsWith(".json"))
43447
+ const full = join27(extensionDir, entry2);
43448
+ if (statSync10(full).isDirectory() || entry2.endsWith(".json"))
42905
43449
  candidates.push(full);
42906
43450
  }
42907
43451
  }
42908
- return candidates.filter(existsSync27);
43452
+ return candidates.filter(existsSync28);
42909
43453
  }
42910
43454
  function discoverLocalExtensions(options = {}) {
42911
43455
  const config = loadConfig();
42912
- const projectPath = options.project_path ? resolve18(options.project_path) : null;
43456
+ const projectPath = options.project_path ? resolve19(options.project_path) : null;
42913
43457
  const configuredSources = [
42914
43458
  ...config.extension_sources || [],
42915
43459
  ...projectPath ? config.project_overrides?.[projectPath]?.extension_sources || [] : []
@@ -42917,7 +43461,7 @@ function discoverLocalExtensions(options = {}) {
42917
43461
  const sources = Array.from(new Set([
42918
43462
  ...configuredSources,
42919
43463
  ...projectExtensionSources(projectPath || undefined)
42920
- ])).map((source9) => projectPath && !source9.startsWith("/") ? resolve18(projectPath, source9) : resolve18(source9));
43464
+ ])).map((source9) => projectPath && !source9.startsWith("/") ? resolve19(projectPath, source9) : resolve19(source9));
42921
43465
  const warnings = [];
42922
43466
  const discovered = [];
42923
43467
  for (const source9 of sources) {
@@ -42999,7 +43543,7 @@ function removeLocalExtension(name) {
42999
43543
  return true;
43000
43544
  }
43001
43545
  function renderExtensionSummary(record) {
43002
- return `${record.name}@${record.version} ${record.status} ${basename5(record.source)} ${record.signature_verified ? "signed" : "unsigned"}`;
43546
+ return `${record.name}@${record.version} ${record.status} ${basename6(record.source)} ${record.signature_verified ? "signed" : "unsigned"}`;
43003
43547
  }
43004
43548
  // src/lib/workflow-prompts.ts
43005
43549
  var COMMON_ARGUMENTS = [
@@ -43766,7 +44310,7 @@ function resolveMissingTaskFindings(input, db) {
43766
44310
  init_redaction();
43767
44311
 
43768
44312
  // src/lib/retention-cleanup.ts
43769
- import { existsSync as existsSync28, unlinkSync as unlinkSync2 } from "fs";
44313
+ import { existsSync as existsSync29, unlinkSync as unlinkSync2 } from "fs";
43770
44314
  init_database();
43771
44315
  var RETENTION_CLEANUP_CONFIRMATION = "delete-local-retention-data";
43772
44316
  var ALL_SCOPES = ["comments", "runs", "verifications", "expired_artifacts"];
@@ -43978,7 +44522,7 @@ function applyRetentionCleanup(input, db) {
43978
44522
  for (const artifact of report.candidates.artifact_files) {
43979
44523
  try {
43980
44524
  const path = artifactStorePath(artifact.relative_path);
43981
- if (!existsSync28(path)) {
44525
+ if (!existsSync29(path)) {
43982
44526
  report.warnings.push(`stored artifact already missing: ${artifact.relative_path}`);
43983
44527
  continue;
43984
44528
  }
@@ -44216,8 +44760,8 @@ function renderScalePerformanceReportMarkdown(report) {
44216
44760
  init_database();
44217
44761
  init_migrations();
44218
44762
  init_schema();
44219
- import { chmodSync, copyFileSync as copyFileSync2, existsSync as existsSync29, mkdirSync as mkdirSync22, statSync as statSync10 } from "fs";
44220
- import { basename as basename6, dirname as dirname18, join as join27 } from "path";
44763
+ import { chmodSync, copyFileSync as copyFileSync2, existsSync as existsSync30, mkdirSync as mkdirSync22, statSync as statSync11 } from "fs";
44764
+ import { basename as basename7, dirname as dirname19, join as join28 } from "path";
44221
44765
  var REQUIRED_TABLES2 = [
44222
44766
  "_migrations",
44223
44767
  "projects",
@@ -44324,7 +44868,7 @@ function findMissingProjectRoots(db) {
44324
44868
  continue;
44325
44869
  if (!row.path.startsWith("/"))
44326
44870
  continue;
44327
- if (!existsSync29(row.path))
44871
+ if (!existsSync30(row.path))
44328
44872
  missing++;
44329
44873
  }
44330
44874
  return missing;
@@ -44376,7 +44920,7 @@ function databasePermissionsAreUnsafe(dbPath) {
44376
44920
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
44377
44921
  return false;
44378
44922
  try {
44379
- return (statSync10(dbPath).mode & 63) !== 0;
44923
+ return (statSync11(dbPath).mode & 63) !== 0;
44380
44924
  } catch {
44381
44925
  return false;
44382
44926
  }
@@ -44384,16 +44928,16 @@ function databasePermissionsAreUnsafe(dbPath) {
44384
44928
  function createBackup(dbPath) {
44385
44929
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
44386
44930
  return;
44387
- if (!existsSync29(dbPath))
44931
+ if (!existsSync30(dbPath))
44388
44932
  return;
44389
44933
  const stamp = now().replace(/[:.]/g, "-");
44390
- const backupDir = join27(dirname18(dbPath), `${basename6(dbPath)}.backup-${stamp}`);
44934
+ const backupDir = join28(dirname19(dbPath), `${basename7(dbPath)}.backup-${stamp}`);
44391
44935
  const files = [];
44392
44936
  mkdirSync22(backupDir, { recursive: true });
44393
44937
  for (const source9 of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
44394
- if (!existsSync29(source9))
44938
+ if (!existsSync30(source9))
44395
44939
  continue;
44396
- const target = join27(backupDir, basename6(source9));
44940
+ const target = join28(backupDir, basename7(source9));
44397
44941
  copyFileSync2(source9, target);
44398
44942
  files.push(target);
44399
44943
  }
@@ -45817,6 +46361,7 @@ export {
45817
46361
  dispatchToMultiple,
45818
46362
  dismissReminder,
45819
46363
  discoverVerificationProviderCapabilities,
46364
+ discoverTaskRouteSources,
45820
46365
  discoverProjectWorkspace,
45821
46366
  discoverLocalExtensions,
45822
46367
  detectSourceType,
@@ -46068,6 +46613,7 @@ export {
46068
46613
  TASK_WORKFLOW_POINTER_SCHEMA_VERSION,
46069
46614
  TASK_STATUSES,
46070
46615
  TASK_SCHEDULING_SCHEMA,
46616
+ TASK_ROUTE_SOURCE_DISCOVERY_SCHEMA_VERSION,
46071
46617
  TASK_PRIORITIES,
46072
46618
  TASK_FINDING_UPSERT_SCHEMA_VERSION,
46073
46619
  TASK_FINDING_SCHEMA_VERSION,