@hasna/todos 0.15.50 → 0.15.52

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
@@ -4075,23 +4075,118 @@ var init_identity_mapping = __esm(() => {
4075
4075
  });
4076
4076
  });
4077
4077
 
4078
- // src/lib/sync-utils.ts
4079
- import { existsSync as existsSync2, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
4080
- import { createHash } from "crypto";
4078
+ // node_modules/.bun/@hasna+paths@0.1.0/node_modules/@hasna/paths/dist/index.js
4081
4079
  import { homedir } from "os";
4082
4080
  import { join } from "path";
4081
+ function assertApp(app) {
4082
+ if (typeof app !== "string" || app.length === 0) {
4083
+ throw new TypeError("paths: app must be a non-empty string");
4084
+ }
4085
+ if (!APP_SLUG_RE.test(app)) {
4086
+ throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
4087
+ }
4088
+ }
4089
+ function envOf(options) {
4090
+ return options.env ?? process.env;
4091
+ }
4092
+ function envValue(options, kind) {
4093
+ const value = envOf(options)[KIND_ENV[kind]];
4094
+ return typeof value === "string" && value.length > 0 ? value : undefined;
4095
+ }
4096
+ function isMacOS(platform) {
4097
+ return platform === "darwin";
4098
+ }
4099
+ function baseDir(kind, options) {
4100
+ const override = envValue(options, kind);
4101
+ if (override)
4102
+ return override;
4103
+ const home = options.home ?? homedir();
4104
+ const platform = options.platform ?? process.platform;
4105
+ if (isMacOS(platform)) {
4106
+ switch (kind) {
4107
+ case "config":
4108
+ case "data":
4109
+ return join(home, "Library", "Application Support", "Hasna");
4110
+ case "cache":
4111
+ return join(home, "Library", "Caches", "Hasna");
4112
+ case "state":
4113
+ return join(home, "Library", "Logs", "Hasna");
4114
+ }
4115
+ }
4116
+ switch (kind) {
4117
+ case "config":
4118
+ return join(home, ".config", "hasna");
4119
+ case "data":
4120
+ return join(home, ".local", "share", "hasna");
4121
+ case "state":
4122
+ return join(home, ".local", "state", "hasna");
4123
+ case "cache":
4124
+ return join(home, ".cache", "hasna");
4125
+ }
4126
+ }
4127
+ function resolvePath(kind, options) {
4128
+ assertApp(options.app);
4129
+ const appSegment = options.internal === true ? join("internal", options.app) : options.app;
4130
+ return join(baseDir(kind, options), appSegment);
4131
+ }
4132
+ function dataDir(options) {
4133
+ return resolvePath("data", options);
4134
+ }
4135
+ var KIND_ENV, APP_SLUG_RE;
4136
+ var init_dist = __esm(() => {
4137
+ KIND_ENV = {
4138
+ config: "HASNA_CONFIG_HOME",
4139
+ data: "HASNA_DATA_HOME",
4140
+ state: "HASNA_STATE_HOME",
4141
+ cache: "HASNA_CACHE_HOME"
4142
+ };
4143
+ APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
4144
+ });
4145
+
4146
+ // src/lib/paths.ts
4147
+ import { existsSync as existsSync2 } from "fs";
4148
+ import { homedir as homedir2 } from "os";
4149
+ import { join as join2, resolve as resolve2 } from "path";
4150
+ function effectiveHome(env = process.env) {
4151
+ return env.HOME || env.USERPROFILE || homedir2();
4152
+ }
4153
+ function legacyHomeDir(env = process.env) {
4154
+ return join2(effectiveHome(env), ".hasna", "todos");
4155
+ }
4156
+ function resolverHome(env = process.env) {
4157
+ return dataDir({ app: "todos", home: effectiveHome(env), env });
4158
+ }
4159
+ function adoptResolverHome(resolved, env = process.env) {
4160
+ const dataOverride = env.HASNA_DATA_HOME;
4161
+ if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
4162
+ return true;
4163
+ return existsSync2(join2(resolved, "todos.db")) || existsSync2(join2(resolved, "config.json"));
4164
+ }
4165
+ function getTodosDir(env = process.env) {
4166
+ const resolved = resolverHome(env);
4167
+ return resolve2(adoptResolverHome(resolved, env) ? resolved : legacyHomeDir(env));
4168
+ }
4169
+ var init_paths = __esm(() => {
4170
+ init_dist();
4171
+ });
4172
+
4173
+ // src/lib/sync-utils.ts
4174
+ import { existsSync as existsSync3, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
4175
+ import { createHash } from "crypto";
4176
+ import { homedir as homedir3 } from "os";
4177
+ import { join as join3 } from "path";
4083
4178
  function getHomeDir() {
4084
- return process.env["HOME"] || process.env["USERPROFILE"] || homedir();
4179
+ return process.env["HOME"] || process.env["USERPROFILE"] || homedir3();
4085
4180
  }
4086
4181
  function getTodosGlobalDir() {
4087
- return join(getHomeDir(), ".hasna", "todos");
4182
+ return getTodosDir();
4088
4183
  }
4089
4184
  function ensureDir(dir) {
4090
- if (!existsSync2(dir))
4185
+ if (!existsSync3(dir))
4091
4186
  mkdirSync(dir, { recursive: true });
4092
4187
  }
4093
4188
  function listJsonFiles(dir) {
4094
- if (!existsSync2(dir))
4189
+ if (!existsSync3(dir))
4095
4190
  return [];
4096
4191
  return readdirSync(dir).filter((f) => f.endsWith(".json"));
4097
4192
  }
@@ -4107,14 +4202,14 @@ function writeJsonFile(path, data) {
4107
4202
  `);
4108
4203
  }
4109
4204
  function readHighWaterMark(dir) {
4110
- const path = join(dir, ".highwatermark");
4111
- if (!existsSync2(path))
4205
+ const path = join3(dir, ".highwatermark");
4206
+ if (!existsSync3(path))
4112
4207
  return 1;
4113
4208
  const val = parseInt(readFileSync(path, "utf-8").trim(), 10);
4114
4209
  return isNaN(val) ? 1 : val;
4115
4210
  }
4116
4211
  function writeHighWaterMark(dir, value) {
4117
- writeFileSync(join(dir, ".highwatermark"), String(value));
4212
+ writeFileSync(join3(dir, ".highwatermark"), String(value));
4118
4213
  }
4119
4214
  function getFileMtimeMs(path) {
4120
4215
  try {
@@ -4169,6 +4264,7 @@ function hasSyncFingerprintChanged(record) {
4169
4264
  }
4170
4265
  var TODO_SYNC_FINGERPRINT_KEY = "todos_sync_fingerprint", HOME;
4171
4266
  var init_sync_utils = __esm(() => {
4267
+ init_paths();
4172
4268
  HOME = getHomeDir();
4173
4269
  });
4174
4270
 
@@ -4479,18 +4575,18 @@ __export(exports_database, {
4479
4575
  LOCK_EXPIRY_MINUTES: () => LOCK_EXPIRY_MINUTES
4480
4576
  });
4481
4577
  import { Database } from "bun:sqlite";
4482
- import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
4483
- import { dirname, join as join2, resolve as resolve2 } from "path";
4578
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
4579
+ import { dirname, join as join4, resolve as resolve3 } from "path";
4484
4580
  function isInMemoryDb(path) {
4485
4581
  return path === ":memory:" || path.startsWith("file::memory:");
4486
4582
  }
4487
4583
  function findNearestProjectDb(startDir) {
4488
4584
  const gitRoot = findGitRoot(startDir);
4489
- const stopAt = gitRoot ? resolve2(gitRoot) : resolve2(startDir);
4490
- let dir = resolve2(startDir);
4585
+ const stopAt = gitRoot ? resolve3(gitRoot) : resolve3(startDir);
4586
+ let dir = resolve3(startDir);
4491
4587
  while (true) {
4492
- const candidate = join2(dir, ".hasna", "todos", "todos.db");
4493
- if (existsSync3(candidate))
4588
+ const candidate = join4(dir, ".hasna", "todos", "todos.db");
4589
+ if (existsSync4(candidate))
4494
4590
  return candidate;
4495
4591
  if (dir === stopAt)
4496
4592
  break;
@@ -4502,9 +4598,9 @@ function findNearestProjectDb(startDir) {
4502
4598
  return null;
4503
4599
  }
4504
4600
  function findGitRoot(startDir) {
4505
- let dir = resolve2(startDir);
4601
+ let dir = resolve3(startDir);
4506
4602
  while (true) {
4507
- if (existsSync3(join2(dir, ".git")))
4603
+ if (existsSync4(join4(dir, ".git")))
4508
4604
  return dir;
4509
4605
  const parent = dirname(dir);
4510
4606
  if (parent === dir)
@@ -4514,7 +4610,7 @@ function findGitRoot(startDir) {
4514
4610
  return null;
4515
4611
  }
4516
4612
  function getGlobalDbPath() {
4517
- return join2(getHomeDir(), ".hasna", "todos", "todos.db");
4613
+ return join4(getTodosGlobalDir(), "todos.db");
4518
4614
  }
4519
4615
  function hasExplicitProjectArg(args = process.argv.slice(2)) {
4520
4616
  return args.some((arg) => arg === "--project" || arg.startsWith("--project="));
@@ -4552,7 +4648,7 @@ function getDbPath() {
4552
4648
  if (process.env["TODOS_DB_SCOPE"] === "project") {
4553
4649
  const gitRoot = findGitRoot(cwd);
4554
4650
  if (gitRoot && canCreateScopedProjectDb()) {
4555
- return join2(gitRoot, ".hasna", "todos", "todos.db");
4651
+ return join4(gitRoot, ".hasna", "todos", "todos.db");
4556
4652
  }
4557
4653
  }
4558
4654
  return getGlobalDbPath();
@@ -4563,8 +4659,8 @@ function getDatabasePath() {
4563
4659
  function ensureDir2(filePath) {
4564
4660
  if (isInMemoryDb(filePath))
4565
4661
  return;
4566
- const dir = dirname(resolve2(filePath));
4567
- if (!existsSync3(dir)) {
4662
+ const dir = dirname(resolve3(filePath));
4663
+ if (!existsSync4(dir)) {
4568
4664
  mkdirSync2(dir, { recursive: true });
4569
4665
  }
4570
4666
  }
@@ -4741,10 +4837,10 @@ var init_database = __esm(() => {
4741
4837
  });
4742
4838
 
4743
4839
  // src/lib/config.ts
4744
- import { existsSync as existsSync4, readFileSync as readFileSync2 } from "fs";
4745
- import { dirname as dirname2, join as join3 } from "path";
4840
+ import { existsSync as existsSync5, readFileSync as readFileSync2 } from "fs";
4841
+ import { dirname as dirname2, join as join5 } from "path";
4746
4842
  function getConfigPath() {
4747
- return join3(getTodosGlobalDir(), "config.json");
4843
+ return join5(getTodosGlobalDir(), "config.json");
4748
4844
  }
4749
4845
  function normalizeAgent(agent) {
4750
4846
  return agent.trim().toLowerCase();
@@ -4752,7 +4848,7 @@ function normalizeAgent(agent) {
4752
4848
  function loadConfig() {
4753
4849
  if (cached)
4754
4850
  return cached;
4755
- if (!existsSync4(getConfigPath())) {
4851
+ if (!existsSync5(getConfigPath())) {
4756
4852
  cached = {};
4757
4853
  return cached;
4758
4854
  }
@@ -4775,7 +4871,7 @@ function updateConfig(patch) {
4775
4871
  }
4776
4872
  function getTodosAiConfig() {
4777
4873
  const configPath = getConfigPath();
4778
- if (!existsSync4(configPath))
4874
+ if (!existsSync5(configPath))
4779
4875
  return {};
4780
4876
  const parsed = JSON.parse(readFileSync2(configPath, "utf8"));
4781
4877
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -5393,14 +5489,14 @@ var init_completion_guard = __esm(() => {
5393
5489
 
5394
5490
  // src/lib/event-emission-safety.ts
5395
5491
  import { tmpdir } from "os";
5396
- import { resolve as resolve3, sep } from "path";
5492
+ import { resolve as resolve4, sep } from "path";
5397
5493
  function envFlag(name) {
5398
5494
  const value = process.env[name]?.trim().toLowerCase();
5399
5495
  return value === "1" || value === "true" || value === "yes" || value === "on";
5400
5496
  }
5401
5497
  function isUnder(parent, child) {
5402
- const normalizedParent = resolve3(parent);
5403
- const normalizedChild = resolve3(child);
5498
+ const normalizedParent = resolve4(parent);
5499
+ const normalizedChild = resolve4(child);
5404
5500
  return normalizedChild === normalizedParent || normalizedChild.startsWith(`${normalizedParent}${sep}`);
5405
5501
  }
5406
5502
  function databasePathFromDatabase(db) {
@@ -5561,9 +5657,9 @@ var init_redaction = __esm(() => {
5561
5657
  });
5562
5658
 
5563
5659
  // src/lib/workspace-trust.ts
5564
- import { relative, resolve as resolve4 } from "path";
5660
+ import { relative, resolve as resolve5 } from "path";
5565
5661
  function normalizePath(path) {
5566
- return resolve4(path);
5662
+ return resolve5(path);
5567
5663
  }
5568
5664
  function unique2(values) {
5569
5665
  return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
@@ -5745,9 +5841,9 @@ var init_workspace_trust = __esm(() => {
5745
5841
  });
5746
5842
 
5747
5843
  // src/lib/runner-sandbox.ts
5748
- import { relative as relative2, resolve as resolve5 } from "path";
5844
+ import { relative as relative2, resolve as resolve6 } from "path";
5749
5845
  function normalizePath2(path) {
5750
- return resolve5(path);
5846
+ return resolve6(path);
5751
5847
  }
5752
5848
  function unique3(values) {
5753
5849
  return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
@@ -5940,7 +6036,7 @@ var init_runner_sandbox = __esm(() => {
5940
6036
  // src/lib/event-hooks.ts
5941
6037
  import { createHash as createHash2, randomUUID } from "crypto";
5942
6038
  import { appendFileSync, mkdirSync as mkdirSync3 } from "fs";
5943
- import { dirname as dirname3, resolve as resolve6 } from "path";
6039
+ import { dirname as dirname3, resolve as resolve7 } from "path";
5944
6040
  import { createConnection } from "net";
5945
6041
  function safeName(name) {
5946
6042
  const trimmed = name.trim();
@@ -6078,7 +6174,7 @@ async function deliverHook(hook, envelope) {
6078
6174
  if (hook.target === "stdout") {
6079
6175
  output = line.trim();
6080
6176
  } else if (hook.target === "file") {
6081
- const filePath = resolve6(hook.file_path);
6177
+ const filePath = resolve7(hook.file_path);
6082
6178
  mkdirSync3(dirname3(filePath), { recursive: true });
6083
6179
  appendFileSync(filePath, line);
6084
6180
  } else if (hook.target === "socket") {
@@ -6199,9 +6295,9 @@ var init_event_hooks = __esm(() => {
6199
6295
  // node_modules/.bun/@hasna+events@0.1.16/node_modules/@hasna/events/dist/index.js
6200
6296
  import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
6201
6297
  import { Buffer as Buffer2 } from "buffer";
6202
- import { existsSync as existsSync5 } from "fs";
6203
- import { homedir as homedir2 } from "os";
6204
- import { join as join4 } from "path";
6298
+ import { existsSync as existsSync6 } from "fs";
6299
+ import { homedir as homedir4 } from "os";
6300
+ import { join as join6 } from "path";
6205
6301
  import { createHmac, timingSafeEqual } from "crypto";
6206
6302
  import { randomUUID as randomUUID2 } from "crypto";
6207
6303
  import { spawn } from "child_process";
@@ -6302,7 +6398,7 @@ function channelMatchesEvent(channel, event) {
6302
6398
  return channel.filters.some((filter) => eventMatchesFilter(event, filter));
6303
6399
  }
6304
6400
  function getEventsDataDir(override) {
6305
- return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join4(homedir2(), ".hasna", "events");
6401
+ return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join6(homedir4(), ".hasna", "events");
6306
6402
  }
6307
6403
 
6308
6404
  class JsonEventsStore {
@@ -6311,12 +6407,12 @@ class JsonEventsStore {
6311
6407
  channelsPath;
6312
6408
  eventsPath;
6313
6409
  deliveriesPath;
6314
- constructor(dataDir = getEventsDataDir()) {
6315
- this.dataDir = dataDir;
6316
- this.runtime = localJsonRuntime(dataDir);
6317
- this.channelsPath = join4(dataDir, "channels.json");
6318
- this.eventsPath = join4(dataDir, "events.json");
6319
- this.deliveriesPath = join4(dataDir, "deliveries.json");
6410
+ constructor(dataDir2 = getEventsDataDir()) {
6411
+ this.dataDir = dataDir2;
6412
+ this.runtime = localJsonRuntime(dataDir2);
6413
+ this.channelsPath = join6(dataDir2, "channels.json");
6414
+ this.eventsPath = join6(dataDir2, "events.json");
6415
+ this.deliveriesPath = join6(dataDir2, "deliveries.json");
6320
6416
  }
6321
6417
  async init() {
6322
6418
  await mkdir(this.dataDir, { recursive: true, mode: 448 });
@@ -6433,7 +6529,7 @@ class JsonEventsStore {
6433
6529
  };
6434
6530
  }
6435
6531
  async ensureArrayFile(path) {
6436
- if (!existsSync5(path)) {
6532
+ if (!existsSync6(path)) {
6437
6533
  await writeFile(path, `[]
6438
6534
  `, { encoding: "utf-8", mode: 384 });
6439
6535
  }
@@ -6463,7 +6559,7 @@ class JsonEventsStore {
6463
6559
  });
6464
6560
  }
6465
6561
  }
6466
- function localJsonRuntime(dataDir = getEventsDataDir()) {
6562
+ function localJsonRuntime(dataDir2 = getEventsDataDir()) {
6467
6563
  return {
6468
6564
  mode: "local-files",
6469
6565
  name: "json-events-store",
@@ -6476,7 +6572,7 @@ function localJsonRuntime(dataDir = getEventsDataDir()) {
6476
6572
  durable: true,
6477
6573
  idempotency: "best-effort-local",
6478
6574
  replayCursors: true,
6479
- description: `Local JSON files in ${dataDir}; no SQLite, Postgres, S3, or AWS runtime is configured by this store.`
6575
+ description: `Local JSON files in ${dataDir2}; no SQLite, Postgres, S3, or AWS runtime is configured by this store.`
6480
6576
  };
6481
6577
  }
6482
6578
  function encodeLocalJsonEventCursor(offset, options = {}) {
@@ -6655,7 +6751,7 @@ async function dispatchCommand(event, channel) {
6655
6751
  HASNA_EVENT_SCHEMA_VERSION: event.schemaVersion,
6656
6752
  HASNA_EVENT_JSON: eventJson
6657
6753
  };
6658
- return new Promise((resolve7) => {
6754
+ return new Promise((resolve8) => {
6659
6755
  const child = spawn(channel.command.command, channel.command.args ?? [], {
6660
6756
  cwd: channel.command.cwd,
6661
6757
  env,
@@ -6673,7 +6769,7 @@ async function dispatchCommand(event, channel) {
6673
6769
  });
6674
6770
  child.on("error", (error) => {
6675
6771
  clearTimeout(timeout);
6676
- resolve7({
6772
+ resolve8({
6677
6773
  attempt: 1,
6678
6774
  status: "failed",
6679
6775
  startedAt,
@@ -6686,7 +6782,7 @@ async function dispatchCommand(event, channel) {
6686
6782
  child.on("close", (code, signal) => {
6687
6783
  clearTimeout(timeout);
6688
6784
  const success = code === 0;
6689
- resolve7({
6785
+ resolve8({
6690
6786
  attempt: 1,
6691
6787
  status: success ? "success" : "failed",
6692
6788
  startedAt,
@@ -7028,7 +7124,7 @@ function normalizeRetryPolicy(policy) {
7028
7124
  };
7029
7125
  }
7030
7126
  var HASNA_EVENTS_DIR_ENV = "HASNA_EVENTS_DIR", HASNA_EVENTS_HOME_ENV = "HASNA_EVENTS_HOME", LOCAL_JSON_EVENT_CURSOR_PREFIX = "local-json-v1:", DEFAULT_EVENT_PAGE_LIMIT = 100, MAX_EVENT_PAGE_LIMIT = 1000, DEFAULT_SIGNATURE_TOLERANCE_MS, EventValidationError, defaultEventTypeCatalog, APP_EVENT_V1_MAX_DATA_BYTES;
7031
- var init_dist = __esm(() => {
7127
+ var init_dist2 = __esm(() => {
7032
7128
  DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
7033
7129
  EventValidationError = class EventValidationError extends Error {
7034
7130
  eventType;
@@ -7451,7 +7547,7 @@ function emitSharedTaskEventQuiet(input) {
7451
7547
  }
7452
7548
  var SOURCE = "todos";
7453
7549
  var init_shared_events = __esm(() => {
7454
- init_dist();
7550
+ init_dist2();
7455
7551
  init_database();
7456
7552
  init_projects();
7457
7553
  init_task_lists();
@@ -7459,7 +7555,7 @@ var init_shared_events = __esm(() => {
7459
7555
  });
7460
7556
 
7461
7557
  // src/lib/secret-redaction.ts
7462
- import { readFileSync as readFileSync3, existsSync as existsSync6 } from "fs";
7558
+ import { readFileSync as readFileSync3, existsSync as existsSync7 } from "fs";
7463
7559
  function registerCustomRedactor(fn) {
7464
7560
  customRedactors.push(fn);
7465
7561
  }
@@ -7524,7 +7620,7 @@ function scanAndRedactText(text, options = {}) {
7524
7620
  };
7525
7621
  }
7526
7622
  function scanFileForSecrets(path, options = {}) {
7527
- if (!existsSync6(path))
7623
+ if (!existsSync7(path))
7528
7624
  throw new Error(`File not found: ${path}`);
7529
7625
  const content = readFileSync3(path, "utf8");
7530
7626
  return scanAndRedactText(content, options);
@@ -9782,17 +9878,17 @@ function sanitizeCreateTaskInput(input) {
9782
9878
  return {
9783
9879
  ...input,
9784
9880
  title: sanitizePreWriteText(input.title, "task.title"),
9785
- description: input.description !== undefined ? sanitizePreWriteText(input.description, "task.description") : undefined,
9881
+ description: input.description == null ? input.description : sanitizePreWriteText(input.description, "task.description"),
9786
9882
  tags: input.tags !== undefined ? sanitizePreWriteValue(input.tags, "task.tags") : undefined,
9787
9883
  metadata: input.metadata !== undefined ? sanitizePreWriteValue(input.metadata, "task.metadata") : undefined,
9788
- reason: input.reason !== undefined ? sanitizePreWriteText(input.reason, "task.reason") : undefined
9884
+ reason: input.reason == null ? input.reason : sanitizePreWriteText(input.reason, "task.reason")
9789
9885
  };
9790
9886
  }
9791
9887
  function sanitizeUpdateTaskInput(input) {
9792
9888
  return {
9793
9889
  ...input,
9794
9890
  title: input.title !== undefined ? sanitizePreWriteText(input.title, "task.title") : undefined,
9795
- description: input.description !== undefined ? sanitizePreWriteText(input.description, "task.description") : undefined,
9891
+ description: input.description == null ? input.description : sanitizePreWriteText(input.description, "task.description"),
9796
9892
  tags: input.tags !== undefined ? sanitizePreWriteValue(input.tags, "task.tags") : undefined,
9797
9893
  metadata: input.metadata !== undefined ? sanitizePreWriteValue(input.metadata, "task.metadata") : undefined
9798
9894
  };
@@ -11646,28 +11742,28 @@ var init_boards = __esm(() => {
11646
11742
 
11647
11743
  // src/lib/artifact-store.ts
11648
11744
  import { createHash as createHash3 } from "crypto";
11649
- import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
11650
- import { basename, dirname as dirname4, join as join5, resolve as resolve7 } from "path";
11745
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
11746
+ import { basename, dirname as dirname4, join as join7, resolve as resolve8 } from "path";
11651
11747
  import { tmpdir as tmpdir2 } from "os";
11652
11748
  function isInMemoryDb2(path) {
11653
11749
  return path === ":memory:" || path.startsWith("file::memory:");
11654
11750
  }
11655
11751
  function artifactStoreRoot() {
11656
11752
  if (process.env["HASNA_TODOS_ARTIFACTS_DIR"])
11657
- return resolve7(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
11753
+ return resolve8(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
11658
11754
  if (process.env["TODOS_ARTIFACTS_DIR"])
11659
- return resolve7(process.env["TODOS_ARTIFACTS_DIR"]);
11755
+ return resolve8(process.env["TODOS_ARTIFACTS_DIR"]);
11660
11756
  const dbPath = getDatabasePath();
11661
11757
  if (isInMemoryDb2(dbPath))
11662
- return join5(tmpdir2(), "hasna-todos-artifacts");
11663
- return join5(dirname4(resolve7(dbPath)), "artifacts");
11758
+ return join7(tmpdir2(), "hasna-todos-artifacts");
11759
+ return join7(dirname4(resolve8(dbPath)), "artifacts");
11664
11760
  }
11665
11761
  function artifactStorePath(relativePath) {
11666
11762
  const normalized = relativePath.replace(/\\/g, "/");
11667
11763
  if (normalized.includes("..") || normalized.startsWith("/") || normalized.length === 0) {
11668
11764
  throw new Error("Invalid artifact store path");
11669
11765
  }
11670
- return join5(artifactStoreRoot(), normalized);
11766
+ return join7(artifactStoreRoot(), normalized);
11671
11767
  }
11672
11768
  function sha256(buffer) {
11673
11769
  return createHash3("sha256").update(buffer).digest("hex");
@@ -11707,8 +11803,8 @@ function mediaTypeFor(path, textLike) {
11707
11803
  return "application/octet-stream";
11708
11804
  }
11709
11805
  function storeArtifactContent(input) {
11710
- const sourcePath = resolve7(input.path);
11711
- if (!existsSync7(sourcePath))
11806
+ const sourcePath = resolve8(input.path);
11807
+ if (!existsSync8(sourcePath))
11712
11808
  return null;
11713
11809
  const sourceStat = statSync2(sourcePath);
11714
11810
  if (!sourceStat.isFile())
@@ -11725,9 +11821,9 @@ function storeArtifactContent(input) {
11725
11821
  redactionStatus = "redacted";
11726
11822
  }
11727
11823
  const storedSha = sha256(storedBuffer);
11728
- const relativePath = join5("sha256", storedSha.slice(0, 2), storedSha).replace(/\\/g, "/");
11824
+ const relativePath = join7("sha256", storedSha.slice(0, 2), storedSha).replace(/\\/g, "/");
11729
11825
  const destination = artifactStorePath(relativePath);
11730
- if (!existsSync7(destination)) {
11826
+ if (!existsSync8(destination)) {
11731
11827
  mkdirSync4(dirname4(destination), { recursive: true });
11732
11828
  writeFileSync2(destination, storedBuffer);
11733
11829
  }
@@ -11787,7 +11883,7 @@ function verifyStoredArtifact(input) {
11787
11883
  };
11788
11884
  }
11789
11885
  const storedPath = artifactStorePath(store.relative_path);
11790
- if (!existsSync7(storedPath)) {
11886
+ if (!existsSync8(storedPath)) {
11791
11887
  return {
11792
11888
  id: input.id,
11793
11889
  path: input.path,
@@ -11862,20 +11958,20 @@ function importStoredArtifactContent(content) {
11862
11958
  }
11863
11959
  function getArtifactStoreRoot(dbPath) {
11864
11960
  if (process.env["HASNA_TODOS_ARTIFACTS_DIR"])
11865
- return resolve7(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
11961
+ return resolve8(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
11866
11962
  if (process.env["TODOS_ARTIFACTS_DIR"])
11867
- return resolve7(process.env["TODOS_ARTIFACTS_DIR"]);
11963
+ return resolve8(process.env["TODOS_ARTIFACTS_DIR"]);
11868
11964
  const path = dbPath ?? getDatabasePath();
11869
11965
  if (isInMemoryDb2(path))
11870
- return join5(tmpdir2(), "hasna-todos-artifacts");
11871
- return join5(dirname4(resolve7(path)), "artifacts");
11966
+ return join7(tmpdir2(), "hasna-todos-artifacts");
11967
+ return join7(dirname4(resolve8(path)), "artifacts");
11872
11968
  }
11873
11969
  function computeContentHash(path) {
11874
- return sha256(readFileSync4(resolve7(path)));
11970
+ return sha256(readFileSync4(resolve8(path)));
11875
11971
  }
11876
11972
  function storeArtifactFile(input) {
11877
- const sourcePath = resolve7(input.sourcePath);
11878
- if (!existsSync7(sourcePath)) {
11973
+ const sourcePath = resolve8(input.sourcePath);
11974
+ if (!existsSync8(sourcePath)) {
11879
11975
  throw new Error(`Source file not found: ${input.sourcePath}`);
11880
11976
  }
11881
11977
  if (!statSync2(sourcePath).isFile()) {
@@ -11888,7 +11984,7 @@ function storeArtifactFile(input) {
11888
11984
  let localPath = sourcePath;
11889
11985
  if (storageMode === "copy") {
11890
11986
  const fileName = input.name && input.name.trim().length > 0 ? basename(input.name) : basename(sourcePath);
11891
- const destination = join5(getArtifactStoreRoot(input.dbPath), input.artifactId, fileName);
11987
+ const destination = join7(getArtifactStoreRoot(input.dbPath), input.artifactId, fileName);
11892
11988
  mkdirSync4(dirname4(destination), { recursive: true });
11893
11989
  writeFileSync2(destination, buffer);
11894
11990
  localPath = destination;
@@ -11898,7 +11994,7 @@ function storeArtifactFile(input) {
11898
11994
  function deleteStoredArtifactFile(localPath, storageMode, _dbPath2) {
11899
11995
  if (storageMode === "reference")
11900
11996
  return false;
11901
- if (!localPath || !existsSync7(localPath))
11997
+ if (!localPath || !existsSync8(localPath))
11902
11998
  return false;
11903
11999
  rmSync(localPath, { force: true });
11904
12000
  try {
@@ -11923,7 +12019,7 @@ function buildArtifactExportManifest(artifacts, dbPath) {
11923
12019
  };
11924
12020
  }
11925
12021
  function writeArtifactExportManifest(manifest, outputPath) {
11926
- const destination = resolve7(outputPath);
12022
+ const destination = resolve8(outputPath);
11927
12023
  mkdirSync4(dirname4(destination), { recursive: true });
11928
12024
  writeFileSync2(destination, `${JSON.stringify(manifest, null, 2)}
11929
12025
  `);
@@ -13193,7 +13289,7 @@ var init_dispatches = __esm(() => {
13193
13289
  // package.json
13194
13290
  var package_default = {
13195
13291
  name: "@hasna/todos",
13196
- version: "0.15.50",
13292
+ version: "0.15.52",
13197
13293
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
13198
13294
  type: "module",
13199
13295
  main: "dist/index.js",
@@ -13252,6 +13348,7 @@ var package_default = {
13252
13348
  files: [
13253
13349
  "dist",
13254
13350
  "dashboard/dist",
13351
+ "postinstall.js",
13255
13352
  "LICENSE",
13256
13353
  "README.md"
13257
13354
  ],
@@ -13278,7 +13375,7 @@ var package_default = {
13278
13375
  "test:attested-container-compatibility-vector": "bun test scripts/attested-container-compatibility-vector.test.ts",
13279
13376
  "issue:release-review": "bun run scripts/issue-npm-release-agent-review.ts",
13280
13377
  prepublishOnly: "bun run scripts/verify-public-release.ts --mode=publish",
13281
- postinstall: "mkdir -p $HOME/.hasna/todos $HOME/.hasna/todos/training 2>/dev/null || true"
13378
+ postinstall: "node postinstall.js"
13282
13379
  },
13283
13380
  keywords: [
13284
13381
  "todos",
@@ -13312,13 +13409,15 @@ var package_default = {
13312
13409
  author: "Andrei Hasna <andrei@hasna.com>",
13313
13410
  license: "Apache-2.0",
13314
13411
  dependencies: {
13315
- "@hasna/contracts": "0.14.0",
13412
+ "@hasna/contracts": "0.14.2",
13316
13413
  "@hasna/events": "^0.1.11",
13414
+ "@hasna/paths": "0.1.0",
13317
13415
  "@modelcontextprotocol/sdk": "^1.12.1",
13318
13416
  chalk: "^5.4.1",
13319
13417
  commander: "^13.1.0",
13320
13418
  ink: "^5.2.0",
13321
13419
  react: "^18.3.1",
13420
+ "signal-exit": "3.0.7",
13322
13421
  zod: "3.25.76"
13323
13422
  },
13324
13423
  overrides: {
@@ -13327,7 +13426,7 @@ var package_default = {
13327
13426
  zod: "3.25.76"
13328
13427
  },
13329
13428
  devDependencies: {
13330
- "@types/bun": "^1.2.4",
13429
+ "@types/bun": "1.3.14",
13331
13430
  "@types/react": "^18.3.18",
13332
13431
  "bun-types": "1.3.9",
13333
13432
  "hasna-deployment-contracts": "npm:@hasna/contracts@0.10.4",
@@ -16553,7 +16652,7 @@ function todosAiExitCodeForResult(result) {
16553
16652
  }
16554
16653
  // src/lib/onboarding-fixtures.ts
16555
16654
  import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync3 } from "fs";
16556
- import { join as join6 } from "path";
16655
+ import { join as join8 } from "path";
16557
16656
 
16558
16657
  // src/lib/local-bridge.ts
16559
16658
  init_database();
@@ -17490,7 +17589,7 @@ function writeOnboardingFixtureFiles(directory) {
17490
17589
  mkdirSync5(directory, { recursive: true });
17491
17590
  const files = [];
17492
17591
  for (const fixture of allFixtures()) {
17493
- const path = join6(directory, `${fixture.summary.name}.bridge.json`);
17592
+ const path = join8(directory, `${fixture.summary.name}.bridge.json`);
17494
17593
  writeFileSync3(path, `${JSON.stringify(fixture.bundle, null, 2)}
17495
17594
  `, "utf-8");
17496
17595
  files.push(path);
@@ -17508,7 +17607,7 @@ function importOnboardingFixture(options = {}) {
17508
17607
  init_database();
17509
17608
  import { createHash as createHash4 } from "crypto";
17510
17609
  import { readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "fs";
17511
- import { dirname as dirname5, resolve as resolve8 } from "path";
17610
+ import { dirname as dirname5, resolve as resolve9 } from "path";
17512
17611
  import { mkdirSync as mkdirSync6 } from "fs";
17513
17612
  var TODOS_LOCAL_BACKUP_KIND = "hasna.todos.local-backup";
17514
17613
  var TODOS_LOCAL_BACKUP_SCHEMA_VERSION = 1;
@@ -17615,14 +17714,14 @@ function createLocalBackup(options = {}, db) {
17615
17714
  return backup;
17616
17715
  }
17617
17716
  function writeLocalBackupFile(backup, outputPath) {
17618
- const path = resolve8(outputPath);
17717
+ const path = resolve9(outputPath);
17619
17718
  mkdirSync6(dirname5(path), { recursive: true });
17620
17719
  writeFileSync4(path, `${JSON.stringify(backup, null, 2)}
17621
17720
  `);
17622
17721
  return path;
17623
17722
  }
17624
17723
  function readLocalBackupFile(path) {
17625
- return JSON.parse(readFileSync5(resolve8(path), "utf-8"));
17724
+ return JSON.parse(readFileSync5(resolve9(path), "utf-8"));
17626
17725
  }
17627
17726
  function verifyLocalBackup(value, options = {}, db) {
17628
17727
  const verifiedAt = options.verified_at ?? now();
@@ -18316,7 +18415,7 @@ function renderLocalSnapshotMarkdown(snapshot) {
18316
18415
  }
18317
18416
  // src/lib/sdk-integration-fixtures.ts
18318
18417
  import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync5 } from "fs";
18319
- import { join as join7 } from "path";
18418
+ import { join as join9 } from "path";
18320
18419
 
18321
18420
  // src/cli-mcp-parity.ts
18322
18421
  function source4(version) {
@@ -20287,7 +20386,7 @@ function writeSdkIntegrationFixtures(directory, options = {}) {
20287
20386
  ];
20288
20387
  const written = [];
20289
20388
  for (const [name, payload] of files) {
20290
- const file = join7(directory, name);
20389
+ const file = join9(directory, name);
20291
20390
  writeFileSync5(file, `${JSON.stringify(payload, null, 2)}
20292
20391
  `, "utf-8");
20293
20392
  written.push(file);
@@ -21570,7 +21669,7 @@ function renderLocalAuditLedgerMarkdown(ledger) {
21570
21669
  init_migrations();
21571
21670
  init_schema();
21572
21671
  import { readFileSync as readFileSync6 } from "fs";
21573
- import { join as join8, resolve as resolve9 } from "path";
21672
+ import { join as join10, resolve as resolve10 } from "path";
21574
21673
  import { Database as Database2 } from "bun:sqlite";
21575
21674
  var LOCAL_RELEASE_COMPATIBILITY_SCHEMA_VERSION = 1;
21576
21675
  var EXPECTED_PACKAGE_NAME = "@hasna/todos";
@@ -21616,7 +21715,7 @@ function warn(id, message, details) {
21616
21715
  return { id, status: "warning", message, details };
21617
21716
  }
21618
21717
  function readPackageJson(root) {
21619
- return JSON.parse(readFileSync6(join8(root, "package.json"), "utf8"));
21718
+ return JSON.parse(readFileSync6(join10(root, "package.json"), "utf8"));
21620
21719
  }
21621
21720
  function sortedKeys(value) {
21622
21721
  return Object.keys(value ?? {}).sort((left, right) => left.localeCompare(right));
@@ -21712,7 +21811,7 @@ function checkChangelog() {
21712
21811
  ];
21713
21812
  }
21714
21813
  function createReleaseCompatibilityReport(options = {}) {
21715
- const root = resolve9(options.root ?? process.cwd());
21814
+ const root = resolve10(options.root ?? process.cwd());
21716
21815
  const packageJson = readPackageJson(root);
21717
21816
  const simulatedLevels = options.simulated_levels ?? defaultSimulationLevels();
21718
21817
  const checks = [
@@ -30707,7 +30806,7 @@ async function retryOnTransientPostgresError(fn, attempts = 2, delayMs = 150) {
30707
30806
  lastError = error;
30708
30807
  if (!isTransientPostgresError(error) || attempt === attempts)
30709
30808
  throw error;
30710
- await new Promise((resolve10) => setTimeout(resolve10, delayMs * attempt));
30809
+ await new Promise((resolve11) => setTimeout(resolve11, delayMs * attempt));
30711
30810
  }
30712
30811
  }
30713
30812
  throw lastError;
@@ -30808,8 +30907,8 @@ class TodosShadowMirror {
30808
30907
  async flush() {
30809
30908
  if (this.idle())
30810
30909
  return;
30811
- await new Promise((resolve10) => {
30812
- this.idleResolvers.push(resolve10);
30910
+ await new Promise((resolve11) => {
30911
+ this.idleResolvers.push(resolve11);
30813
30912
  this.pump();
30814
30913
  });
30815
30914
  }
@@ -30821,8 +30920,8 @@ class TodosShadowMirror {
30821
30920
  return;
30822
30921
  const resolvers = this.idleResolvers;
30823
30922
  this.idleResolvers = [];
30824
- for (const resolve10 of resolvers)
30825
- resolve10();
30923
+ for (const resolve11 of resolvers)
30924
+ resolve11();
30826
30925
  }
30827
30926
  pump() {
30828
30927
  if (this.pumping)
@@ -31301,7 +31400,7 @@ class TodosShadowOutbox {
31301
31400
  const remaining = deadline - Date.now();
31302
31401
  if (remaining <= 0)
31303
31402
  break;
31304
- await new Promise((resolve10) => setTimeout(resolve10, Math.min(200, remaining)));
31403
+ await new Promise((resolve11) => setTimeout(resolve11, Math.min(200, remaining)));
31305
31404
  }
31306
31405
  }
31307
31406
  return this.getStats();
@@ -32605,7 +32704,7 @@ class TodosClient {
32605
32704
  return this._fetchWithRetry(path, { method: "DELETE" });
32606
32705
  }
32607
32706
  _sleep(ms) {
32608
- return new Promise((resolve10) => setTimeout(resolve10, ms));
32707
+ return new Promise((resolve11) => setTimeout(resolve11, ms));
32609
32708
  }
32610
32709
  async getHealth() {
32611
32710
  return this._get("/api/health");
@@ -34262,8 +34361,8 @@ class SqlitePrGroupLedgerPersistence {
34262
34361
  async transaction(fn) {
34263
34362
  const previous = sqliteTransactionTails.get(this.db) ?? Promise.resolve();
34264
34363
  let release;
34265
- const current = new Promise((resolve10) => {
34266
- release = resolve10;
34364
+ const current = new Promise((resolve11) => {
34365
+ release = resolve11;
34267
34366
  });
34268
34367
  sqliteTransactionTails.set(this.db, current);
34269
34368
  await previous;
@@ -36281,8 +36380,8 @@ class SqliteTodosProjectRegistrationBackend {
36281
36380
  async transaction(fn) {
36282
36381
  const previous = sqliteTransactionTails2.get(this.db) ?? Promise.resolve();
36283
36382
  let release;
36284
- const current = new Promise((resolve10) => {
36285
- release = resolve10;
36383
+ const current = new Promise((resolve11) => {
36384
+ release = resolve11;
36286
36385
  });
36287
36386
  sqliteTransactionTails2.set(this.db, current);
36288
36387
  await previous;
@@ -42561,8 +42660,8 @@ class SqliteTodosTaskManifestBackend {
42561
42660
  async serialized(run) {
42562
42661
  const previous = sqliteTails.get(this.db) ?? Promise.resolve();
42563
42662
  let release;
42564
- const current = new Promise((resolve10) => {
42565
- release = resolve10;
42663
+ const current = new Promise((resolve11) => {
42664
+ release = resolve11;
42566
42665
  });
42567
42666
  const tail = previous.then(() => current);
42568
42667
  sqliteTails.set(this.db, tail);
@@ -44903,8 +45002,8 @@ class SqliteTodosTaskSubtreeTransferBackend {
44903
45002
  async serialized(run) {
44904
45003
  const previous = sqliteTails2.get(this.db) ?? Promise.resolve();
44905
45004
  let release;
44906
- const current = new Promise((resolve10) => {
44907
- release = resolve10;
45005
+ const current = new Promise((resolve11) => {
45006
+ release = resolve11;
44908
45007
  });
44909
45008
  const tail = previous.then(() => current);
44910
45009
  sqliteTails2.set(this.db, tail);
@@ -45655,15 +45754,23 @@ function normalizeRemoteAuthorityUrl(value) {
45655
45754
  if (url.search || url.hash) {
45656
45755
  throw new Error("REMOTE_API_URL_INVALID: HASNA_TODOS_API_URL must not contain a query or fragment; local SQLite fallback is disabled");
45657
45756
  }
45658
- if (url.pathname !== "/" && url.pathname !== "/v1" && url.pathname !== "/v1/") {
45659
- throw new Error("REMOTE_API_URL_INVALID: HASNA_TODOS_API_URL must be an authority root or end in /v1, not /api/v1 or another path; " + "local SQLite fallback is disabled");
45757
+ const path = url.pathname.replace(/\/+$/, "");
45758
+ const segments = path.split("/").filter(Boolean);
45759
+ const reservedGatewaySegments = new Set(["api", "v1"]);
45760
+ const isRoot = path === "";
45761
+ const isV1Root = segments.length === 1 && segments[0] === "v1";
45762
+ const isAppRoot = segments.length === 1 && !reservedGatewaySegments.has(segments[0].toLowerCase());
45763
+ const isAppV1Root = segments.length === 2 && segments[1] === "v1" && !reservedGatewaySegments.has(segments[0].toLowerCase());
45764
+ if (!isRoot && !isV1Root && !isAppRoot && !isAppV1Root) {
45765
+ throw new Error("REMOTE_API_URL_INVALID: HASNA_TODOS_API_URL must be an authority root, /v1, or <app>[/v1], not /api/v1 or another path; " + "local SQLite fallback is disabled");
45660
45766
  }
45661
45767
  const hostname2 = url.hostname.toLowerCase();
45662
45768
  const loopback = hostname2 === "localhost" || hostname2 === "::1" || hostname2 === "[::1]" || /^127(?:\.\d{1,3}){3}$/.test(hostname2);
45663
45769
  if (url.protocol === "http:" && !loopback) {
45664
45770
  throw new Error("REMOTE_API_URL_INVALID: plaintext HTTP is allowed only for loopback Todos authorities; local SQLite fallback is disabled");
45665
45771
  }
45666
- return url.origin;
45772
+ const rootPath = isV1Root || isAppV1Root ? path.slice(0, -"/v1".length) : path;
45773
+ return rootPath ? `${url.origin}${rootPath}` : url.origin;
45667
45774
  }
45668
45775
  function getTodosRemoteAuthorityConfigStatus(env = process.env) {
45669
45776
  let resolution;
@@ -46217,8 +46324,8 @@ var DANGEROUS_TOOLS = new Set([
46217
46324
  "merge_tasks",
46218
46325
  "cancel_agent_run"
46219
46326
  ]);
46220
- function resolveAccessProfile(envValue) {
46221
- const raw = (envValue ?? process.env["TODOS_PROFILE"] ?? "full").toLowerCase();
46327
+ function resolveAccessProfile(envValue2) {
46328
+ const raw = (envValue2 ?? process.env["TODOS_PROFILE"] ?? "full").toLowerCase();
46222
46329
  if (ACCESS_PROFILES.includes(raw))
46223
46330
  return raw;
46224
46331
  if (raw === "readonly")
@@ -47761,8 +47868,8 @@ init_task_crud();
47761
47868
  init_redaction();
47762
47869
  import { Database as Database3 } from "bun:sqlite";
47763
47870
  import { createHash as createHash17 } from "crypto";
47764
- import { existsSync as existsSync9, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
47765
- import { basename as basename2, dirname as dirname6, join as join9, resolve as resolve10 } from "path";
47871
+ import { existsSync as existsSync10, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
47872
+ import { basename as basename2, dirname as dirname6, join as join11, resolve as resolve11 } from "path";
47766
47873
 
47767
47874
  // src/lib/task-routing.ts
47768
47875
  init_database();
@@ -47770,7 +47877,7 @@ init_projects();
47770
47877
  init_task_crud();
47771
47878
  init_task_lifecycle();
47772
47879
  init_task_lists();
47773
- import { existsSync as existsSync8, statSync as statSync3 } from "fs";
47880
+ import { existsSync as existsSync9, statSync as statSync3 } from "fs";
47774
47881
  var DEFAULT_STALE_IN_PROGRESS_MS = 3 * 24 * 60 * 60 * 1000;
47775
47882
  var TERMINAL_WORKFLOW_STATES = new Set([
47776
47883
  "failed",
@@ -47825,7 +47932,7 @@ function routeConcurrencyKey(task3, project, taskList, projectPath) {
47825
47932
  }
47826
47933
  function directoryExists(path) {
47827
47934
  try {
47828
- return existsSync8(path) && statSync3(path).isDirectory();
47935
+ return existsSync9(path) && statSync3(path).isDirectory();
47829
47936
  } catch {
47830
47937
  return false;
47831
47938
  }
@@ -48026,7 +48133,7 @@ function pointerPatch(previous, input, key2) {
48026
48133
 
48027
48134
  // src/lib/task-route-sources.ts
48028
48135
  var TASK_ROUTE_SOURCE_DISCOVERY_SCHEMA_VERSION = "todos.task_route_sources.v1";
48029
- var TODO_STORE_RELATIVE_PATH = join9(".hasna", "todos", "todos.db");
48136
+ var TODO_STORE_RELATIVE_PATH = join11(".hasna", "todos", "todos.db");
48030
48137
  var ROOT_SCAN_MAX_DEPTH = 5;
48031
48138
  var SKIPPED_SCAN_DIRS = new Set([
48032
48139
  ".git",
@@ -48040,7 +48147,7 @@ var SKIPPED_SCAN_DIRS = new Set([
48040
48147
  ".cache"
48041
48148
  ]);
48042
48149
  function normalizePath3(input) {
48043
- return resolve10(input);
48150
+ return resolve11(input);
48044
48151
  }
48045
48152
  function sourceStoreId(sourceDbPath) {
48046
48153
  const digest3 = createHash17("sha256").update(sourceDbPath).digest("hex").slice(0, 16);
@@ -48102,8 +48209,8 @@ function discoverStoresUnderRoot(sourceRoot) {
48102
48209
  const rootPath = normalizePath3(sourceRoot);
48103
48210
  const errors2 = [];
48104
48211
  const stores = [];
48105
- if (!existsSync9(rootPath)) {
48106
- const ref = createStoreRef(join9(rootPath, TODO_STORE_RELATIVE_PATH));
48212
+ if (!existsSync10(rootPath)) {
48213
+ const ref = createStoreRef(join11(rootPath, TODO_STORE_RELATIVE_PATH));
48107
48214
  errors2.push({
48108
48215
  ...ref,
48109
48216
  code: "SOURCE_ROOT_MISSING",
@@ -48115,7 +48222,7 @@ function discoverStoresUnderRoot(sourceRoot) {
48115
48222
  try {
48116
48223
  rootStat = statSync4(rootPath);
48117
48224
  } catch (error) {
48118
- const ref = createStoreRef(join9(rootPath, TODO_STORE_RELATIVE_PATH));
48225
+ const ref = createStoreRef(join11(rootPath, TODO_STORE_RELATIVE_PATH));
48119
48226
  errors2.push({
48120
48227
  ...ref,
48121
48228
  code: "SOURCE_ROOT_UNREADABLE",
@@ -48128,8 +48235,8 @@ function discoverStoresUnderRoot(sourceRoot) {
48128
48235
  return { stores, errors: errors2 };
48129
48236
  }
48130
48237
  function scanDirectory(dir, depth) {
48131
- const candidate = join9(dir, TODO_STORE_RELATIVE_PATH);
48132
- if (existsSync9(candidate)) {
48238
+ const candidate = join11(dir, TODO_STORE_RELATIVE_PATH);
48239
+ if (existsSync10(candidate)) {
48133
48240
  stores.push(createStoreRef(candidate));
48134
48241
  }
48135
48242
  if (depth >= ROOT_SCAN_MAX_DEPTH)
@@ -48149,7 +48256,7 @@ function discoverStoresUnderRoot(sourceRoot) {
48149
48256
  for (const entry2 of entries) {
48150
48257
  if (!entry2.isDirectory() || SKIPPED_SCAN_DIRS.has(entry2.name))
48151
48258
  continue;
48152
- scanDirectory(join9(dir, entry2.name), depth + 1);
48259
+ scanDirectory(join11(dir, entry2.name), depth + 1);
48153
48260
  }
48154
48261
  }
48155
48262
  scanDirectory(rootPath, 0);
@@ -48175,7 +48282,7 @@ function collectStoreRefs(input) {
48175
48282
  };
48176
48283
  }
48177
48284
  function openReadonlyStore(ref) {
48178
- if (!existsSync9(ref.source_db_path)) {
48285
+ if (!existsSync10(ref.source_db_path)) {
48179
48286
  throw Object.assign(new Error(`Store does not exist: ${ref.source_db_path}`), { code: "STORE_MISSING" });
48180
48287
  }
48181
48288
  return new Database3(ref.source_db_path, { readonly: true, create: false });
@@ -48487,8 +48594,8 @@ init_plans();
48487
48594
  init_database();
48488
48595
  init_projects();
48489
48596
  init_tasks();
48490
- import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
48491
- import { join as join10, resolve as resolve11 } from "path";
48597
+ import { existsSync as existsSync11, mkdirSync as mkdirSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
48598
+ import { join as join12, resolve as resolve12 } from "path";
48492
48599
  var PLAN_MARKDOWN_SCHEMA = "hasna.todos.plan/v1";
48493
48600
  function assertSafePathSegment(value, label) {
48494
48601
  const trimmed = value.trim();
@@ -48532,7 +48639,7 @@ function resolvePlanArtifactProject(input) {
48532
48639
  const ref = input.project_id || input.project_ref;
48533
48640
  if (!ref)
48534
48641
  throw new Error("Plan artifacts require a project id or project reference");
48535
- const byPath = getProjectByPath(resolve11(ref), db);
48642
+ const byPath = getProjectByPath(resolve12(ref), db);
48536
48643
  if (byPath)
48537
48644
  return byPath;
48538
48645
  const resolvedId = resolvePartialId(db, "projects", ref);
@@ -48549,8 +48656,8 @@ function resolvePlanArtifactProject(input) {
48549
48656
  function resolvePlanArtifactPaths(input) {
48550
48657
  const project = resolvePlanArtifactProject(input);
48551
48658
  const projectId = assertSafePathSegment(project.id, "project id");
48552
- const projectRoot = resolve11(project.path);
48553
- const directory = join10(projectRoot, ".hasna", "todos", "plans", projectId);
48659
+ const projectRoot = resolve12(project.path);
48660
+ const directory = join12(projectRoot, ".hasna", "todos", "plans", projectId);
48554
48661
  const planId = input.plan_id ? assertSafePathSegment(input.plan_id, "plan id") : null;
48555
48662
  const planSlug = input.plan_slug ? assertSafePathSegment(slugify(input.plan_slug), "plan slug") : null;
48556
48663
  const fileName = planId ? planSlug ? `${planSlug}--${planId.slice(0, 8)}.md` : `${planId}.md` : null;
@@ -48558,7 +48665,7 @@ function resolvePlanArtifactPaths(input) {
48558
48665
  project_id: project.id,
48559
48666
  project_root: projectRoot,
48560
48667
  directory,
48561
- file_path: fileName ? join10(directory, fileName) : directory
48668
+ file_path: fileName ? join12(directory, fileName) : directory
48562
48669
  };
48563
48670
  }
48564
48671
  function resolvePlanArtifactCandidatePaths(plan, db) {
@@ -48724,7 +48831,7 @@ function readPlanArtifact(plan, db) {
48724
48831
  return null;
48725
48832
  const d = db || getDatabase();
48726
48833
  const paths = resolvePlanArtifactCandidatePaths(plan, d);
48727
- const path = existsSync10(paths.primary.file_path) ? paths.primary.file_path : existsSync10(paths.legacy.file_path) ? paths.legacy.file_path : null;
48834
+ const path = existsSync11(paths.primary.file_path) ? paths.primary.file_path : existsSync11(paths.legacy.file_path) ? paths.legacy.file_path : null;
48728
48835
  if (!path)
48729
48836
  return null;
48730
48837
  const markdown = readFileSync7(path, "utf8");
@@ -48739,7 +48846,7 @@ function inspectPlanArtifact(plan, db) {
48739
48846
  return null;
48740
48847
  const d = db || getDatabase();
48741
48848
  const paths = resolvePlanArtifactCandidatePaths(plan, d);
48742
- const path = existsSync10(paths.primary.file_path) ? paths.primary.file_path : existsSync10(paths.legacy.file_path) ? paths.legacy.file_path : null;
48849
+ const path = existsSync11(paths.primary.file_path) ? paths.primary.file_path : existsSync11(paths.legacy.file_path) ? paths.legacy.file_path : null;
48743
48850
  if (!path) {
48744
48851
  return {
48745
48852
  path: paths.primary.file_path,
@@ -49828,8 +49935,8 @@ function renderRetrospectiveMarkdown(record) {
49828
49935
  init_database();
49829
49936
  init_projects();
49830
49937
  init_task_lists();
49831
- import { existsSync as existsSync11, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
49832
- import { basename as basename3, dirname as dirname7, resolve as resolve12 } from "path";
49938
+ import { existsSync as existsSync12, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
49939
+ import { basename as basename3, dirname as dirname7, resolve as resolve13 } from "path";
49833
49940
  function safeStat(path) {
49834
49941
  try {
49835
49942
  return statSync5(path);
@@ -49838,7 +49945,7 @@ function safeStat(path) {
49838
49945
  }
49839
49946
  }
49840
49947
  function canonicalPath(input) {
49841
- const resolved = resolve12(input);
49948
+ const resolved = resolve13(input);
49842
49949
  const stats2 = safeStat(resolved);
49843
49950
  if (stats2?.isFile())
49844
49951
  return dirname7(resolved);
@@ -49847,7 +49954,7 @@ function canonicalPath(input) {
49847
49954
  function findUp(start, marker) {
49848
49955
  let current = canonicalPath(start);
49849
49956
  while (true) {
49850
- if (existsSync11(resolve12(current, marker)))
49957
+ if (existsSync12(resolve13(current, marker)))
49851
49958
  return current;
49852
49959
  const parent = dirname7(current);
49853
49960
  if (parent === current)
@@ -49858,8 +49965,8 @@ function findUp(start, marker) {
49858
49965
  function readPackageJson2(path) {
49859
49966
  if (!path)
49860
49967
  return null;
49861
- const file = resolve12(path, "package.json");
49862
- if (!existsSync11(file))
49968
+ const file = resolve13(path, "package.json");
49969
+ if (!existsSync12(file))
49863
49970
  return null;
49864
49971
  try {
49865
49972
  const parsed = JSON.parse(readFileSync8(file, "utf-8"));
@@ -49881,7 +49988,7 @@ function workspaceMarker(root, rootPackage) {
49881
49988
  if (rootPackage?.workspaces)
49882
49989
  markers.push("package.json#workspaces");
49883
49990
  for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
49884
- if (existsSync11(resolve12(root, marker)))
49991
+ if (existsSync12(resolve13(root, marker)))
49885
49992
  markers.push(marker);
49886
49993
  }
49887
49994
  const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
@@ -50219,18 +50326,18 @@ var gatherTrainingData = async (options = {}) => {
50219
50326
  };
50220
50327
  // src/lib/model-config.ts
50221
50328
  init_sync_utils();
50222
- import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
50223
- import { join as join11 } from "path";
50329
+ import { existsSync as existsSync13, mkdirSync as mkdirSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
50330
+ import { join as join13 } from "path";
50224
50331
  var DEFAULT_MODEL = "gpt-4o-mini";
50225
50332
  function getConfigDir() {
50226
50333
  return getTodosGlobalDir();
50227
50334
  }
50228
50335
  function getConfigPath2() {
50229
- return join11(getConfigDir(), "config.json");
50336
+ return join13(getConfigDir(), "config.json");
50230
50337
  }
50231
50338
  function readConfig() {
50232
50339
  const configPath = getConfigPath2();
50233
- if (!existsSync12(configPath))
50340
+ if (!existsSync13(configPath))
50234
50341
  return {};
50235
50342
  try {
50236
50343
  const raw = readFileSync9(configPath, "utf-8");
@@ -50241,7 +50348,7 @@ function readConfig() {
50241
50348
  }
50242
50349
  function writeConfig(config) {
50243
50350
  const configDir = getConfigDir();
50244
- if (!existsSync12(configDir)) {
50351
+ if (!existsSync13(configDir)) {
50245
50352
  mkdirSync9(configDir, { recursive: true });
50246
50353
  }
50247
50354
  writeFileSync7(getConfigPath2(), JSON.stringify(config, null, 2) + `
@@ -50989,7 +51096,7 @@ init_database();
50989
51096
  init_tasks();
50990
51097
  init_config2();
50991
51098
  init_redaction();
50992
- import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
51099
+ import { existsSync as existsSync14, readFileSync as readFileSync10 } from "fs";
50993
51100
  var DEFAULT_RETRY = {
50994
51101
  attempts: 1,
50995
51102
  backoff_ms: 0
@@ -51097,7 +51204,7 @@ function classifyLog(text) {
51097
51204
  async function sleep2(ms) {
51098
51205
  if (ms <= 0)
51099
51206
  return;
51100
- await new Promise((resolve13) => setTimeout(resolve13, ms));
51207
+ await new Promise((resolve14) => setTimeout(resolve14, ms));
51101
51208
  }
51102
51209
  async function runCommandProvider(provider, input) {
51103
51210
  const commandTemplate = input.command || provider.command;
@@ -51152,7 +51259,7 @@ Timed out after ${provider.timeout_ms}ms`);
51152
51259
  };
51153
51260
  }
51154
51261
  function runCiLogProvider(input) {
51155
- const text = input.log_text ?? (input.log_path && existsSync13(input.log_path) ? readFileSync10(input.log_path, "utf-8") : "");
51262
+ const text = input.log_text ?? (input.log_path && existsSync14(input.log_path) ? readFileSync10(input.log_path, "utf-8") : "");
51156
51263
  return {
51157
51264
  status: classifyLog(text),
51158
51265
  attempts: 1,
@@ -51164,7 +51271,7 @@ function runBrowserProvider(input) {
51164
51271
  if (!input.artifact_path) {
51165
51272
  return { status: "unknown", attempts: 1, exit_code: null, output_summary: "browser provider needs a screenshot or artifact path" };
51166
51273
  }
51167
- if (!existsSync13(input.artifact_path)) {
51274
+ if (!existsSync14(input.artifact_path)) {
51168
51275
  return { status: "failed", attempts: 1, exit_code: null, output_summary: `artifact not found: ${input.artifact_path}` };
51169
51276
  }
51170
51277
  return {
@@ -51458,9 +51565,9 @@ init_database();
51458
51565
  init_tasks();
51459
51566
  init_task_runs();
51460
51567
  init_config2();
51461
- import { relative as relative3, resolve as resolve13 } from "path";
51568
+ import { relative as relative3, resolve as resolve14 } from "path";
51462
51569
  function normalizePath4(path) {
51463
- return resolve13(path);
51570
+ return resolve14(path);
51464
51571
  }
51465
51572
  function unique4(values) {
51466
51573
  return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
@@ -51515,7 +51622,7 @@ function commandMatches(commands, pattern) {
51515
51622
  }
51516
51623
  function pathMatches(paths, pattern, root) {
51517
51624
  return paths.filter((path) => {
51518
- const candidate = path.startsWith("/") ? path : resolve13(root, path);
51625
+ const candidate = path.startsWith("/") ? path : resolve14(root, path);
51519
51626
  if (!isPathInside3(root, candidate))
51520
51627
  return matchesPattern4(path, pattern);
51521
51628
  return matchesPattern4(path, pattern) || matchesPattern4(relative3(root, candidate), pattern);
@@ -51810,20 +51917,20 @@ function resourceDiagnostics() {
51810
51917
  }
51811
51918
  // src/lib/sandbox-profiles.ts
51812
51919
  init_sync_utils();
51813
- import { existsSync as existsSync14, readFileSync as readFileSync11, writeFileSync as writeFileSync9, mkdirSync as mkdirSync11 } from "fs";
51814
- import { join as join12, dirname as dirname9 } from "path";
51920
+ import { existsSync as existsSync15, readFileSync as readFileSync11, writeFileSync as writeFileSync9, mkdirSync as mkdirSync11 } from "fs";
51921
+ import { join as join14, dirname as dirname9 } from "path";
51815
51922
  var SANDBOX_PROFILE_VERSION = "todos.sandbox-profile.v1";
51816
51923
  function getProfilesPath() {
51817
51924
  if (process.env["TODOS_SANDBOX_PROFILES_PATH"]) {
51818
51925
  return process.env["TODOS_SANDBOX_PROFILES_PATH"];
51819
51926
  }
51820
- const localDir = join12(process.cwd(), ".todos");
51821
- const local = join12(localDir, "sandbox-profiles.json");
51822
- if (existsSync14(localDir))
51927
+ const localDir = join14(process.cwd(), ".todos");
51928
+ const local = join14(localDir, "sandbox-profiles.json");
51929
+ if (existsSync15(localDir))
51823
51930
  return local;
51824
- if (existsSync14(local))
51931
+ if (existsSync15(local))
51825
51932
  return local;
51826
- return join12(getHomeDir(), ".hasna", "todos", "sandbox-profiles.json");
51933
+ return join14(getTodosGlobalDir(), "sandbox-profiles.json");
51827
51934
  }
51828
51935
  var cached2 = null;
51829
51936
  function resetSandboxProfileCache() {
@@ -51855,7 +51962,7 @@ function loadSandboxProfiles() {
51855
51962
  if (cached2)
51856
51963
  return cached2;
51857
51964
  const path = getProfilesPath();
51858
- if (!existsSync14(path)) {
51965
+ if (!existsSync15(path)) {
51859
51966
  cached2 = getDefaultSandboxProfiles();
51860
51967
  return cached2;
51861
51968
  }
@@ -52235,9 +52342,9 @@ init_task_commits();
52235
52342
 
52236
52343
  // src/lib/git-traceability.ts
52237
52344
  init_task_commits();
52238
- import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
52345
+ import { existsSync as existsSync16, readFileSync as readFileSync12 } from "fs";
52239
52346
  import { spawnSync as spawnSync2 } from "child_process";
52240
- import { resolve as resolve14 } from "path";
52347
+ import { resolve as resolve15 } from "path";
52241
52348
  var GIT_TRACEABILITY_SCHEMA_VERSION = "todos.git_traceability.v1";
52242
52349
  function runGit(args, cwd) {
52243
52350
  const result = spawnSync2("git", args, { cwd, encoding: "utf8" });
@@ -52280,8 +52387,8 @@ function inspectGitCommit(sha, cwd) {
52280
52387
  };
52281
52388
  }
52282
52389
  function loadCiSnapshot(path) {
52283
- const target = path ? resolve14(path) : resolve14(process.cwd(), ".todos", "ci-snapshot.json");
52284
- if (!existsSync15(target))
52390
+ const target = path ? resolve15(path) : resolve15(process.cwd(), ".todos", "ci-snapshot.json");
52391
+ if (!existsSync16(target))
52285
52392
  return null;
52286
52393
  try {
52287
52394
  const parsed = JSON.parse(readFileSync12(target, "utf8"));
@@ -52377,8 +52484,8 @@ function formatTraceabilityReport(report) {
52377
52484
  `);
52378
52485
  }
52379
52486
  // src/lib/mention-resolver.ts
52380
- import { existsSync as existsSync16, readdirSync as readdirSync3, readFileSync as readFileSync13, statSync as statSync6 } from "fs";
52381
- import { basename as basename4, isAbsolute, join as join13, relative as relative4, resolve as resolve15, sep as sep2 } from "path";
52487
+ import { existsSync as existsSync17, readdirSync as readdirSync3, readFileSync as readFileSync13, statSync as statSync6 } from "fs";
52488
+ import { basename as basename4, isAbsolute, join as join15, relative as relative4, resolve as resolve16, sep as sep2 } from "path";
52382
52489
  init_database();
52383
52490
  init_plans();
52384
52491
  init_task_runs();
@@ -52457,7 +52564,7 @@ function backlink(kind, key2, label, target = key2) {
52457
52564
  return { kind, key: key2, label, target };
52458
52565
  }
52459
52566
  function normalizeWorkspace(workspace) {
52460
- return resolve15(workspace || process.cwd());
52567
+ return resolve16(workspace || process.cwd());
52461
52568
  }
52462
52569
  function isInside(root, absolutePath) {
52463
52570
  const rel = relative4(root, absolutePath);
@@ -52525,14 +52632,14 @@ function resolveFile(parsed, workspace) {
52525
52632
  resolution.warnings.push("path is empty or escapes the workspace");
52526
52633
  return resolution;
52527
52634
  }
52528
- const absolutePath = resolve15(workspace, relPath);
52635
+ const absolutePath = resolve16(workspace, relPath);
52529
52636
  if (!isInside(workspace, absolutePath)) {
52530
52637
  resolution.path = relPath;
52531
52638
  resolution.warnings.push("path escapes the workspace");
52532
52639
  return resolution;
52533
52640
  }
52534
52641
  resolution.path = relPath;
52535
- if (!existsSync16(absolutePath)) {
52642
+ if (!existsSync17(absolutePath)) {
52536
52643
  resolution.warnings.push("file does not exist in the local workspace");
52537
52644
  return resolution;
52538
52645
  }
@@ -52565,7 +52672,7 @@ function walkSourceFiles(root, current = root, files = []) {
52565
52672
  if (SKIP_DIRS.has(entry2.name))
52566
52673
  continue;
52567
52674
  }
52568
- const absolutePath = join13(current, entry2.name);
52675
+ const absolutePath = join15(current, entry2.name);
52569
52676
  if (entry2.isDirectory()) {
52570
52677
  if (!SKIP_DIRS.has(entry2.name))
52571
52678
  walkSourceFiles(root, absolutePath, files);
@@ -53918,7 +54025,7 @@ function getAdapterDocsFingerprint() {
53918
54025
  // src/lib/inbox-intake.ts
53919
54026
  init_database();
53920
54027
  init_tasks();
53921
- import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
54028
+ import { existsSync as existsSync18, readFileSync as readFileSync14 } from "fs";
53922
54029
  import { basename as basename5 } from "path";
53923
54030
  import { createHash as createHash19 } from "crypto";
53924
54031
  init_secret_redaction();
@@ -53964,7 +54071,7 @@ function loadRawContent(input) {
53964
54071
  }
53965
54072
  }
53966
54073
  if (input.file_path) {
53967
- if (!existsSync17(input.file_path))
54074
+ if (!existsSync18(input.file_path))
53968
54075
  throw new Error(`File not found: ${input.file_path}`);
53969
54076
  const raw = readFileSync14(input.file_path, "utf8");
53970
54077
  const name = basename5(input.file_path).toLowerCase();
@@ -54585,7 +54692,7 @@ function formatNlIntakePreviewText(preview) {
54585
54692
  // src/lib/issue-importers.ts
54586
54693
  init_database();
54587
54694
  init_tasks();
54588
- import { existsSync as existsSync18, readFileSync as readFileSync15 } from "fs";
54695
+ import { existsSync as existsSync19, readFileSync as readFileSync15 } from "fs";
54589
54696
  var ISSUE_IMPORT_SCHEMA = "todos.issue_import.v1";
54590
54697
  var ISSUE_SOURCES = ["github", "linear", "jira", "auto"];
54591
54698
  var GITHUB_LABEL_PRIORITY = {
@@ -54804,7 +54911,7 @@ function parseIssueExport(data, source9 = "auto") {
54804
54911
  return normalized;
54805
54912
  }
54806
54913
  function loadIssueExportFromFile(path) {
54807
- if (!existsSync18(path))
54914
+ if (!existsSync19(path))
54808
54915
  throw new Error(`File not found: ${path}`);
54809
54916
  return JSON.parse(readFileSync15(path, "utf8"));
54810
54917
  }
@@ -54965,8 +55072,8 @@ todos import issues ./linear.json --source linear --dry-run
54965
55072
  init_sync_utils();
54966
55073
  init_database();
54967
55074
  init_secret_redaction();
54968
- import { existsSync as existsSync19, mkdirSync as mkdirSync12, writeFileSync as writeFileSync10 } from "fs";
54969
- import { join as join14, dirname as dirname10 } from "path";
55075
+ import { existsSync as existsSync20, mkdirSync as mkdirSync12, writeFileSync as writeFileSync10 } from "fs";
55076
+ import { join as join16, dirname as dirname10 } from "path";
54970
55077
  var RUN_RECORD_SCHEMA = "todos.run_record.v1";
54971
55078
  var RUN_RECORD_STATUSES = ["active", "completed", "failed", "archived"];
54972
55079
  function parseJsonArray3(raw, fallback = []) {
@@ -55159,7 +55266,7 @@ function buildRunReplayBundle(id, db) {
55159
55266
  }
55160
55267
  function exportRunReplay(id, outputPath, db) {
55161
55268
  const bundle = buildRunReplayBundle(id, db);
55162
- const path = outputPath ?? join14(process.cwd(), ".todos", "replays", `${id.slice(0, 8)}.json`);
55269
+ const path = outputPath ?? join16(process.cwd(), ".todos", "replays", `${id.slice(0, 8)}.json`);
55163
55270
  mkdirSync12(dirname10(path), { recursive: true });
55164
55271
  writeFileSync10(path, JSON.stringify(bundle, null, 2));
55165
55272
  const d = db || getDatabase();
@@ -55199,15 +55306,15 @@ function formatRunRecordMarkdown(record) {
55199
55306
  `;
55200
55307
  }
55201
55308
  function getDefaultReplayDir() {
55202
- const local = join14(process.cwd(), ".todos", "replays");
55203
- if (existsSync19(join14(process.cwd(), ".todos")))
55309
+ const local = join16(process.cwd(), ".todos", "replays");
55310
+ if (existsSync20(join16(process.cwd(), ".todos")))
55204
55311
  return local;
55205
- return join14(getHomeDir(), ".hasna", "todos", "replays");
55312
+ return join16(getTodosGlobalDir(), "replays");
55206
55313
  }
55207
55314
  // src/lib/release-checks.ts
55208
55315
  init_secret_redaction();
55209
- import { existsSync as existsSync20, readFileSync as readFileSync16, readdirSync as readdirSync4, statSync as statSync7 } from "fs";
55210
- import { join as join15, relative as relative5 } from "path";
55316
+ import { existsSync as existsSync21, readFileSync as readFileSync16, readdirSync as readdirSync4, statSync as statSync7 } from "fs";
55317
+ import { join as join17, relative as relative5 } from "path";
55211
55318
  var RELEASE_CHECK_SCHEMA = "todos.release_check.v1";
55212
55319
  var FORBIDDEN_DIST_PATTERNS = [
55213
55320
  {
@@ -55220,16 +55327,16 @@ var FORBIDDEN_DIST_PATTERNS = [
55220
55327
  ];
55221
55328
  var REQUIRED_BINS = ["todos", "todos-mcp", "todos-serve"];
55222
55329
  function readPackageJson3(root) {
55223
- const path = join15(root, "package.json");
55224
- if (!existsSync20(path))
55330
+ const path = join17(root, "package.json");
55331
+ if (!existsSync21(path))
55225
55332
  throw new Error(`package.json not found in ${root}`);
55226
55333
  return JSON.parse(readFileSync16(path, "utf8"));
55227
55334
  }
55228
55335
  function walkFiles(dir, acc = []) {
55229
- if (!existsSync20(dir))
55336
+ if (!existsSync21(dir))
55230
55337
  return acc;
55231
55338
  for (const entry2 of readdirSync4(dir)) {
55232
- const full = join15(dir, entry2);
55339
+ const full = join17(dir, entry2);
55233
55340
  const st = statSync7(full);
55234
55341
  if (st.isDirectory())
55235
55342
  walkFiles(full, acc);
@@ -55247,8 +55354,8 @@ function auditPackageContents(root) {
55247
55354
  checks.push({ id: "files_dist", severity: "error", message: "package.json files must include dist" });
55248
55355
  }
55249
55356
  for (const pattern of files) {
55250
- const target = join15(root, pattern);
55251
- if (!existsSync20(target)) {
55357
+ const target = join17(root, pattern);
55358
+ if (!existsSync21(target)) {
55252
55359
  checks.push({ id: `files_missing_${pattern}`, severity: "error", message: `Published file path missing: ${pattern}` });
55253
55360
  }
55254
55361
  }
@@ -55262,8 +55369,8 @@ function auditPackageContents(root) {
55262
55369
  checks.push({ id: `bin_${name}`, severity: "error", message: `Missing bin entry: ${name}` });
55263
55370
  continue;
55264
55371
  }
55265
- const binPath = join15(root, rel);
55266
- if (!existsSync20(binPath)) {
55372
+ const binPath = join17(root, rel);
55373
+ if (!existsSync21(binPath)) {
55267
55374
  checks.push({ id: `bin_path_${name}`, severity: "error", message: `Bin file missing: ${rel}` });
55268
55375
  } else {
55269
55376
  checks.push({ id: `bin_ok_${name}`, severity: "info", message: `Bin present: ${name} \u2192 ${rel}` });
@@ -55280,8 +55387,8 @@ function auditPackageContents(root) {
55280
55387
  }
55281
55388
  function scanDistArtifacts(root) {
55282
55389
  const checks = [];
55283
- const distDir = join15(root, "dist");
55284
- if (!existsSync20(distDir)) {
55390
+ const distDir = join17(root, "dist");
55391
+ if (!existsSync21(distDir)) {
55285
55392
  checks.push({ id: "dist_missing", severity: "error", message: "dist/ directory not found \u2014 run bun run build" });
55286
55393
  return checks;
55287
55394
  }
@@ -55591,15 +55698,15 @@ function renderReleaseNotesMarkdown(document) {
55591
55698
  // src/lib/db-backup.ts
55592
55699
  init_database();
55593
55700
  init_migrations();
55594
- import { existsSync as existsSync21, copyFileSync, mkdirSync as mkdirSync13, readFileSync as readFileSync17, renameSync, statSync as statSync8, writeFileSync as writeFileSync11, unlinkSync } from "fs";
55595
- import { dirname as dirname11, join as join16, resolve as resolve16 } from "path";
55701
+ import { existsSync as existsSync22, copyFileSync, mkdirSync as mkdirSync13, readFileSync as readFileSync17, renameSync, statSync as statSync8, writeFileSync as writeFileSync11, unlinkSync } from "fs";
55702
+ import { dirname as dirname11, join as join18, resolve as resolve17 } from "path";
55596
55703
  import { Database as Database4 } from "bun:sqlite";
55597
55704
  var DB_BACKUP_SCHEMA = "todos.db_backup.v1";
55598
55705
  function resolveDbPath(dbPath) {
55599
55706
  if (dbPath)
55600
- return resolve16(dbPath);
55707
+ return resolve17(dbPath);
55601
55708
  if (process.env["TODOS_DB_PATH"] && process.env["TODOS_DB_PATH"] !== ":memory:") {
55602
- return resolve16(process.env["TODOS_DB_PATH"]);
55709
+ return resolve17(process.env["TODOS_DB_PATH"]);
55603
55710
  }
55604
55711
  const db = getDatabase();
55605
55712
  const filename = db.filename;
@@ -55609,7 +55716,7 @@ function resolveDbPath(dbPath) {
55609
55716
  }
55610
55717
  function backupDatabase(outputPath, sourcePath) {
55611
55718
  const source9 = resolveDbPath(sourcePath);
55612
- if (!existsSync21(source9))
55719
+ if (!existsSync22(source9))
55613
55720
  throw new Error(`Database not found: ${source9}`);
55614
55721
  mkdirSync13(dirname11(outputPath), { recursive: true });
55615
55722
  closeDatabase();
@@ -55632,13 +55739,13 @@ function backupDatabase(outputPath, sourcePath) {
55632
55739
  };
55633
55740
  }
55634
55741
  function restoreDatabase(backupPath, targetPath) {
55635
- if (!existsSync21(backupPath))
55742
+ if (!existsSync22(backupPath))
55636
55743
  throw new Error(`Backup not found: ${backupPath}`);
55637
55744
  const integrity = checkDatabaseIntegrity(backupPath);
55638
55745
  if (!integrity.ok) {
55639
55746
  throw new Error(`Backup failed integrity check: ${integrity.errors.join("; ")}`);
55640
55747
  }
55641
- const target = targetPath ? resolve16(targetPath) : resolveDbPath();
55748
+ const target = targetPath ? resolve17(targetPath) : resolveDbPath();
55642
55749
  mkdirSync13(dirname11(target), { recursive: true });
55643
55750
  closeDatabase();
55644
55751
  const staging = `${target}.restore.tmp`;
@@ -55648,7 +55755,7 @@ function restoreDatabase(backupPath, targetPath) {
55648
55755
  copyFileSync(backupPath, staging);
55649
55756
  for (const sidecar of [`${target}-wal`, `${target}-shm`]) {
55650
55757
  try {
55651
- if (existsSync21(sidecar))
55758
+ if (existsSync22(sidecar))
55652
55759
  unlinkSync(sidecar);
55653
55760
  } catch {}
55654
55761
  }
@@ -55663,9 +55770,9 @@ function restoreDatabase(backupPath, targetPath) {
55663
55770
  };
55664
55771
  }
55665
55772
  function checkDatabaseIntegrity(dbPath) {
55666
- const path = dbPath ? resolve16(dbPath) : resolveDbPath();
55773
+ const path = dbPath ? resolve17(dbPath) : resolveDbPath();
55667
55774
  const errors2 = [];
55668
- if (!existsSync21(path)) {
55775
+ if (!existsSync22(path)) {
55669
55776
  return {
55670
55777
  schema_version: DB_BACKUP_SCHEMA,
55671
55778
  path,
@@ -55728,7 +55835,7 @@ function checkDatabaseIntegrity(dbPath) {
55728
55835
  };
55729
55836
  }
55730
55837
  function compactDatabase(dbPath) {
55731
- const path = dbPath ? resolve16(dbPath) : resolveDbPath();
55838
+ const path = dbPath ? resolve17(dbPath) : resolveDbPath();
55732
55839
  const before = statSync8(path).size;
55733
55840
  const db = new Database4(path);
55734
55841
  db.exec("VACUUM");
@@ -55738,7 +55845,7 @@ function compactDatabase(dbPath) {
55738
55845
  return { path, bytes_before: before, bytes_after: after };
55739
55846
  }
55740
55847
  function migrationDryRun(dbPath) {
55741
- const path = dbPath ? resolve16(dbPath) : resolveDbPath();
55848
+ const path = dbPath ? resolve17(dbPath) : resolveDbPath();
55742
55849
  const db = new Database4(path, { readonly: true });
55743
55850
  let current = 0;
55744
55851
  try {
@@ -55762,13 +55869,13 @@ function migrationDryRun(dbPath) {
55762
55869
  };
55763
55870
  }
55764
55871
  function defaultBackupPath(dbPath) {
55765
- const base = dbPath ? dirname11(resolve16(dbPath)) : dirname11(resolveDbPath());
55872
+ const base = dbPath ? dirname11(resolve17(dbPath)) : dirname11(resolveDbPath());
55766
55873
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
55767
- return join16(base, "backups", `todos-${stamp}.db`);
55874
+ return join18(base, "backups", `todos-${stamp}.db`);
55768
55875
  }
55769
55876
  function readBackupManifest(backupPath) {
55770
55877
  const manifestPath = `${backupPath}.json`;
55771
- if (!existsSync21(manifestPath))
55878
+ if (!existsSync22(manifestPath))
55772
55879
  return null;
55773
55880
  try {
55774
55881
  return JSON.parse(readFileSync17(manifestPath, "utf8"));
@@ -56289,15 +56396,15 @@ ${SCHEMA_ENTITIES.map((e) => `- **${e}**: \`${JSON_SCHEMAS[e].schema_version}\``
56289
56396
  }
56290
56397
  function exportSchemasToDirectory(dir) {
56291
56398
  const { mkdirSync: mkdirSync14, writeFileSync: writeFileSync12 } = __require("fs");
56292
- const { join: join17 } = __require("path");
56399
+ const { join: join19 } = __require("path");
56293
56400
  mkdirSync14(dir, { recursive: true });
56294
56401
  const written = [];
56295
56402
  for (const entity of SCHEMA_ENTITIES) {
56296
- const path = join17(dir, `${entity}.${JSON_SCHEMAS[entity].schema_version.replace(/\./g, "-")}.json`);
56403
+ const path = join19(dir, `${entity}.${JSON_SCHEMAS[entity].schema_version.replace(/\./g, "-")}.json`);
56297
56404
  writeFileSync12(path, JSON.stringify(JSON_SCHEMAS[entity], null, 2));
56298
56405
  written.push(path);
56299
56406
  }
56300
- const catalogPath = join17(dir, "catalog.json");
56407
+ const catalogPath = join19(dir, "catalog.json");
56301
56408
  writeFileSync12(catalogPath, JSON.stringify({
56302
56409
  catalog_version: JSON_SCHEMA_CATALOG_VERSION,
56303
56410
  semver: SCHEMA_SEMVER,
@@ -58832,7 +58939,7 @@ init_templates();
58832
58939
  init_database();
58833
58940
  init_templates();
58834
58941
  import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
58835
- import { join as join17 } from "path";
58942
+ import { join as join19 } from "path";
58836
58943
  var BUILTIN_TEMPLATE_LIBRARY_VERSION = "2026-05-21";
58837
58944
  var BUILTIN_TEMPLATE_LIBRARY_SOURCE = "bundled-local-template-library";
58838
58945
  var TEMPLATE_LIBRARY_SCHEMA = "todos.template_library.v1";
@@ -59108,7 +59215,7 @@ function writeBuiltinTemplateFiles(directory) {
59108
59215
  mkdirSync16(directory, { recursive: true });
59109
59216
  const files = [];
59110
59217
  for (const entry2 of exportBuiltinTemplateFiles()) {
59111
- const path = join17(directory, entry2.filename);
59218
+ const path = join19(directory, entry2.filename);
59112
59219
  writeFileSync14(path, `${JSON.stringify(entry2.template, null, 2)}
59113
59220
  `, "utf-8");
59114
59221
  files.push(path);
@@ -59400,9 +59507,9 @@ init_tasks();
59400
59507
  init_redaction();
59401
59508
  init_sync_utils();
59402
59509
  import { createHash as createHash20 } from "crypto";
59403
- import { existsSync as existsSync22, readFileSync as readFileSync20, statSync as statSync9 } from "fs";
59510
+ import { existsSync as existsSync23, readFileSync as readFileSync20, statSync as statSync9 } from "fs";
59404
59511
  import { hostname as hostname3, platform, arch } from "os";
59405
- import { dirname as dirname15, join as join18, resolve as resolve17 } from "path";
59512
+ import { dirname as dirname15, join as join20, resolve as resolve18 } from "path";
59406
59513
  import { tmpdir as tmpdir3 } from "os";
59407
59514
  var MANIFEST_FILES = ["package.json", "dashboard/package.json", "sdk/package.json"];
59408
59515
  var LOCKFILES = ["bun.lock", "bun.lockb", "package-lock.json", "npm-shrinkwrap.json"];
@@ -59424,8 +59531,8 @@ function sha2566(value) {
59424
59531
  return createHash20("sha256").update(value).digest("hex");
59425
59532
  }
59426
59533
  function fileRecord(root, relativePath) {
59427
- const path = join18(root, relativePath);
59428
- if (!existsSync22(path))
59534
+ const path = join20(root, relativePath);
59535
+ if (!existsSync23(path))
59429
59536
  return null;
59430
59537
  const stat = statSync9(path);
59431
59538
  if (!stat.isFile())
@@ -59437,7 +59544,7 @@ function manifestRecord(root, relativePath) {
59437
59544
  const base = fileRecord(root, relativePath);
59438
59545
  if (!base)
59439
59546
  return null;
59440
- const parsed = readJsonFile(join18(root, relativePath));
59547
+ const parsed = readJsonFile(join20(root, relativePath));
59441
59548
  if (!parsed)
59442
59549
  return { ...base, redacted: {} };
59443
59550
  const redacted = redactValue({
@@ -59532,15 +59639,15 @@ function commandEnv(env, includeValues) {
59532
59639
  function defaultSnapshotDir() {
59533
59640
  const dbPath = getDatabasePath();
59534
59641
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
59535
- return join18(tmpdir3(), "hasna-todos", "environment-snapshots");
59536
- return join18(dirname15(resolve17(dbPath)), "environment-snapshots");
59642
+ return join20(tmpdir3(), "hasna-todos", "environment-snapshots");
59643
+ return join20(dirname15(resolve18(dbPath)), "environment-snapshots");
59537
59644
  }
59538
59645
  function snapshotWithId(snapshot) {
59539
59646
  const digest3 = sha2566(JSON.stringify(snapshot)).slice(0, 24);
59540
59647
  return { id: `env_${digest3}`, ...snapshot };
59541
59648
  }
59542
59649
  function captureEnvironmentSnapshot(input = {}) {
59543
- const root = resolve17(input.root || process.cwd());
59650
+ const root = resolve18(input.root || process.cwd());
59544
59651
  const env = input.env || process.env;
59545
59652
  const warnings = [];
59546
59653
  const manifests = MANIFEST_FILES.map((file) => manifestRecord(root, file)).filter((file) => Boolean(file));
@@ -59580,13 +59687,13 @@ function captureEnvironmentSnapshot(input = {}) {
59580
59687
  });
59581
59688
  }
59582
59689
  function writeEnvironmentSnapshot(snapshot, outputPath) {
59583
- const path = outputPath ? resolve17(outputPath) : join18(defaultSnapshotDir(), `${snapshot.id}.json`);
59690
+ const path = outputPath ? resolve18(outputPath) : join20(defaultSnapshotDir(), `${snapshot.id}.json`);
59584
59691
  ensureDir(dirname15(path));
59585
59692
  writeJsonFile(path, snapshot);
59586
59693
  return path;
59587
59694
  }
59588
59695
  function readEnvironmentSnapshot(path) {
59589
- const snapshot = readJsonFile(resolve17(path));
59696
+ const snapshot = readJsonFile(resolve18(path));
59590
59697
  if (!snapshot || snapshot.schema_version !== 1 || typeof snapshot.id !== "string") {
59591
59698
  throw new Error(`Invalid environment snapshot: ${path}`);
59592
59699
  }
@@ -59675,7 +59782,7 @@ init_projects();
59675
59782
  init_plans();
59676
59783
  import { createHash as createHash21 } from "crypto";
59677
59784
  import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
59678
- import { dirname as dirname16, join as join19 } from "path";
59785
+ import { dirname as dirname16, join as join21 } from "path";
59679
59786
  var DECISION_RECORD_SCHEMA = "todos.decision_record.v1";
59680
59787
  var KNOWLEDGE_SNAPSHOT_SCHEMA = "todos.knowledge_snapshot.v1";
59681
59788
  var DECISION_STATUSES = ["proposed", "accepted", "deprecated", "superseded", "rejected"];
@@ -59905,7 +60012,7 @@ function exportDecisionRecord(id, outputPath, format = "markdown", db) {
59905
60012
  if (!record)
59906
60013
  throw new Error(`Decision record not found: ${id}`);
59907
60014
  const content = format === "markdown" ? formatDecisionRecordMarkdown(record) : JSON.stringify(record, null, 2);
59908
- const path = outputPath ?? join19(process.cwd(), ".todos", "decisions", `${record.short_ref}.${format === "markdown" ? "md" : "json"}`);
60015
+ const path = outputPath ?? join21(process.cwd(), ".todos", "decisions", `${record.short_ref}.${format === "markdown" ? "md" : "json"}`);
59909
60016
  mkdirSync18(dirname16(path), { recursive: true });
59910
60017
  writeFileSync16(path, content, "utf8");
59911
60018
  return { path, content };
@@ -60053,7 +60160,7 @@ function exportKnowledgeSnapshot(id, outputPath, format = "markdown", db) {
60053
60160
  throw new Error(`Knowledge snapshot not found: ${id}`);
60054
60161
  const content = format === "markdown" ? formatKnowledgeSnapshotMarkdown(record) : JSON.stringify(record, null, 2);
60055
60162
  const slug = record.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
60056
- const path = outputPath ?? join19(process.cwd(), ".todos", "knowledge", `${slug || record.id.slice(0, 8)}.${format === "markdown" ? "md" : "json"}`);
60163
+ const path = outputPath ?? join21(process.cwd(), ".todos", "knowledge", `${slug || record.id.slice(0, 8)}.${format === "markdown" ? "md" : "json"}`);
60057
60164
  mkdirSync18(dirname16(path), { recursive: true });
60058
60165
  writeFileSync16(path, content, "utf8");
60059
60166
  return { path, content };
@@ -60352,8 +60459,8 @@ todos report export --kind retrospective --days 14 --format markdown --out retro
60352
60459
  `;
60353
60460
  }
60354
60461
  // src/lib/command-aliases.ts
60355
- import { existsSync as existsSync23, readFileSync as readFileSync21, writeFileSync as writeFileSync18, mkdirSync as mkdirSync20 } from "fs";
60356
- import { join as join20 } from "path";
60462
+ import { existsSync as existsSync24, readFileSync as readFileSync21, writeFileSync as writeFileSync18, mkdirSync as mkdirSync20 } from "fs";
60463
+ import { join as join22 } from "path";
60357
60464
  var COMMAND_ALIASES_SCHEMA = "todos.command_aliases.v1";
60358
60465
  var RESERVED = new Set([...listTopLevelCommands(), "help", "version", "alias", "shortcuts"]);
60359
60466
  var BUILTIN_SHORTCUTS = [
@@ -60372,7 +60479,7 @@ var BUILTIN_SHORTCUTS = [
60372
60479
  { pattern: /^reports?$/, argv: ["report", "docs"], explain: "Report export documentation" }
60373
60480
  ];
60374
60481
  function aliasesPath(cwd = process.cwd()) {
60375
- return join20(cwd, ".todos", "aliases.json");
60482
+ return join22(cwd, ".todos", "aliases.json");
60376
60483
  }
60377
60484
  function emptyStore() {
60378
60485
  return { schema_version: COMMAND_ALIASES_SCHEMA, aliases: {}, updated_at: new Date(0).toISOString() };
@@ -60389,7 +60496,7 @@ function validateAliasName(name) {
60389
60496
  }
60390
60497
  function loadAliasStore(cwd) {
60391
60498
  const path = aliasesPath(cwd);
60392
- if (!existsSync23(path))
60499
+ if (!existsSync24(path))
60393
60500
  return emptyStore();
60394
60501
  const parsed = JSON.parse(readFileSync21(path, "utf8"));
60395
60502
  if (parsed.schema_version !== COMMAND_ALIASES_SCHEMA) {
@@ -60399,7 +60506,7 @@ function loadAliasStore(cwd) {
60399
60506
  }
60400
60507
  function saveAliasStore(store, cwd) {
60401
60508
  const path = aliasesPath(cwd);
60402
- mkdirSync20(join20(path, ".."), { recursive: true });
60509
+ mkdirSync20(join22(path, ".."), { recursive: true });
60403
60510
  store.updated_at = new Date().toISOString();
60404
60511
  writeFileSync18(path, JSON.stringify(store, null, 2), "utf8");
60405
60512
  }
@@ -61054,18 +61161,18 @@ function createBranchWorkPlan(input, db) {
61054
61161
  init_database();
61055
61162
  init_templates();
61056
61163
  init_plans();
61057
- import { existsSync as existsSync24, readFileSync as readFileSync22, writeFileSync as writeFileSync19, mkdirSync as mkdirSync21 } from "fs";
61058
- import { join as join21 } from "path";
61164
+ import { existsSync as existsSync25, readFileSync as readFileSync22, writeFileSync as writeFileSync19, mkdirSync as mkdirSync21 } from "fs";
61165
+ import { join as join23 } from "path";
61059
61166
  var USER_SCAFFOLD_SCHEMA = "todos.user_scaffold.v1";
61060
61167
  var SCAFFOLD_KINDS = ["task", "project", "plan", "checklist", "contract", "verification_policy"];
61061
61168
  function storeDir(cwd = process.cwd()) {
61062
- return join21(cwd, ".todos", "scaffolds");
61169
+ return join23(cwd, ".todos", "scaffolds");
61063
61170
  }
61064
61171
  function storePath(cwd) {
61065
- return join21(storeDir(cwd), "store.json");
61172
+ return join23(storeDir(cwd), "store.json");
61066
61173
  }
61067
61174
  function versionsDir(cwd) {
61068
- return join21(storeDir(cwd), "versions");
61175
+ return join23(storeDir(cwd), "versions");
61069
61176
  }
61070
61177
  function slugify4(name) {
61071
61178
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
@@ -61075,7 +61182,7 @@ function emptyStore2() {
61075
61182
  }
61076
61183
  function loadUserScaffoldStore(cwd) {
61077
61184
  const path = storePath(cwd);
61078
- if (!existsSync24(path))
61185
+ if (!existsSync25(path))
61079
61186
  return emptyStore2();
61080
61187
  const parsed = JSON.parse(readFileSync22(path, "utf8"));
61081
61188
  if (parsed.schema_version !== USER_SCAFFOLD_SCHEMA) {
@@ -61090,7 +61197,7 @@ function saveUserScaffoldStore(store, cwd) {
61090
61197
  }
61091
61198
  function snapshotVersion(scaffold, cwd) {
61092
61199
  mkdirSync21(versionsDir(cwd), { recursive: true });
61093
- const path = join21(versionsDir(cwd), `${scaffold.id}-v${scaffold.version}.json`);
61200
+ const path = join23(versionsDir(cwd), `${scaffold.id}-v${scaffold.version}.json`);
61094
61201
  writeFileSync19(path, JSON.stringify(scaffold, null, 2), "utf8");
61095
61202
  }
61096
61203
  function listUserScaffolds(kind, cwd) {
@@ -61337,7 +61444,7 @@ function listLinkedTemplates(db, cwd) {
61337
61444
  // src/lib/agent-workflow-demo.ts
61338
61445
  init_database();
61339
61446
  import { mkdtempSync } from "fs";
61340
- import { join as join22 } from "path";
61447
+ import { join as join24 } from "path";
61341
61448
  import { tmpdir as tmpdir4 } from "os";
61342
61449
  init_projects();
61343
61450
  init_task_lists();
@@ -61358,7 +61465,7 @@ function setupEphemeralDemoDb(options = {}) {
61358
61465
  if (options.db_path) {
61359
61466
  db_path = options.db_path;
61360
61467
  } else if (options.persist) {
61361
- db_path = join22(mkdtempSync(join22(tmpdir4(), "todos-demo-")), "todos.db");
61468
+ db_path = join24(mkdtempSync(join24(tmpdir4(), "todos-demo-")), "todos.db");
61362
61469
  } else {
61363
61470
  db_path = ":memory:";
61364
61471
  }
@@ -63695,16 +63802,16 @@ function runSearchView(idOrName, db) {
63695
63802
  init_tasks();
63696
63803
  init_config2();
63697
63804
  init_sync_utils();
63698
- import { existsSync as existsSync25, readFileSync as readFileSync23, readdirSync as readdirSync5, writeFileSync as writeFileSync20 } from "fs";
63699
- import { join as join23 } from "path";
63805
+ import { existsSync as existsSync26, readFileSync as readFileSync23, readdirSync as readdirSync5, writeFileSync as writeFileSync20 } from "fs";
63806
+ import { join as join25 } from "path";
63700
63807
  function getTaskListDir(taskListId) {
63701
- return join23(HOME, ".claude", "tasks", taskListId);
63808
+ return join25(HOME, ".claude", "tasks", taskListId);
63702
63809
  }
63703
63810
  function readClaudeTask(dir, filename) {
63704
- return readJsonFile(join23(dir, filename));
63811
+ return readJsonFile(join25(dir, filename));
63705
63812
  }
63706
63813
  function writeClaudeTask(dir, task3) {
63707
- writeJsonFile(join23(dir, `${task3.id}.json`), task3);
63814
+ writeJsonFile(join25(dir, `${task3.id}.json`), task3);
63708
63815
  }
63709
63816
  function toClaudeStatus(status3) {
63710
63817
  if (status3 === "pending" || status3 === "in_progress" || status3 === "completed") {
@@ -63716,14 +63823,14 @@ function toSqliteStatus(status3) {
63716
63823
  return status3;
63717
63824
  }
63718
63825
  function readPrefixCounter(dir) {
63719
- const path = join23(dir, ".prefix-counter");
63720
- if (!existsSync25(path))
63826
+ const path = join25(dir, ".prefix-counter");
63827
+ if (!existsSync26(path))
63721
63828
  return 0;
63722
63829
  const val = parseInt(readFileSync23(path, "utf-8").trim(), 10);
63723
63830
  return isNaN(val) ? 0 : val;
63724
63831
  }
63725
63832
  function writePrefixCounter(dir, value) {
63726
- writeFileSync20(join23(dir, ".prefix-counter"), String(value));
63833
+ writeFileSync20(join25(dir, ".prefix-counter"), String(value));
63727
63834
  }
63728
63835
  function formatPrefixedSubject(title, prefix, counter) {
63729
63836
  const padded = String(counter).padStart(5, "0");
@@ -63750,7 +63857,7 @@ function taskToClaudeTask(task3, claudeTaskId, existingMeta) {
63750
63857
  }
63751
63858
  function pushToClaudeTaskList(taskListId, projectId, options = {}) {
63752
63859
  const dir = getTaskListDir(taskListId);
63753
- if (!existsSync25(dir))
63860
+ if (!existsSync26(dir))
63754
63861
  ensureDir(dir);
63755
63862
  const filter = {};
63756
63863
  if (projectId)
@@ -63759,7 +63866,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
63759
63866
  const existingByTodosId = new Map;
63760
63867
  const files = listJsonFiles(dir);
63761
63868
  for (const f of files) {
63762
- const path = join23(dir, f);
63869
+ const path = join25(dir, f);
63763
63870
  const ct = readClaudeTask(dir, f);
63764
63871
  if (ct?.metadata?.["todos_id"]) {
63765
63872
  existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
@@ -63848,7 +63955,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
63848
63955
  }
63849
63956
  function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
63850
63957
  const dir = getTaskListDir(taskListId);
63851
- if (!existsSync25(dir)) {
63958
+ if (!existsSync26(dir)) {
63852
63959
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
63853
63960
  }
63854
63961
  const files = readdirSync5(dir).filter((f) => f.endsWith(".json"));
@@ -63868,7 +63975,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
63868
63975
  }
63869
63976
  for (const f of files) {
63870
63977
  try {
63871
- const filePath = join23(dir, f);
63978
+ const filePath = join25(dir, f);
63872
63979
  const ct = readClaudeTask(dir, f);
63873
63980
  if (!ct)
63874
63981
  continue;
@@ -63939,20 +64046,20 @@ function syncClaudeTaskList(taskListId, projectId, options = {}) {
63939
64046
  init_tasks();
63940
64047
  init_sync_utils();
63941
64048
  init_config2();
63942
- import { existsSync as existsSync26 } from "fs";
63943
- import { join as join24 } from "path";
64049
+ import { existsSync as existsSync27 } from "fs";
64050
+ import { join as join26 } from "path";
63944
64051
  function agentBaseDir(agent) {
63945
64052
  const key2 = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
63946
- return process.env[key2] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join24(getTodosGlobalDir(), "agents");
64053
+ return process.env[key2] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join26(getTodosGlobalDir(), "agents");
63947
64054
  }
63948
64055
  function getTaskListDir2(agent, taskListId) {
63949
- return join24(agentBaseDir(agent), agent, taskListId);
64056
+ return join26(agentBaseDir(agent), agent, taskListId);
63950
64057
  }
63951
64058
  function readAgentTask(dir, filename) {
63952
- return readJsonFile(join24(dir, filename));
64059
+ return readJsonFile(join26(dir, filename));
63953
64060
  }
63954
64061
  function writeAgentTask(dir, task3) {
63955
- writeJsonFile(join24(dir, `${task3.id}.json`), task3);
64062
+ writeJsonFile(join26(dir, `${task3.id}.json`), task3);
63956
64063
  }
63957
64064
  function taskToAgentTask(task3, externalId, existingMeta) {
63958
64065
  return withSyncFingerprint({
@@ -63977,7 +64084,7 @@ function metadataKey(agent) {
63977
64084
  }
63978
64085
  function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
63979
64086
  const dir = getTaskListDir2(agent, taskListId);
63980
- if (!existsSync26(dir))
64087
+ if (!existsSync27(dir))
63981
64088
  ensureDir(dir);
63982
64089
  const filter = {};
63983
64090
  if (projectId)
@@ -63986,7 +64093,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
63986
64093
  const existingByTodosId = new Map;
63987
64094
  const files = listJsonFiles(dir);
63988
64095
  for (const f of files) {
63989
- const path = join24(dir, f);
64096
+ const path = join26(dir, f);
63990
64097
  const at = readAgentTask(dir, f);
63991
64098
  if (at?.metadata?.["todos_id"]) {
63992
64099
  existingByTodosId.set(at.metadata["todos_id"], { task: at, mtimeMs: getFileMtimeMs(path) });
@@ -64062,7 +64169,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
64062
64169
  }
64063
64170
  function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
64064
64171
  const dir = getTaskListDir2(agent, taskListId);
64065
- if (!existsSync26(dir)) {
64172
+ if (!existsSync27(dir)) {
64066
64173
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
64067
64174
  }
64068
64175
  const files = listJsonFiles(dir);
@@ -64081,7 +64188,7 @@ function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
64081
64188
  }
64082
64189
  for (const f of files) {
64083
64190
  try {
64084
- const filePath = join24(dir, f);
64191
+ const filePath = join26(dir, f);
64085
64192
  const at = readAgentTask(dir, f);
64086
64193
  if (!at)
64087
64194
  continue;
@@ -64221,9 +64328,9 @@ function syncWithAgents(agents, taskListIdByAgent, projectId, direction = "both"
64221
64328
  // src/lib/extract.ts
64222
64329
  init_tasks();
64223
64330
  init_task_files();
64224
- import { existsSync as existsSync27, readFileSync as readFileSync24, statSync as statSync10 } from "fs";
64331
+ import { existsSync as existsSync28, readFileSync as readFileSync24, statSync as statSync10 } from "fs";
64225
64332
  import { createHash as createHash22 } from "crypto";
64226
- import { relative as relative6, resolve as resolve18, join as join25 } from "path";
64333
+ import { relative as relative6, resolve as resolve19, join as join27 } from "path";
64227
64334
  var EXTRACT_TAGS = ["TODO", "FIXME", "HACK", "XXX", "BUG", "NOTE"];
64228
64335
  var DEFAULT_EXTENSIONS = new Set([
64229
64336
  ".ts",
@@ -64293,9 +64400,9 @@ function normalizePathForMatch(value) {
64293
64400
  return value.replace(/\\/g, "/").replace(/^\.\//, "");
64294
64401
  }
64295
64402
  function readGitignorePatterns(basePath) {
64296
- const root = statSync10(basePath).isFile() ? resolve18(basePath, "..") : basePath;
64297
- const gitignorePath = join25(root, ".gitignore");
64298
- if (!existsSync27(gitignorePath))
64403
+ const root = statSync10(basePath).isFile() ? resolve19(basePath, "..") : basePath;
64404
+ const gitignorePath = join27(root, ".gitignore");
64405
+ if (!existsSync28(gitignorePath))
64299
64406
  return [];
64300
64407
  try {
64301
64408
  return readFileSync24(gitignorePath, "utf-8").split(`
@@ -64429,7 +64536,7 @@ function collectFiles(basePath, extensions, excludes, respectGitignore) {
64429
64536
  return files.sort();
64430
64537
  }
64431
64538
  function buildCodebaseIndex(options) {
64432
- const basePath = resolve18(options.path);
64539
+ const basePath = resolve19(options.path);
64433
64540
  const tags = options.patterns || [...EXTRACT_TAGS];
64434
64541
  const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
64435
64542
  const excludes = options.exclude || [];
@@ -64437,10 +64544,10 @@ function buildCodebaseIndex(options) {
64437
64544
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
64438
64545
  const indexed = [];
64439
64546
  for (const file of files) {
64440
- const fullPath = statSync10(basePath).isFile() ? basePath : join25(basePath, file);
64547
+ const fullPath = statSync10(basePath).isFile() ? basePath : join27(basePath, file);
64441
64548
  try {
64442
64549
  const source9 = readFileSync24(fullPath, "utf-8");
64443
- const relPath = statSync10(basePath).isFile() ? relative6(resolve18(basePath, ".."), fullPath) : file;
64550
+ const relPath = statSync10(basePath).isFile() ? relative6(resolve19(basePath, ".."), fullPath) : file;
64444
64551
  indexed.push({
64445
64552
  file: relPath,
64446
64553
  checksum: stableHash(source9).slice(0, 24),
@@ -64460,7 +64567,7 @@ function buildCodebaseIndex(options) {
64460
64567
  };
64461
64568
  }
64462
64569
  function extractTodos(options, db) {
64463
- const basePath = resolve18(options.path);
64570
+ const basePath = resolve19(options.path);
64464
64571
  const tags = options.patterns || [...EXTRACT_TAGS];
64465
64572
  const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
64466
64573
  const excludes = options.exclude || [];
@@ -64468,10 +64575,10 @@ function extractTodos(options, db) {
64468
64575
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
64469
64576
  const allComments = [];
64470
64577
  for (const file of files) {
64471
- const fullPath = statSync10(basePath).isFile() ? basePath : join25(basePath, file);
64578
+ const fullPath = statSync10(basePath).isFile() ? basePath : join27(basePath, file);
64472
64579
  try {
64473
64580
  const source9 = readFileSync24(fullPath, "utf-8");
64474
- const relPath = statSync10(basePath).isFile() ? relative6(resolve18(basePath, ".."), fullPath) : file;
64581
+ const relPath = statSync10(basePath).isFile() ? relative6(resolve19(basePath, ".."), fullPath) : file;
64475
64582
  const comments = extractFromSource(source9, relPath, tags);
64476
64583
  allComments.push(...comments);
64477
64584
  } catch {}
@@ -64565,7 +64672,7 @@ async function watchSourceTodos(options, onRun) {
64565
64672
  const interval = Math.max(100, options.interval_ms || 2000);
64566
64673
  const once = options.once !== false && (!options.max_runs || options.max_runs <= 1);
64567
64674
  const maxRuns = options.max_runs ?? (once ? 1 : Number.POSITIVE_INFINITY);
64568
- const root = resolve18(options.path);
64675
+ const root = resolve19(options.path);
64569
64676
  const runs = [];
64570
64677
  let previous = new Map;
64571
64678
  for (let runNumber = 1;runNumber <= maxRuns; runNumber++) {
@@ -65447,8 +65554,8 @@ function renderAgentReplaySimulationMarkdown(simulation) {
65447
65554
  // src/lib/local-extensions.ts
65448
65555
  init_config2();
65449
65556
  import { createHash as createHash24, createVerify } from "crypto";
65450
- import { existsSync as existsSync28, readdirSync as readdirSync6, readFileSync as readFileSync26, statSync as statSync11 } from "fs";
65451
- import { basename as basename6, join as join26, resolve as resolve19 } from "path";
65557
+ import { existsSync as existsSync29, readdirSync as readdirSync6, readFileSync as readFileSync26, statSync as statSync11 } from "fs";
65558
+ import { basename as basename6, join as join28, resolve as resolve20 } from "path";
65452
65559
  init_redaction();
65453
65560
  init_runner_sandbox();
65454
65561
  function isObject2(value) {
@@ -65729,11 +65836,11 @@ function verifyExtensionSignature(input) {
65729
65836
  return verifier.verify(input.public_key, decodeSignature(input.signature));
65730
65837
  }
65731
65838
  function inspectExtensionSource(source9) {
65732
- const resolved = resolve19(source9);
65733
- if (!existsSync28(resolved))
65839
+ const resolved = resolve20(source9);
65840
+ if (!existsSync29(resolved))
65734
65841
  throw new Error(`extension source not found: ${source9}`);
65735
65842
  const stat = statSync11(resolved);
65736
- const manifestPath = stat.isDirectory() ? [join26(resolved, "todos.extension.json"), join26(resolved, "extension.json")].find(existsSync28) : resolved;
65843
+ const manifestPath = stat.isDirectory() ? [join28(resolved, "todos.extension.json"), join28(resolved, "extension.json")].find(existsSync29) : resolved;
65737
65844
  if (!manifestPath)
65738
65845
  throw new Error(`extension directory ${source9} is missing todos.extension.json`);
65739
65846
  const raw = readFileSync26(manifestPath);
@@ -65827,26 +65934,26 @@ function testExtensionCompatibility(sourceOrManifest) {
65827
65934
  function projectExtensionSources(projectPath) {
65828
65935
  if (!projectPath)
65829
65936
  return [];
65830
- const root = resolve19(projectPath);
65937
+ const root = resolve20(projectPath);
65831
65938
  const candidates = [
65832
- join26(root, "todos.extension.json"),
65833
- join26(root, ".todos", "todos.extension.json")
65939
+ join28(root, "todos.extension.json"),
65940
+ join28(root, ".todos", "todos.extension.json")
65834
65941
  ];
65835
- const extensionDir = join26(root, ".todos", "extensions");
65836
- if (existsSync28(extensionDir)) {
65942
+ const extensionDir = join28(root, ".todos", "extensions");
65943
+ if (existsSync29(extensionDir)) {
65837
65944
  for (const entry2 of readdirSync6(extensionDir)) {
65838
65945
  if (entry2.startsWith("."))
65839
65946
  continue;
65840
- const full = join26(extensionDir, entry2);
65947
+ const full = join28(extensionDir, entry2);
65841
65948
  if (statSync11(full).isDirectory() || entry2.endsWith(".json"))
65842
65949
  candidates.push(full);
65843
65950
  }
65844
65951
  }
65845
- return candidates.filter(existsSync28);
65952
+ return candidates.filter(existsSync29);
65846
65953
  }
65847
65954
  function discoverLocalExtensions(options = {}) {
65848
65955
  const config = loadConfig();
65849
- const projectPath = options.project_path ? resolve19(options.project_path) : null;
65956
+ const projectPath = options.project_path ? resolve20(options.project_path) : null;
65850
65957
  const configuredSources = [
65851
65958
  ...config.extension_sources || [],
65852
65959
  ...projectPath ? config.project_overrides?.[projectPath]?.extension_sources || [] : []
@@ -65854,7 +65961,7 @@ function discoverLocalExtensions(options = {}) {
65854
65961
  const sources = Array.from(new Set([
65855
65962
  ...configuredSources,
65856
65963
  ...projectExtensionSources(projectPath || undefined)
65857
- ])).map((source9) => projectPath && !source9.startsWith("/") ? resolve19(projectPath, source9) : resolve19(source9));
65964
+ ])).map((source9) => projectPath && !source9.startsWith("/") ? resolve20(projectPath, source9) : resolve20(source9));
65858
65965
  const warnings = [];
65859
65966
  const discovered = [];
65860
65967
  for (const source9 of sources) {
@@ -66722,7 +66829,7 @@ init_redaction();
66722
66829
  // src/lib/retention-cleanup.ts
66723
66830
  init_artifact_store();
66724
66831
  init_database();
66725
- import { existsSync as existsSync29, unlinkSync as unlinkSync2 } from "fs";
66832
+ import { existsSync as existsSync30, unlinkSync as unlinkSync2 } from "fs";
66726
66833
  var RETENTION_CLEANUP_CONFIRMATION = "delete-local-retention-data";
66727
66834
  var ALL_SCOPES = ["comments", "runs", "verifications", "expired_artifacts"];
66728
66835
  var EMPTY_COUNTS = {
@@ -66933,7 +67040,7 @@ function applyRetentionCleanup(input, db) {
66933
67040
  for (const artifact of report.candidates.artifact_files) {
66934
67041
  try {
66935
67042
  const path = artifactStorePath(artifact.relative_path);
66936
- if (!existsSync29(path)) {
67043
+ if (!existsSync30(path)) {
66937
67044
  report.warnings.push(`stored artifact already missing: ${artifact.relative_path}`);
66938
67045
  continue;
66939
67046
  }
@@ -67174,8 +67281,8 @@ init_artifact_store();
67174
67281
 
67175
67282
  // src/lib/doctor.ts
67176
67283
  init_database();
67177
- import { chmodSync, copyFileSync as copyFileSync2, existsSync as existsSync30, mkdirSync as mkdirSync22, statSync as statSync12 } from "fs";
67178
- import { basename as basename7, dirname as dirname18, join as join27 } from "path";
67284
+ import { chmodSync, copyFileSync as copyFileSync2, existsSync as existsSync31, mkdirSync as mkdirSync22, statSync as statSync12 } from "fs";
67285
+ import { basename as basename7, dirname as dirname18, join as join29 } from "path";
67179
67286
  init_migrations();
67180
67287
  init_schema();
67181
67288
  init_recurrence();
@@ -67285,7 +67392,7 @@ function findMissingProjectRoots(db) {
67285
67392
  continue;
67286
67393
  if (!row.path.startsWith("/"))
67287
67394
  continue;
67288
- if (!existsSync30(row.path))
67395
+ if (!existsSync31(row.path))
67289
67396
  missing++;
67290
67397
  }
67291
67398
  return missing;
@@ -67345,16 +67452,16 @@ function databasePermissionsAreUnsafe(dbPath) {
67345
67452
  function createBackup(dbPath) {
67346
67453
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
67347
67454
  return;
67348
- if (!existsSync30(dbPath))
67455
+ if (!existsSync31(dbPath))
67349
67456
  return;
67350
67457
  const stamp = now().replace(/[:.]/g, "-");
67351
- const backupDir = join27(dirname18(dbPath), `${basename7(dbPath)}.backup-${stamp}`);
67458
+ const backupDir = join29(dirname18(dbPath), `${basename7(dbPath)}.backup-${stamp}`);
67352
67459
  const files = [];
67353
67460
  mkdirSync22(backupDir, { recursive: true });
67354
67461
  for (const source9 of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
67355
- if (!existsSync30(source9))
67462
+ if (!existsSync31(source9))
67356
67463
  continue;
67357
- const target = join27(backupDir, basename7(source9));
67464
+ const target = join29(backupDir, basename7(source9));
67358
67465
  copyFileSync2(source9, target);
67359
67466
  files.push(target);
67360
67467
  }