@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/cli/index.js CHANGED
@@ -2123,7 +2123,7 @@ var package_default;
2123
2123
  var init_package = __esm(() => {
2124
2124
  package_default = {
2125
2125
  name: "@hasna/todos",
2126
- version: "0.15.50",
2126
+ version: "0.15.52",
2127
2127
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
2128
2128
  type: "module",
2129
2129
  main: "dist/index.js",
@@ -2182,6 +2182,7 @@ var init_package = __esm(() => {
2182
2182
  files: [
2183
2183
  "dist",
2184
2184
  "dashboard/dist",
2185
+ "postinstall.js",
2185
2186
  "LICENSE",
2186
2187
  "README.md"
2187
2188
  ],
@@ -2208,7 +2209,7 @@ var init_package = __esm(() => {
2208
2209
  "test:attested-container-compatibility-vector": "bun test scripts/attested-container-compatibility-vector.test.ts",
2209
2210
  "issue:release-review": "bun run scripts/issue-npm-release-agent-review.ts",
2210
2211
  prepublishOnly: "bun run scripts/verify-public-release.ts --mode=publish",
2211
- postinstall: "mkdir -p $HOME/.hasna/todos $HOME/.hasna/todos/training 2>/dev/null || true"
2212
+ postinstall: "node postinstall.js"
2212
2213
  },
2213
2214
  keywords: [
2214
2215
  "todos",
@@ -2242,13 +2243,15 @@ var init_package = __esm(() => {
2242
2243
  author: "Andrei Hasna <andrei@hasna.com>",
2243
2244
  license: "Apache-2.0",
2244
2245
  dependencies: {
2245
- "@hasna/contracts": "0.14.0",
2246
+ "@hasna/contracts": "0.14.2",
2246
2247
  "@hasna/events": "^0.1.11",
2248
+ "@hasna/paths": "0.1.0",
2247
2249
  "@modelcontextprotocol/sdk": "^1.12.1",
2248
2250
  chalk: "^5.4.1",
2249
2251
  commander: "^13.1.0",
2250
2252
  ink: "^5.2.0",
2251
2253
  react: "^18.3.1",
2254
+ "signal-exit": "3.0.7",
2252
2255
  zod: "3.25.76"
2253
2256
  },
2254
2257
  overrides: {
@@ -2257,7 +2260,7 @@ var init_package = __esm(() => {
2257
2260
  zod: "3.25.76"
2258
2261
  },
2259
2262
  devDependencies: {
2260
- "@types/bun": "^1.2.4",
2263
+ "@types/bun": "1.3.14",
2261
2264
  "@types/react": "^18.3.18",
2262
2265
  "bun-types": "1.3.9",
2263
2266
  "hasna-deployment-contracts": "npm:@hasna/contracts@0.10.4",
@@ -2491,6 +2494,101 @@ var init_types = __esm(() => {
2491
2494
  };
2492
2495
  });
2493
2496
 
2497
+ // node_modules/.bun/@hasna+paths@0.1.0/node_modules/@hasna/paths/dist/index.js
2498
+ import { homedir } from "os";
2499
+ import { join } from "path";
2500
+ function assertApp(app) {
2501
+ if (typeof app !== "string" || app.length === 0) {
2502
+ throw new TypeError("paths: app must be a non-empty string");
2503
+ }
2504
+ if (!APP_SLUG_RE.test(app)) {
2505
+ throw new TypeError(`paths: invalid app slug "${app}" \u2014 expected lowercase kebab-case ([a-z0-9]+(-[a-z0-9]+)*)`);
2506
+ }
2507
+ }
2508
+ function envOf(options) {
2509
+ return options.env ?? process.env;
2510
+ }
2511
+ function envValue(options, kind) {
2512
+ const value = envOf(options)[KIND_ENV[kind]];
2513
+ return typeof value === "string" && value.length > 0 ? value : undefined;
2514
+ }
2515
+ function isMacOS(platform) {
2516
+ return platform === "darwin";
2517
+ }
2518
+ function baseDir(kind, options) {
2519
+ const override = envValue(options, kind);
2520
+ if (override)
2521
+ return override;
2522
+ const home = options.home ?? homedir();
2523
+ const platform = options.platform ?? process.platform;
2524
+ if (isMacOS(platform)) {
2525
+ switch (kind) {
2526
+ case "config":
2527
+ case "data":
2528
+ return join(home, "Library", "Application Support", "Hasna");
2529
+ case "cache":
2530
+ return join(home, "Library", "Caches", "Hasna");
2531
+ case "state":
2532
+ return join(home, "Library", "Logs", "Hasna");
2533
+ }
2534
+ }
2535
+ switch (kind) {
2536
+ case "config":
2537
+ return join(home, ".config", "hasna");
2538
+ case "data":
2539
+ return join(home, ".local", "share", "hasna");
2540
+ case "state":
2541
+ return join(home, ".local", "state", "hasna");
2542
+ case "cache":
2543
+ return join(home, ".cache", "hasna");
2544
+ }
2545
+ }
2546
+ function resolvePath(kind, options) {
2547
+ assertApp(options.app);
2548
+ const appSegment = options.internal === true ? join("internal", options.app) : options.app;
2549
+ return join(baseDir(kind, options), appSegment);
2550
+ }
2551
+ function dataDir(options) {
2552
+ return resolvePath("data", options);
2553
+ }
2554
+ var KIND_ENV, APP_SLUG_RE;
2555
+ var init_dist = __esm(() => {
2556
+ KIND_ENV = {
2557
+ config: "HASNA_CONFIG_HOME",
2558
+ data: "HASNA_DATA_HOME",
2559
+ state: "HASNA_STATE_HOME",
2560
+ cache: "HASNA_CACHE_HOME"
2561
+ };
2562
+ APP_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
2563
+ });
2564
+
2565
+ // src/lib/paths.ts
2566
+ import { existsSync } from "fs";
2567
+ import { homedir as homedir2 } from "os";
2568
+ import { join as join2, resolve } from "path";
2569
+ function effectiveHome(env = process.env) {
2570
+ return env.HOME || env.USERPROFILE || homedir2();
2571
+ }
2572
+ function legacyHomeDir(env = process.env) {
2573
+ return join2(effectiveHome(env), ".hasna", "todos");
2574
+ }
2575
+ function resolverHome(env = process.env) {
2576
+ return dataDir({ app: "todos", home: effectiveHome(env), env });
2577
+ }
2578
+ function adoptResolverHome(resolved, env = process.env) {
2579
+ const dataOverride = env.HASNA_DATA_HOME;
2580
+ if (typeof dataOverride === "string" && dataOverride.trim().length > 0)
2581
+ return true;
2582
+ return existsSync(join2(resolved, "todos.db")) || existsSync(join2(resolved, "config.json"));
2583
+ }
2584
+ function getTodosDir(env = process.env) {
2585
+ const resolved = resolverHome(env);
2586
+ return resolve(adoptResolverHome(resolved, env) ? resolved : legacyHomeDir(env));
2587
+ }
2588
+ var init_paths = __esm(() => {
2589
+ init_dist();
2590
+ });
2591
+
2494
2592
  // src/lib/sync-utils.ts
2495
2593
  var exports_sync_utils = {};
2496
2594
  __export(exports_sync_utils, {
@@ -2510,22 +2608,22 @@ __export(exports_sync_utils, {
2510
2608
  TODO_SYNC_FINGERPRINT_KEY: () => TODO_SYNC_FINGERPRINT_KEY,
2511
2609
  HOME: () => HOME
2512
2610
  });
2513
- import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
2611
+ import { existsSync as existsSync2, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
2514
2612
  import { createHash } from "crypto";
2515
- import { homedir } from "os";
2516
- import { join } from "path";
2613
+ import { homedir as homedir3 } from "os";
2614
+ import { join as join3 } from "path";
2517
2615
  function getHomeDir() {
2518
- return process.env["HOME"] || process.env["USERPROFILE"] || homedir();
2616
+ return process.env["HOME"] || process.env["USERPROFILE"] || homedir3();
2519
2617
  }
2520
2618
  function getTodosGlobalDir() {
2521
- return join(getHomeDir(), ".hasna", "todos");
2619
+ return getTodosDir();
2522
2620
  }
2523
2621
  function ensureDir(dir) {
2524
- if (!existsSync(dir))
2622
+ if (!existsSync2(dir))
2525
2623
  mkdirSync(dir, { recursive: true });
2526
2624
  }
2527
2625
  function listJsonFiles(dir) {
2528
- if (!existsSync(dir))
2626
+ if (!existsSync2(dir))
2529
2627
  return [];
2530
2628
  return readdirSync(dir).filter((f) => f.endsWith(".json"));
2531
2629
  }
@@ -2541,14 +2639,14 @@ function writeJsonFile(path, data) {
2541
2639
  `);
2542
2640
  }
2543
2641
  function readHighWaterMark(dir) {
2544
- const path = join(dir, ".highwatermark");
2545
- if (!existsSync(path))
2642
+ const path = join3(dir, ".highwatermark");
2643
+ if (!existsSync2(path))
2546
2644
  return 1;
2547
2645
  const val = parseInt(readFileSync(path, "utf-8").trim(), 10);
2548
2646
  return isNaN(val) ? 1 : val;
2549
2647
  }
2550
2648
  function writeHighWaterMark(dir, value) {
2551
- writeFileSync(join(dir, ".highwatermark"), String(value));
2649
+ writeFileSync(join3(dir, ".highwatermark"), String(value));
2552
2650
  }
2553
2651
  function getFileMtimeMs(path) {
2554
2652
  try {
@@ -2603,6 +2701,7 @@ function hasSyncFingerprintChanged(record) {
2603
2701
  }
2604
2702
  var TODO_SYNC_FINGERPRINT_KEY = "todos_sync_fingerprint", HOME;
2605
2703
  var init_sync_utils = __esm(() => {
2704
+ init_paths();
2606
2705
  HOME = getHomeDir();
2607
2706
  });
2608
2707
 
@@ -2624,10 +2723,10 @@ __export(exports_config, {
2624
2723
  getAgentTaskListId: () => getAgentTaskListId,
2625
2724
  getAgentPoolForProject: () => getAgentPoolForProject
2626
2725
  });
2627
- import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
2628
- import { dirname, join as join2 } from "path";
2726
+ import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
2727
+ import { dirname, join as join4 } from "path";
2629
2728
  function getConfigPath() {
2630
- return join2(getTodosGlobalDir(), "config.json");
2729
+ return join4(getTodosGlobalDir(), "config.json");
2631
2730
  }
2632
2731
  function resetConfig() {
2633
2732
  cached = null;
@@ -2638,7 +2737,7 @@ function normalizeAgent(agent) {
2638
2737
  function loadConfig() {
2639
2738
  if (cached)
2640
2739
  return cached;
2641
- if (!existsSync2(getConfigPath())) {
2740
+ if (!existsSync3(getConfigPath())) {
2642
2741
  cached = {};
2643
2742
  return cached;
2644
2743
  }
@@ -2661,7 +2760,7 @@ function updateConfig(patch) {
2661
2760
  }
2662
2761
  function getTodosAiConfig() {
2663
2762
  const configPath = getConfigPath();
2664
- if (!existsSync2(configPath))
2763
+ if (!existsSync3(configPath))
2665
2764
  return {};
2666
2765
  const parsed = JSON.parse(readFileSync2(configPath, "utf8"));
2667
2766
  if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
@@ -5721,7 +5820,7 @@ var init_page_validation = __esm(() => {
5721
5820
  // src/cli/cloud-router.ts
5722
5821
  import { resolveStorageClient } from "@hasna/contracts/client/storage";
5723
5822
  import { randomUUID } from "crypto";
5724
- import { resolve as resolvePath } from "path";
5823
+ import { resolve as resolvePath2 } from "path";
5725
5824
  function emitTodosLocalFallbackNotice(env) {
5726
5825
  if (todosLocalFallbackNoticeEmitted)
5727
5826
  return;
@@ -5781,15 +5880,23 @@ function normalizeRemoteAuthorityUrl(value) {
5781
5880
  if (url.search || url.hash) {
5782
5881
  throw new Error("REMOTE_API_URL_INVALID: HASNA_TODOS_API_URL must not contain a query or fragment; local SQLite fallback is disabled");
5783
5882
  }
5784
- if (url.pathname !== "/" && url.pathname !== "/v1" && url.pathname !== "/v1/") {
5785
- 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");
5883
+ const path = url.pathname.replace(/\/+$/, "");
5884
+ const segments = path.split("/").filter(Boolean);
5885
+ const reservedGatewaySegments = new Set(["api", "v1"]);
5886
+ const isRoot = path === "";
5887
+ const isV1Root = segments.length === 1 && segments[0] === "v1";
5888
+ const isAppRoot = segments.length === 1 && !reservedGatewaySegments.has(segments[0].toLowerCase());
5889
+ const isAppV1Root = segments.length === 2 && segments[1] === "v1" && !reservedGatewaySegments.has(segments[0].toLowerCase());
5890
+ if (!isRoot && !isV1Root && !isAppRoot && !isAppV1Root) {
5891
+ 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");
5786
5892
  }
5787
5893
  const hostname = url.hostname.toLowerCase();
5788
5894
  const loopback = hostname === "localhost" || hostname === "::1" || hostname === "[::1]" || /^127(?:\.\d{1,3}){3}$/.test(hostname);
5789
5895
  if (url.protocol === "http:" && !loopback) {
5790
5896
  throw new Error("REMOTE_API_URL_INVALID: plaintext HTTP is allowed only for loopback Todos authorities; local SQLite fallback is disabled");
5791
5897
  }
5792
- return url.origin;
5898
+ const rootPath = isV1Root || isAppV1Root ? path.slice(0, -"/v1".length) : path;
5899
+ return rootPath ? `${url.origin}${rootPath}` : url.origin;
5793
5900
  }
5794
5901
  function getTodosRemoteAuthorityConfigStatus(env = process.env) {
5795
5902
  let resolution;
@@ -6707,11 +6814,11 @@ function resolveCloudProjectRef(projects, ref) {
6707
6814
  const input = ref.trim();
6708
6815
  const normalizedRef = input.toLowerCase();
6709
6816
  const pathLike = input.startsWith(".") || input.includes("/") || input.includes("\\");
6710
- const normalizedPath = pathLike ? resolvePath(input) : undefined;
6817
+ const normalizedPath = pathLike ? resolvePath2(input) : undefined;
6711
6818
  const slug = cloudProjectSlug(pathLike ? cloudProjectPathBasename(input) : input);
6712
6819
  const matchGroups = [
6713
6820
  uniqueProjectMatches(projects, (project) => project.id.toLowerCase() === normalizedRef),
6714
- uniqueProjectMatches(projects, (project) => project.path === input || normalizedPath !== undefined && resolvePath(project.path) === normalizedPath),
6821
+ uniqueProjectMatches(projects, (project) => project.path === input || normalizedPath !== undefined && resolvePath2(project.path) === normalizedPath),
6715
6822
  uniqueProjectMatches(projects, (project) => project.name.toLowerCase() === normalizedRef),
6716
6823
  uniqueProjectMatches(projects, (project) => project.task_list_id === input || cloudProjectSlug(project.name) === slug || cloudProjectSlug(cloudProjectPathBasename(project.path)) === slug),
6717
6824
  uniqueProjectMatches(projects, (project) => project.id.toLowerCase().startsWith(normalizedRef))
@@ -11718,9 +11825,9 @@ var init_schema = __esm(() => {
11718
11825
  });
11719
11826
 
11720
11827
  // src/db/machines.ts
11721
- import { existsSync as existsSync3 } from "fs";
11828
+ import { existsSync as existsSync4 } from "fs";
11722
11829
  import { hostname as osHostname, platform as osPlatform, arch as osArch } from "os";
11723
- import { resolve } from "path";
11830
+ import { resolve as resolve2 } from "path";
11724
11831
  import { spawnSync } from "child_process";
11725
11832
  function parseMetadata(value) {
11726
11833
  if (!value)
@@ -11751,7 +11858,7 @@ function discoverGitRoot(workspacePath) {
11751
11858
  }
11752
11859
  function topologyMetadata(input, existing = {}) {
11753
11860
  const next = { ...existing };
11754
- const workspacePath = input.workspace_path ? resolve(input.workspace_path) : undefined;
11861
+ const workspacePath = input.workspace_path ? resolve2(input.workspace_path) : undefined;
11755
11862
  const entries = {
11756
11863
  tailscale_name: input.tailscale_name,
11757
11864
  tailscale_ip: input.tailscale_ip,
@@ -11925,7 +12032,7 @@ function getMachineTopologyDiagnostics(opts = {}, db, at = new Date) {
11925
12032
  message: `${project.name} has ${distinctPaths.length} different machine-local paths`
11926
12033
  });
11927
12034
  }
11928
- if (localRow && !existsSync3(localRow.path)) {
12035
+ if (localRow && !existsSync4(localRow.path)) {
11929
12036
  pathIssues.push({
11930
12037
  type: "path_missing",
11931
12038
  project_id: project.id,
@@ -11936,7 +12043,7 @@ function getMachineTopologyDiagnostics(opts = {}, db, at = new Date) {
11936
12043
  message: `Local path does not exist on this machine: ${localRow.path}`
11937
12044
  });
11938
12045
  }
11939
- if (!localRow && project.path && machineById.has(localMachine.id) && !existsSync3(project.path)) {
12046
+ if (!localRow && project.path && machineById.has(localMachine.id) && !existsSync4(project.path)) {
11940
12047
  pathIssues.push({
11941
12048
  type: "path_missing",
11942
12049
  project_id: project.id,
@@ -12525,18 +12632,18 @@ __export(exports_database, {
12525
12632
  LOCK_EXPIRY_MINUTES: () => LOCK_EXPIRY_MINUTES
12526
12633
  });
12527
12634
  import { Database } from "bun:sqlite";
12528
- import { existsSync as existsSync4, mkdirSync as mkdirSync2 } from "fs";
12529
- import { dirname as dirname2, join as join3, resolve as resolve2 } from "path";
12635
+ import { existsSync as existsSync5, mkdirSync as mkdirSync2 } from "fs";
12636
+ import { dirname as dirname2, join as join5, resolve as resolve3 } from "path";
12530
12637
  function isInMemoryDb(path) {
12531
12638
  return path === ":memory:" || path.startsWith("file::memory:");
12532
12639
  }
12533
12640
  function findNearestProjectDb(startDir) {
12534
12641
  const gitRoot = findGitRoot(startDir);
12535
- const stopAt = gitRoot ? resolve2(gitRoot) : resolve2(startDir);
12536
- let dir = resolve2(startDir);
12642
+ const stopAt = gitRoot ? resolve3(gitRoot) : resolve3(startDir);
12643
+ let dir = resolve3(startDir);
12537
12644
  while (true) {
12538
- const candidate = join3(dir, ".hasna", "todos", "todos.db");
12539
- if (existsSync4(candidate))
12645
+ const candidate = join5(dir, ".hasna", "todos", "todos.db");
12646
+ if (existsSync5(candidate))
12540
12647
  return candidate;
12541
12648
  if (dir === stopAt)
12542
12649
  break;
@@ -12548,9 +12655,9 @@ function findNearestProjectDb(startDir) {
12548
12655
  return null;
12549
12656
  }
12550
12657
  function findGitRoot(startDir) {
12551
- let dir = resolve2(startDir);
12658
+ let dir = resolve3(startDir);
12552
12659
  while (true) {
12553
- if (existsSync4(join3(dir, ".git")))
12660
+ if (existsSync5(join5(dir, ".git")))
12554
12661
  return dir;
12555
12662
  const parent = dirname2(dir);
12556
12663
  if (parent === dir)
@@ -12560,7 +12667,7 @@ function findGitRoot(startDir) {
12560
12667
  return null;
12561
12668
  }
12562
12669
  function getGlobalDbPath() {
12563
- return join3(getHomeDir(), ".hasna", "todos", "todos.db");
12670
+ return join5(getTodosGlobalDir(), "todos.db");
12564
12671
  }
12565
12672
  function hasExplicitProjectArg(args = process.argv.slice(2)) {
12566
12673
  return args.some((arg) => arg === "--project" || arg.startsWith("--project="));
@@ -12598,7 +12705,7 @@ function getDbPath() {
12598
12705
  if (process.env["TODOS_DB_SCOPE"] === "project") {
12599
12706
  const gitRoot = findGitRoot(cwd);
12600
12707
  if (gitRoot && canCreateScopedProjectDb()) {
12601
- return join3(gitRoot, ".hasna", "todos", "todos.db");
12708
+ return join5(gitRoot, ".hasna", "todos", "todos.db");
12602
12709
  }
12603
12710
  }
12604
12711
  return getGlobalDbPath();
@@ -12609,8 +12716,8 @@ function getDatabasePath() {
12609
12716
  function ensureDir2(filePath) {
12610
12717
  if (isInMemoryDb(filePath))
12611
12718
  return;
12612
- const dir = dirname2(resolve2(filePath));
12613
- if (!existsSync4(dir)) {
12719
+ const dir = dirname2(resolve3(filePath));
12720
+ if (!existsSync5(dir)) {
12614
12721
  mkdirSync2(dir, { recursive: true });
12615
12722
  }
12616
12723
  }
@@ -13453,9 +13560,9 @@ __export(exports_helpers, {
13453
13560
  });
13454
13561
  import chalk from "chalk";
13455
13562
  import { execSync } from "child_process";
13456
- import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, writeSync } from "fs";
13563
+ import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2, writeSync } from "fs";
13457
13564
  import { tmpdir } from "os";
13458
- import { dirname as dirname3, join as join4, resolve as resolve3, sep } from "path";
13565
+ import { dirname as dirname3, join as join6, resolve as resolve4, sep } from "path";
13459
13566
  function jsonModeRequested(argv = process.argv) {
13460
13567
  return argv.some((arg) => arg === "--json" || /^-[a-z]+$/i.test(arg) && arg.includes("j"));
13461
13568
  }
@@ -13527,11 +13634,13 @@ async function resolveTaskIdForCommand(input, cloud = getTodosCloudClient()) {
13527
13634
  }
13528
13635
  function cloudTaskIdCachePath() {
13529
13636
  const home = process.env["HOME"] || process.env["USERPROFILE"];
13530
- return home ? join4(home, ".hasna", "todos", "cloud-task-id-cache.json") : null;
13637
+ if (!home)
13638
+ return null;
13639
+ return join6(getTodosGlobalDir(), "cloud-task-id-cache.json");
13531
13640
  }
13532
13641
  function readCloudTaskIdCache() {
13533
13642
  const path = cloudTaskIdCachePath();
13534
- if (!path || !existsSync5(path))
13643
+ if (!path || !existsSync6(path))
13535
13644
  return { version: 1, tasks: [] };
13536
13645
  try {
13537
13646
  const parsed = JSON.parse(readFileSync3(path, "utf8"));
@@ -13595,12 +13704,12 @@ function detectGitRoot() {
13595
13704
  }
13596
13705
  }
13597
13706
  function isPathWithin(child, parent) {
13598
- const normalizedChild = resolve3(child);
13599
- const normalizedParent = resolve3(parent);
13707
+ const normalizedChild = resolve4(child);
13708
+ const normalizedParent = resolve4(parent);
13600
13709
  return normalizedChild === normalizedParent || normalizedChild.startsWith(`${normalizedParent}${sep}`);
13601
13710
  }
13602
13711
  function shouldSkipAutoProjectForGitRoot(gitRoot) {
13603
- const normalized = resolve3(gitRoot);
13712
+ const normalized = resolve4(gitRoot);
13604
13713
  if (process.platform !== "win32" && (normalized === "/tmp" || normalized.startsWith("/tmp/"))) {
13605
13714
  return true;
13606
13715
  }
@@ -13608,7 +13717,7 @@ function shouldSkipAutoProjectForGitRoot(gitRoot) {
13608
13717
  }
13609
13718
  function resolveExplicitProject(input) {
13610
13719
  const db = getDatabase();
13611
- const byPath = getProjectByPath(resolve3(input), db);
13720
+ const byPath = getProjectByPath(resolve4(input), db);
13612
13721
  if (byPath)
13613
13722
  return byPath;
13614
13723
  const id = resolvePartialId(db, "projects", input);
@@ -13780,6 +13889,7 @@ var init_helpers = __esm(() => {
13780
13889
  init_projects();
13781
13890
  init_lock_display();
13782
13891
  init_package_version();
13892
+ init_sync_utils();
13783
13893
  init_types();
13784
13894
  stdoutRetryBuffer = new SharedArrayBuffer(4);
13785
13895
  stdoutRetrySignal = new Int32Array(stdoutRetryBuffer);
@@ -13869,14 +13979,14 @@ var init_completion_guard = __esm(() => {
13869
13979
 
13870
13980
  // src/lib/event-emission-safety.ts
13871
13981
  import { tmpdir as tmpdir2 } from "os";
13872
- import { resolve as resolve4, sep as sep2 } from "path";
13982
+ import { resolve as resolve5, sep as sep2 } from "path";
13873
13983
  function envFlag(name) {
13874
13984
  const value = process.env[name]?.trim().toLowerCase();
13875
13985
  return value === "1" || value === "true" || value === "yes" || value === "on";
13876
13986
  }
13877
13987
  function isUnder(parent, child) {
13878
- const normalizedParent = resolve4(parent);
13879
- const normalizedChild = resolve4(child);
13988
+ const normalizedParent = resolve5(parent);
13989
+ const normalizedChild = resolve5(child);
13880
13990
  return normalizedChild === normalizedParent || normalizedChild.startsWith(`${normalizedParent}${sep2}`);
13881
13991
  }
13882
13992
  function databasePathFromDatabase(db) {
@@ -13927,9 +14037,9 @@ __export(exports_workspace_trust, {
13927
14037
  getWorkspaceTrustStatus: () => getWorkspaceTrustStatus,
13928
14038
  checkWorkspacePermission: () => checkWorkspacePermission
13929
14039
  });
13930
- import { relative, resolve as resolve5 } from "path";
14040
+ import { relative, resolve as resolve6 } from "path";
13931
14041
  function normalizePath(path) {
13932
- return resolve5(path);
14042
+ return resolve6(path);
13933
14043
  }
13934
14044
  function unique2(values) {
13935
14045
  return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
@@ -14120,9 +14230,9 @@ __export(exports_runner_sandbox, {
14120
14230
  explainRunnerSandbox: () => explainRunnerSandbox,
14121
14231
  checkRunnerSandbox: () => checkRunnerSandbox
14122
14232
  });
14123
- import { relative as relative2, resolve as resolve6 } from "path";
14233
+ import { relative as relative2, resolve as resolve7 } from "path";
14124
14234
  function normalizePath2(path) {
14125
- return resolve6(path);
14235
+ return resolve7(path);
14126
14236
  }
14127
14237
  function unique3(values) {
14128
14238
  return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
@@ -14326,7 +14436,7 @@ __export(exports_event_hooks, {
14326
14436
  });
14327
14437
  import { createHash as createHash4, randomUUID as randomUUID2 } from "crypto";
14328
14438
  import { appendFileSync, mkdirSync as mkdirSync4 } from "fs";
14329
- import { dirname as dirname4, resolve as resolve7 } from "path";
14439
+ import { dirname as dirname4, resolve as resolve8 } from "path";
14330
14440
  import { createConnection } from "net";
14331
14441
  function safeName(name) {
14332
14442
  const trimmed = name.trim();
@@ -14464,7 +14574,7 @@ async function deliverHook(hook, envelope) {
14464
14574
  if (hook.target === "stdout") {
14465
14575
  output2 = line.trim();
14466
14576
  } else if (hook.target === "file") {
14467
- const filePath = resolve7(hook.file_path);
14577
+ const filePath = resolve8(hook.file_path);
14468
14578
  mkdirSync4(dirname4(filePath), { recursive: true });
14469
14579
  appendFileSync(filePath, line);
14470
14580
  } else if (hook.target === "socket") {
@@ -14585,9 +14695,9 @@ var init_event_hooks = __esm(() => {
14585
14695
  // node_modules/.bun/@hasna+events@0.1.16/node_modules/@hasna/events/dist/index.js
14586
14696
  import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
14587
14697
  import { Buffer as Buffer2 } from "buffer";
14588
- import { existsSync as existsSync6 } from "fs";
14589
- import { homedir as homedir2 } from "os";
14590
- import { join as join5 } from "path";
14698
+ import { existsSync as existsSync7 } from "fs";
14699
+ import { homedir as homedir4 } from "os";
14700
+ import { join as join7 } from "path";
14591
14701
  import { createHmac, timingSafeEqual } from "crypto";
14592
14702
  import { randomUUID as randomUUID3 } from "crypto";
14593
14703
  import { spawn } from "child_process";
@@ -14688,7 +14798,7 @@ function channelMatchesEvent(channel, event) {
14688
14798
  return channel.filters.some((filter) => eventMatchesFilter(event, filter));
14689
14799
  }
14690
14800
  function getEventsDataDir(override) {
14691
- return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join5(homedir2(), ".hasna", "events");
14801
+ return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join7(homedir4(), ".hasna", "events");
14692
14802
  }
14693
14803
 
14694
14804
  class JsonEventsStore {
@@ -14697,12 +14807,12 @@ class JsonEventsStore {
14697
14807
  channelsPath;
14698
14808
  eventsPath;
14699
14809
  deliveriesPath;
14700
- constructor(dataDir = getEventsDataDir()) {
14701
- this.dataDir = dataDir;
14702
- this.runtime = localJsonRuntime(dataDir);
14703
- this.channelsPath = join5(dataDir, "channels.json");
14704
- this.eventsPath = join5(dataDir, "events.json");
14705
- this.deliveriesPath = join5(dataDir, "deliveries.json");
14810
+ constructor(dataDir2 = getEventsDataDir()) {
14811
+ this.dataDir = dataDir2;
14812
+ this.runtime = localJsonRuntime(dataDir2);
14813
+ this.channelsPath = join7(dataDir2, "channels.json");
14814
+ this.eventsPath = join7(dataDir2, "events.json");
14815
+ this.deliveriesPath = join7(dataDir2, "deliveries.json");
14706
14816
  }
14707
14817
  async init() {
14708
14818
  await mkdir(this.dataDir, { recursive: true, mode: 448 });
@@ -14819,7 +14929,7 @@ class JsonEventsStore {
14819
14929
  };
14820
14930
  }
14821
14931
  async ensureArrayFile(path) {
14822
- if (!existsSync6(path)) {
14932
+ if (!existsSync7(path)) {
14823
14933
  await writeFile(path, `[]
14824
14934
  `, { encoding: "utf-8", mode: 384 });
14825
14935
  }
@@ -14849,7 +14959,7 @@ class JsonEventsStore {
14849
14959
  });
14850
14960
  }
14851
14961
  }
14852
- function localJsonRuntime(dataDir = getEventsDataDir()) {
14962
+ function localJsonRuntime(dataDir2 = getEventsDataDir()) {
14853
14963
  return {
14854
14964
  mode: "local-files",
14855
14965
  name: "json-events-store",
@@ -14862,7 +14972,7 @@ function localJsonRuntime(dataDir = getEventsDataDir()) {
14862
14972
  durable: true,
14863
14973
  idempotency: "best-effort-local",
14864
14974
  replayCursors: true,
14865
- description: `Local JSON files in ${dataDir}; no SQLite, Postgres, S3, or AWS runtime is configured by this store.`
14975
+ description: `Local JSON files in ${dataDir2}; no SQLite, Postgres, S3, or AWS runtime is configured by this store.`
14866
14976
  };
14867
14977
  }
14868
14978
  function encodeLocalJsonEventCursor(offset, options = {}) {
@@ -15041,7 +15151,7 @@ async function dispatchCommand(event, channel) {
15041
15151
  HASNA_EVENT_SCHEMA_VERSION: event.schemaVersion,
15042
15152
  HASNA_EVENT_JSON: eventJson
15043
15153
  };
15044
- return new Promise((resolve8) => {
15154
+ return new Promise((resolve9) => {
15045
15155
  const child = spawn(channel.command.command, channel.command.args ?? [], {
15046
15156
  cwd: channel.command.cwd,
15047
15157
  env,
@@ -15059,7 +15169,7 @@ async function dispatchCommand(event, channel) {
15059
15169
  });
15060
15170
  child.on("error", (error) => {
15061
15171
  clearTimeout(timeout);
15062
- resolve8({
15172
+ resolve9({
15063
15173
  attempt: 1,
15064
15174
  status: "failed",
15065
15175
  startedAt,
@@ -15072,7 +15182,7 @@ async function dispatchCommand(event, channel) {
15072
15182
  child.on("close", (code, signal) => {
15073
15183
  clearTimeout(timeout);
15074
15184
  const success = code === 0;
15075
- resolve8({
15185
+ resolve9({
15076
15186
  attempt: 1,
15077
15187
  status: success ? "success" : "failed",
15078
15188
  startedAt,
@@ -15414,7 +15524,7 @@ function normalizeRetryPolicy(policy) {
15414
15524
  };
15415
15525
  }
15416
15526
  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;
15417
- var init_dist = __esm(() => {
15527
+ var init_dist2 = __esm(() => {
15418
15528
  DEFAULT_SIGNATURE_TOLERANCE_MS = 5 * 60 * 1000;
15419
15529
  EventValidationError = class EventValidationError extends Error {
15420
15530
  eventType;
@@ -15837,7 +15947,7 @@ function emitSharedTaskEventQuiet(input) {
15837
15947
  }
15838
15948
  var SOURCE = "todos";
15839
15949
  var init_shared_events = __esm(() => {
15840
- init_dist();
15950
+ init_dist2();
15841
15951
  init_database();
15842
15952
  init_projects();
15843
15953
  init_task_lists();
@@ -16594,14 +16704,14 @@ var init_task_parent_integrity = __esm(() => {
16594
16704
  });
16595
16705
 
16596
16706
  // src/lib/creator-identity.ts
16597
- import { existsSync as existsSync7, rmSync } from "fs";
16598
- import { join as join6 } from "path";
16707
+ import { existsSync as existsSync8, rmSync } from "fs";
16708
+ import { join as join8 } from "path";
16599
16709
  function identityFilePath() {
16600
- return join6(getTodosGlobalDir(), "identity.json");
16710
+ return join8(getTodosGlobalDir(), "identity.json");
16601
16711
  }
16602
16712
  function readPersistedIdentity() {
16603
16713
  const path = identityFilePath();
16604
- if (!existsSync7(path))
16714
+ if (!existsSync8(path))
16605
16715
  return null;
16606
16716
  const parsed = readJsonFile(path);
16607
16717
  if (!parsed || typeof parsed.agent_id !== "string" || !parsed.agent_id.trim())
@@ -16631,7 +16741,7 @@ function persistIdentity(identity) {
16631
16741
  }
16632
16742
  function clearPersistedIdentity() {
16633
16743
  const path = identityFilePath();
16634
- if (!existsSync7(path))
16744
+ if (!existsSync8(path))
16635
16745
  return false;
16636
16746
  try {
16637
16747
  rmSync(path);
@@ -18164,17 +18274,17 @@ function sanitizeCreateTaskInput(input) {
18164
18274
  return {
18165
18275
  ...input,
18166
18276
  title: sanitizePreWriteText(input.title, "task.title"),
18167
- description: input.description !== undefined ? sanitizePreWriteText(input.description, "task.description") : undefined,
18277
+ description: input.description == null ? input.description : sanitizePreWriteText(input.description, "task.description"),
18168
18278
  tags: input.tags !== undefined ? sanitizePreWriteValue(input.tags, "task.tags") : undefined,
18169
18279
  metadata: input.metadata !== undefined ? sanitizePreWriteValue(input.metadata, "task.metadata") : undefined,
18170
- reason: input.reason !== undefined ? sanitizePreWriteText(input.reason, "task.reason") : undefined
18280
+ reason: input.reason == null ? input.reason : sanitizePreWriteText(input.reason, "task.reason")
18171
18281
  };
18172
18282
  }
18173
18283
  function sanitizeUpdateTaskInput(input) {
18174
18284
  return {
18175
18285
  ...input,
18176
18286
  title: input.title !== undefined ? sanitizePreWriteText(input.title, "task.title") : undefined,
18177
- description: input.description !== undefined ? sanitizePreWriteText(input.description, "task.description") : undefined,
18287
+ description: input.description == null ? input.description : sanitizePreWriteText(input.description, "task.description"),
18178
18288
  tags: input.tags !== undefined ? sanitizePreWriteValue(input.tags, "task.tags") : undefined,
18179
18289
  metadata: input.metadata !== undefined ? sanitizePreWriteValue(input.metadata, "task.metadata") : undefined
18180
18290
  };
@@ -20188,28 +20298,28 @@ var init_boards = __esm(() => {
20188
20298
 
20189
20299
  // src/lib/artifact-store.ts
20190
20300
  import { createHash as createHash5 } from "crypto";
20191
- import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync4, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
20192
- import { basename as basename2, dirname as dirname5, join as join7, resolve as resolve8 } from "path";
20301
+ import { existsSync as existsSync9, mkdirSync as mkdirSync5, readFileSync as readFileSync4, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync3 } from "fs";
20302
+ import { basename as basename2, dirname as dirname5, join as join9, resolve as resolve9 } from "path";
20193
20303
  import { tmpdir as tmpdir3 } from "os";
20194
20304
  function isInMemoryDb2(path) {
20195
20305
  return path === ":memory:" || path.startsWith("file::memory:");
20196
20306
  }
20197
20307
  function artifactStoreRoot() {
20198
20308
  if (process.env["HASNA_TODOS_ARTIFACTS_DIR"])
20199
- return resolve8(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
20309
+ return resolve9(process.env["HASNA_TODOS_ARTIFACTS_DIR"]);
20200
20310
  if (process.env["TODOS_ARTIFACTS_DIR"])
20201
- return resolve8(process.env["TODOS_ARTIFACTS_DIR"]);
20311
+ return resolve9(process.env["TODOS_ARTIFACTS_DIR"]);
20202
20312
  const dbPath = getDatabasePath();
20203
20313
  if (isInMemoryDb2(dbPath))
20204
- return join7(tmpdir3(), "hasna-todos-artifacts");
20205
- return join7(dirname5(resolve8(dbPath)), "artifacts");
20314
+ return join9(tmpdir3(), "hasna-todos-artifacts");
20315
+ return join9(dirname5(resolve9(dbPath)), "artifacts");
20206
20316
  }
20207
20317
  function artifactStorePath(relativePath) {
20208
20318
  const normalized = relativePath.replace(/\\/g, "/");
20209
20319
  if (normalized.includes("..") || normalized.startsWith("/") || normalized.length === 0) {
20210
20320
  throw new Error("Invalid artifact store path");
20211
20321
  }
20212
- return join7(artifactStoreRoot(), normalized);
20322
+ return join9(artifactStoreRoot(), normalized);
20213
20323
  }
20214
20324
  function sha2562(buffer) {
20215
20325
  return createHash5("sha256").update(buffer).digest("hex");
@@ -20249,8 +20359,8 @@ function mediaTypeFor(path, textLike) {
20249
20359
  return "application/octet-stream";
20250
20360
  }
20251
20361
  function storeArtifactContent(input) {
20252
- const sourcePath = resolve8(input.path);
20253
- if (!existsSync8(sourcePath))
20362
+ const sourcePath = resolve9(input.path);
20363
+ if (!existsSync9(sourcePath))
20254
20364
  return null;
20255
20365
  const sourceStat = statSync2(sourcePath);
20256
20366
  if (!sourceStat.isFile())
@@ -20267,9 +20377,9 @@ function storeArtifactContent(input) {
20267
20377
  redactionStatus = "redacted";
20268
20378
  }
20269
20379
  const storedSha = sha2562(storedBuffer);
20270
- const relativePath = join7("sha256", storedSha.slice(0, 2), storedSha).replace(/\\/g, "/");
20380
+ const relativePath = join9("sha256", storedSha.slice(0, 2), storedSha).replace(/\\/g, "/");
20271
20381
  const destination = artifactStorePath(relativePath);
20272
- if (!existsSync8(destination)) {
20382
+ if (!existsSync9(destination)) {
20273
20383
  mkdirSync5(dirname5(destination), { recursive: true });
20274
20384
  writeFileSync3(destination, storedBuffer);
20275
20385
  }
@@ -20329,7 +20439,7 @@ function verifyStoredArtifact(input) {
20329
20439
  };
20330
20440
  }
20331
20441
  const storedPath = artifactStorePath(store.relative_path);
20332
- if (!existsSync8(storedPath)) {
20442
+ if (!existsSync9(storedPath)) {
20333
20443
  return {
20334
20444
  id: input.id,
20335
20445
  path: input.path,
@@ -21930,8 +22040,8 @@ function normalizeAgentNameInput(name) {
21930
22040
 
21931
22041
  // src/lib/assignee-validation.ts
21932
22042
  import { readFileSync as readFileSync5 } from "fs";
21933
- import { homedir as homedir3 } from "os";
21934
- import { join as join8 } from "path";
22043
+ import { homedir as homedir5 } from "os";
22044
+ import { join as join10 } from "path";
21935
22045
  function describeAssigneeFilter(input, ctx) {
21936
22046
  const raw = input.trim();
21937
22047
  const normalized = normalizeAgentNameInput(raw);
@@ -21950,7 +22060,7 @@ function describeAssigneeFilter(input, ctx) {
21950
22060
  };
21951
22061
  }
21952
22062
  function defaultSeatRosterPath() {
21953
- return process.env["TODOS_SEAT_ROSTER_PATH"] || join8(homedir3(), ".hasna", "identities", "hasna-seats.roster.json");
22063
+ return process.env["TODOS_SEAT_ROSTER_PATH"] || join10(homedir5(), ".hasna", "identities", "hasna-seats.roster.json");
21954
22064
  }
21955
22065
  function loadSeatSlugs(path = defaultSeatRosterPath()) {
21956
22066
  try {
@@ -22756,7 +22866,7 @@ __export(exports_task_routing, {
22756
22866
  setTaskWorkflowPointers: () => setTaskWorkflowPointers,
22757
22867
  getTaskRouteState: () => getTaskRouteState
22758
22868
  });
22759
- import { existsSync as existsSync9, statSync as statSync3 } from "fs";
22869
+ import { existsSync as existsSync10, statSync as statSync3 } from "fs";
22760
22870
  function machineLocalPath(project, db) {
22761
22871
  const machineId = process.env["TODOS_MACHINE_ID"];
22762
22872
  if (!machineId)
@@ -22796,7 +22906,7 @@ function routeConcurrencyKey(task, project, taskList, projectPath) {
22796
22906
  }
22797
22907
  function directoryExists(path) {
22798
22908
  try {
22799
- return existsSync9(path) && statSync3(path).isDirectory();
22909
+ return existsSync10(path) && statSync3(path).isDirectory();
22800
22910
  } catch {
22801
22911
  return false;
22802
22912
  }
@@ -23025,7 +23135,7 @@ __export(exports_task_commands, {
23025
23135
  escapeTerminalControls: () => escapeTerminalControls
23026
23136
  });
23027
23137
  import chalk3 from "chalk";
23028
- import { basename as basename3, resolve as resolve9 } from "path";
23138
+ import { basename as basename3, resolve as resolve10 } from "path";
23029
23139
  function escapeTerminalControls(value) {
23030
23140
  return value.replace(/[\u0000-\u001f\u007f-\u009f]/g, (character) => {
23031
23141
  const code = character.charCodeAt(0);
@@ -23068,11 +23178,11 @@ async function cloudDetailGitRefs(cloud, id) {
23068
23178
  function resolveProjectIdOrSlug(input) {
23069
23179
  const db = getDatabase();
23070
23180
  if (isPathLike(input)) {
23071
- const projectPath = resolve9(input);
23181
+ const projectPath = resolve10(input);
23072
23182
  const byPath2 = getProjectByPath(projectPath, db);
23073
23183
  return (byPath2 ?? ensureProject(basename3(projectPath), projectPath, db)).id;
23074
23184
  }
23075
- const byPath = getProjectByPath(resolve9(input), db);
23185
+ const byPath = getProjectByPath(resolve10(input), db);
23076
23186
  if (byPath)
23077
23187
  return byPath.id;
23078
23188
  const byId = getProject(input, db);
@@ -23481,7 +23591,7 @@ function registerTaskCommands(program2) {
23481
23591
  task_list_id: taskListId2,
23482
23592
  tags: parseTags(opts.tags),
23483
23593
  metadata: buildExpectationMetadata(opts),
23484
- working_dir: opts.workingDir ? resolve9(opts.workingDir) : process.cwd(),
23594
+ working_dir: opts.workingDir ? resolve10(opts.workingDir) : process.cwd(),
23485
23595
  project_id: projectId2,
23486
23596
  assigned_to: opts.assign,
23487
23597
  plan_id: plan?.id
@@ -23518,7 +23628,7 @@ function registerTaskCommands(program2) {
23518
23628
  task_list_id: taskListId,
23519
23629
  tags: parseTags(opts.tags),
23520
23630
  metadata: buildExpectationMetadata(opts),
23521
- working_dir: opts.workingDir ? resolve9(opts.workingDir) : process.cwd(),
23631
+ working_dir: opts.workingDir ? resolve10(opts.workingDir) : process.cwd(),
23522
23632
  project_id: projectId,
23523
23633
  assigned_to: opts.assign,
23524
23634
  plan_id: planId,
@@ -24264,7 +24374,7 @@ ${chalk3.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
24264
24374
  tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : undefined,
24265
24375
  plan_id: plan?.id ?? (opts.clearPlan ? null : undefined),
24266
24376
  ...reparent2,
24267
- working_dir: opts.workingDir ? resolve9(opts.workingDir) : opts.clearWorkingDir ? null : undefined,
24377
+ working_dir: opts.workingDir ? resolve10(opts.workingDir) : opts.clearWorkingDir ? null : undefined,
24268
24378
  estimated_minutes: opts.estimated !== undefined ? parseIntOption(opts.estimated, "--estimated") : undefined,
24269
24379
  sla_minutes: opts.slaMinutes !== undefined || opts.sla !== undefined ? parseIntOption(opts.slaMinutes ?? opts.sla, "--sla-minutes") : undefined,
24270
24380
  due_at: opts.due !== undefined ? opts.due === "" ? null : opts.due.length === 10 ? opts.due + "T00:00:00.000Z" : opts.due : undefined,
@@ -24309,7 +24419,7 @@ ${chalk3.cyan(sid)} ${statusColor(task2.status)} ${prioColor(task2.priority)} ${
24309
24419
  tags: opts.tags ? opts.tags.split(",").map((t) => t.trim()) : undefined,
24310
24420
  plan_id: planId,
24311
24421
  ...reparent,
24312
- working_dir: opts.workingDir ? resolve9(opts.workingDir) : opts.clearWorkingDir ? null : undefined,
24422
+ working_dir: opts.workingDir ? resolve10(opts.workingDir) : opts.clearWorkingDir ? null : undefined,
24313
24423
  estimated_minutes: opts.estimated !== undefined ? parseIntOption(opts.estimated, "--estimated") : undefined,
24314
24424
  sla_minutes: opts.slaMinutes !== undefined || opts.sla !== undefined ? parseIntOption(opts.slaMinutes ?? opts.sla, "--sla-minutes") : undefined,
24315
24425
  due_at: opts.due !== undefined ? opts.due === "" ? null : opts.due.length === 10 ? opts.due + "T00:00:00.000Z" : opts.due : undefined,
@@ -24827,8 +24937,8 @@ var init_plan_comments = __esm(() => {
24827
24937
  });
24828
24938
 
24829
24939
  // src/lib/plan-artifacts.ts
24830
- import { existsSync as existsSync10, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
24831
- import { join as join9, resolve as resolve10 } from "path";
24940
+ import { existsSync as existsSync11, mkdirSync as mkdirSync6, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "fs";
24941
+ import { join as join11, resolve as resolve11 } from "path";
24832
24942
  function assertSafePathSegment(value, label) {
24833
24943
  const trimmed = value.trim();
24834
24944
  if (!trimmed || trimmed === "." || trimmed === ".." || trimmed.includes("/") || trimmed.includes("\\")) {
@@ -24871,7 +24981,7 @@ function resolvePlanArtifactProject(input) {
24871
24981
  const ref = input.project_id || input.project_ref;
24872
24982
  if (!ref)
24873
24983
  throw new Error("Plan artifacts require a project id or project reference");
24874
- const byPath = getProjectByPath(resolve10(ref), db);
24984
+ const byPath = getProjectByPath(resolve11(ref), db);
24875
24985
  if (byPath)
24876
24986
  return byPath;
24877
24987
  const resolvedId = resolvePartialId(db, "projects", ref);
@@ -24888,8 +24998,8 @@ function resolvePlanArtifactProject(input) {
24888
24998
  function resolvePlanArtifactPaths(input) {
24889
24999
  const project = resolvePlanArtifactProject(input);
24890
25000
  const projectId = assertSafePathSegment(project.id, "project id");
24891
- const projectRoot = resolve10(project.path);
24892
- const directory = join9(projectRoot, ".hasna", "todos", "plans", projectId);
25001
+ const projectRoot = resolve11(project.path);
25002
+ const directory = join11(projectRoot, ".hasna", "todos", "plans", projectId);
24893
25003
  const planId = input.plan_id ? assertSafePathSegment(input.plan_id, "plan id") : null;
24894
25004
  const planSlug = input.plan_slug ? assertSafePathSegment(slugify(input.plan_slug), "plan slug") : null;
24895
25005
  const fileName = planId ? planSlug ? `${planSlug}--${planId.slice(0, 8)}.md` : `${planId}.md` : null;
@@ -24897,7 +25007,7 @@ function resolvePlanArtifactPaths(input) {
24897
25007
  project_id: project.id,
24898
25008
  project_root: projectRoot,
24899
25009
  directory,
24900
- file_path: fileName ? join9(directory, fileName) : directory
25010
+ file_path: fileName ? join11(directory, fileName) : directory
24901
25011
  };
24902
25012
  }
24903
25013
  function resolvePlanArtifactCandidatePaths(plan, db) {
@@ -25063,7 +25173,7 @@ function readPlanArtifact(plan, db) {
25063
25173
  return null;
25064
25174
  const d = db || getDatabase();
25065
25175
  const paths = resolvePlanArtifactCandidatePaths(plan, d);
25066
- const path = existsSync10(paths.primary.file_path) ? paths.primary.file_path : existsSync10(paths.legacy.file_path) ? paths.legacy.file_path : null;
25176
+ const path = existsSync11(paths.primary.file_path) ? paths.primary.file_path : existsSync11(paths.legacy.file_path) ? paths.legacy.file_path : null;
25067
25177
  if (!path)
25068
25178
  return null;
25069
25179
  const markdown = readFileSync6(path, "utf8");
@@ -25078,7 +25188,7 @@ function inspectPlanArtifact(plan, db) {
25078
25188
  return null;
25079
25189
  const d = db || getDatabase();
25080
25190
  const paths = resolvePlanArtifactCandidatePaths(plan, d);
25081
- const path = existsSync10(paths.primary.file_path) ? paths.primary.file_path : existsSync10(paths.legacy.file_path) ? paths.legacy.file_path : null;
25191
+ const path = existsSync11(paths.primary.file_path) ? paths.primary.file_path : existsSync11(paths.legacy.file_path) ? paths.legacy.file_path : null;
25082
25192
  if (!path) {
25083
25193
  return {
25084
25194
  path: paths.primary.file_path,
@@ -26554,7 +26664,7 @@ __export(exports_builtin_templates, {
26554
26664
  BUILTIN_TEMPLATES: () => BUILTIN_TEMPLATES
26555
26665
  });
26556
26666
  import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync5 } from "fs";
26557
- import { join as join10 } from "path";
26667
+ import { join as join12 } from "path";
26558
26668
  function templateMetadata(template) {
26559
26669
  return {
26560
26670
  source: BUILTIN_TEMPLATE_LIBRARY_SOURCE,
@@ -26613,7 +26723,7 @@ function writeBuiltinTemplateFiles(directory) {
26613
26723
  mkdirSync7(directory, { recursive: true });
26614
26724
  const files = [];
26615
26725
  for (const entry of exportBuiltinTemplateFiles()) {
26616
- const path = join10(directory, entry.filename);
26726
+ const path = join12(directory, entry.filename);
26617
26727
  writeFileSync5(path, `${JSON.stringify(entry.template, null, 2)}
26618
26728
  `, "utf-8");
26619
26729
  files.push(path);
@@ -28305,16 +28415,16 @@ var init_saved_search_views = __esm(() => {
28305
28415
  });
28306
28416
 
28307
28417
  // src/lib/claude-tasks.ts
28308
- import { existsSync as existsSync11, readFileSync as readFileSync7, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "fs";
28309
- import { join as join11 } from "path";
28418
+ import { existsSync as existsSync12, readFileSync as readFileSync7, readdirSync as readdirSync2, writeFileSync as writeFileSync6 } from "fs";
28419
+ import { join as join13 } from "path";
28310
28420
  function getTaskListDir(taskListId) {
28311
- return join11(HOME, ".claude", "tasks", taskListId);
28421
+ return join13(HOME, ".claude", "tasks", taskListId);
28312
28422
  }
28313
28423
  function readClaudeTask(dir, filename) {
28314
- return readJsonFile(join11(dir, filename));
28424
+ return readJsonFile(join13(dir, filename));
28315
28425
  }
28316
28426
  function writeClaudeTask(dir, task) {
28317
- writeJsonFile(join11(dir, `${task.id}.json`), task);
28427
+ writeJsonFile(join13(dir, `${task.id}.json`), task);
28318
28428
  }
28319
28429
  function toClaudeStatus(status) {
28320
28430
  if (status === "pending" || status === "in_progress" || status === "completed") {
@@ -28326,14 +28436,14 @@ function toSqliteStatus(status) {
28326
28436
  return status;
28327
28437
  }
28328
28438
  function readPrefixCounter(dir) {
28329
- const path = join11(dir, ".prefix-counter");
28330
- if (!existsSync11(path))
28439
+ const path = join13(dir, ".prefix-counter");
28440
+ if (!existsSync12(path))
28331
28441
  return 0;
28332
28442
  const val = parseInt(readFileSync7(path, "utf-8").trim(), 10);
28333
28443
  return isNaN(val) ? 0 : val;
28334
28444
  }
28335
28445
  function writePrefixCounter(dir, value) {
28336
- writeFileSync6(join11(dir, ".prefix-counter"), String(value));
28446
+ writeFileSync6(join13(dir, ".prefix-counter"), String(value));
28337
28447
  }
28338
28448
  function formatPrefixedSubject(title, prefix, counter) {
28339
28449
  const padded = String(counter).padStart(5, "0");
@@ -28360,7 +28470,7 @@ function taskToClaudeTask(task, claudeTaskId, existingMeta) {
28360
28470
  }
28361
28471
  function pushToClaudeTaskList(taskListId, projectId, options = {}) {
28362
28472
  const dir = getTaskListDir(taskListId);
28363
- if (!existsSync11(dir))
28473
+ if (!existsSync12(dir))
28364
28474
  ensureDir(dir);
28365
28475
  const filter = {};
28366
28476
  if (projectId)
@@ -28369,7 +28479,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
28369
28479
  const existingByTodosId = new Map;
28370
28480
  const files = listJsonFiles(dir);
28371
28481
  for (const f of files) {
28372
- const path = join11(dir, f);
28482
+ const path = join13(dir, f);
28373
28483
  const ct = readClaudeTask(dir, f);
28374
28484
  if (ct?.metadata?.["todos_id"]) {
28375
28485
  existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
@@ -28458,7 +28568,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
28458
28568
  }
28459
28569
  function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
28460
28570
  const dir = getTaskListDir(taskListId);
28461
- if (!existsSync11(dir)) {
28571
+ if (!existsSync12(dir)) {
28462
28572
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
28463
28573
  }
28464
28574
  const files = readdirSync2(dir).filter((f) => f.endsWith(".json"));
@@ -28478,7 +28588,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
28478
28588
  }
28479
28589
  for (const f of files) {
28480
28590
  try {
28481
- const filePath = join11(dir, f);
28591
+ const filePath = join13(dir, f);
28482
28592
  const ct = readClaudeTask(dir, f);
28483
28593
  if (!ct)
28484
28594
  continue;
@@ -28551,20 +28661,20 @@ var init_claude_tasks = __esm(() => {
28551
28661
  });
28552
28662
 
28553
28663
  // src/lib/agent-tasks.ts
28554
- import { existsSync as existsSync12 } from "fs";
28555
- import { join as join12 } from "path";
28664
+ import { existsSync as existsSync13 } from "fs";
28665
+ import { join as join14 } from "path";
28556
28666
  function agentBaseDir(agent) {
28557
28667
  const key = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
28558
- return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join12(getTodosGlobalDir(), "agents");
28668
+ return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join14(getTodosGlobalDir(), "agents");
28559
28669
  }
28560
28670
  function getTaskListDir2(agent, taskListId) {
28561
- return join12(agentBaseDir(agent), agent, taskListId);
28671
+ return join14(agentBaseDir(agent), agent, taskListId);
28562
28672
  }
28563
28673
  function readAgentTask(dir, filename) {
28564
- return readJsonFile(join12(dir, filename));
28674
+ return readJsonFile(join14(dir, filename));
28565
28675
  }
28566
28676
  function writeAgentTask(dir, task) {
28567
- writeJsonFile(join12(dir, `${task.id}.json`), task);
28677
+ writeJsonFile(join14(dir, `${task.id}.json`), task);
28568
28678
  }
28569
28679
  function taskToAgentTask(task, externalId, existingMeta) {
28570
28680
  return withSyncFingerprint({
@@ -28589,7 +28699,7 @@ function metadataKey(agent) {
28589
28699
  }
28590
28700
  function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
28591
28701
  const dir = getTaskListDir2(agent, taskListId);
28592
- if (!existsSync12(dir))
28702
+ if (!existsSync13(dir))
28593
28703
  ensureDir(dir);
28594
28704
  const filter = {};
28595
28705
  if (projectId)
@@ -28598,7 +28708,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
28598
28708
  const existingByTodosId = new Map;
28599
28709
  const files = listJsonFiles(dir);
28600
28710
  for (const f of files) {
28601
- const path = join12(dir, f);
28711
+ const path = join14(dir, f);
28602
28712
  const at = readAgentTask(dir, f);
28603
28713
  if (at?.metadata?.["todos_id"]) {
28604
28714
  existingByTodosId.set(at.metadata["todos_id"], { task: at, mtimeMs: getFileMtimeMs(path) });
@@ -28674,7 +28784,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
28674
28784
  }
28675
28785
  function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
28676
28786
  const dir = getTaskListDir2(agent, taskListId);
28677
- if (!existsSync12(dir)) {
28787
+ if (!existsSync13(dir)) {
28678
28788
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
28679
28789
  }
28680
28790
  const files = listJsonFiles(dir);
@@ -28693,7 +28803,7 @@ function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
28693
28803
  }
28694
28804
  for (const f of files) {
28695
28805
  try {
28696
- const filePath = join12(dir, f);
28806
+ const filePath = join14(dir, f);
28697
28807
  const at = readAgentTask(dir, f);
28698
28808
  if (!at)
28699
28809
  continue;
@@ -29101,8 +29211,8 @@ __export(exports_project_bootstrap, {
29101
29211
  discoverProjectWorkspace: () => discoverProjectWorkspace,
29102
29212
  bootstrapProject: () => bootstrapProject
29103
29213
  });
29104
- import { existsSync as existsSync13, readFileSync as readFileSync8, statSync as statSync4 } from "fs";
29105
- import { basename as basename4, dirname as dirname6, resolve as resolve11 } from "path";
29214
+ import { existsSync as existsSync14, readFileSync as readFileSync8, statSync as statSync4 } from "fs";
29215
+ import { basename as basename4, dirname as dirname6, resolve as resolve12 } from "path";
29106
29216
  function safeStat(path) {
29107
29217
  try {
29108
29218
  return statSync4(path);
@@ -29111,7 +29221,7 @@ function safeStat(path) {
29111
29221
  }
29112
29222
  }
29113
29223
  function canonicalPath(input) {
29114
- const resolved = resolve11(input);
29224
+ const resolved = resolve12(input);
29115
29225
  const stats = safeStat(resolved);
29116
29226
  if (stats?.isFile())
29117
29227
  return dirname6(resolved);
@@ -29120,7 +29230,7 @@ function canonicalPath(input) {
29120
29230
  function findUp(start, marker) {
29121
29231
  let current = canonicalPath(start);
29122
29232
  while (true) {
29123
- if (existsSync13(resolve11(current, marker)))
29233
+ if (existsSync14(resolve12(current, marker)))
29124
29234
  return current;
29125
29235
  const parent = dirname6(current);
29126
29236
  if (parent === current)
@@ -29131,8 +29241,8 @@ function findUp(start, marker) {
29131
29241
  function readPackageJson(path) {
29132
29242
  if (!path)
29133
29243
  return null;
29134
- const file = resolve11(path, "package.json");
29135
- if (!existsSync13(file))
29244
+ const file = resolve12(path, "package.json");
29245
+ if (!existsSync14(file))
29136
29246
  return null;
29137
29247
  try {
29138
29248
  const parsed = JSON.parse(readFileSync8(file, "utf-8"));
@@ -29154,7 +29264,7 @@ function workspaceMarker(root, rootPackage) {
29154
29264
  if (rootPackage?.workspaces)
29155
29265
  markers.push("package.json#workspaces");
29156
29266
  for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
29157
- if (existsSync13(resolve11(root, marker)))
29267
+ if (existsSync14(resolve12(root, marker)))
29158
29268
  markers.push(marker);
29159
29269
  }
29160
29270
  const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
@@ -29488,9 +29598,9 @@ __export(exports_extract, {
29488
29598
  buildCodebaseIndex: () => buildCodebaseIndex,
29489
29599
  EXTRACT_TAGS: () => EXTRACT_TAGS
29490
29600
  });
29491
- import { existsSync as existsSync14, readFileSync as readFileSync9, statSync as statSync5 } from "fs";
29601
+ import { existsSync as existsSync15, readFileSync as readFileSync9, statSync as statSync5 } from "fs";
29492
29602
  import { createHash as createHash7 } from "crypto";
29493
- import { relative as relative3, resolve as resolve12, join as join13 } from "path";
29603
+ import { relative as relative3, resolve as resolve13, join as join15 } from "path";
29494
29604
  function stableHash(value) {
29495
29605
  return createHash7("sha256").update(value).digest("hex");
29496
29606
  }
@@ -29498,9 +29608,9 @@ function normalizePathForMatch(value) {
29498
29608
  return value.replace(/\\/g, "/").replace(/^\.\//, "");
29499
29609
  }
29500
29610
  function readGitignorePatterns(basePath) {
29501
- const root = statSync5(basePath).isFile() ? resolve12(basePath, "..") : basePath;
29502
- const gitignorePath = join13(root, ".gitignore");
29503
- if (!existsSync14(gitignorePath))
29611
+ const root = statSync5(basePath).isFile() ? resolve13(basePath, "..") : basePath;
29612
+ const gitignorePath = join15(root, ".gitignore");
29613
+ if (!existsSync15(gitignorePath))
29504
29614
  return [];
29505
29615
  try {
29506
29616
  return readFileSync9(gitignorePath, "utf-8").split(`
@@ -29634,7 +29744,7 @@ function collectFiles(basePath, extensions, excludes, respectGitignore) {
29634
29744
  return files.sort();
29635
29745
  }
29636
29746
  function buildCodebaseIndex(options) {
29637
- const basePath = resolve12(options.path);
29747
+ const basePath = resolve13(options.path);
29638
29748
  const tags = options.patterns || [...EXTRACT_TAGS];
29639
29749
  const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
29640
29750
  const excludes = options.exclude || [];
@@ -29642,10 +29752,10 @@ function buildCodebaseIndex(options) {
29642
29752
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
29643
29753
  const indexed = [];
29644
29754
  for (const file of files) {
29645
- const fullPath = statSync5(basePath).isFile() ? basePath : join13(basePath, file);
29755
+ const fullPath = statSync5(basePath).isFile() ? basePath : join15(basePath, file);
29646
29756
  try {
29647
29757
  const source = readFileSync9(fullPath, "utf-8");
29648
- const relPath = statSync5(basePath).isFile() ? relative3(resolve12(basePath, ".."), fullPath) : file;
29758
+ const relPath = statSync5(basePath).isFile() ? relative3(resolve13(basePath, ".."), fullPath) : file;
29649
29759
  indexed.push({
29650
29760
  file: relPath,
29651
29761
  checksum: stableHash(source).slice(0, 24),
@@ -29665,7 +29775,7 @@ function buildCodebaseIndex(options) {
29665
29775
  };
29666
29776
  }
29667
29777
  function extractTodos(options, db) {
29668
- const basePath = resolve12(options.path);
29778
+ const basePath = resolve13(options.path);
29669
29779
  const tags = options.patterns || [...EXTRACT_TAGS];
29670
29780
  const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
29671
29781
  const excludes = options.exclude || [];
@@ -29673,10 +29783,10 @@ function extractTodos(options, db) {
29673
29783
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
29674
29784
  const allComments = [];
29675
29785
  for (const file of files) {
29676
- const fullPath = statSync5(basePath).isFile() ? basePath : join13(basePath, file);
29786
+ const fullPath = statSync5(basePath).isFile() ? basePath : join15(basePath, file);
29677
29787
  try {
29678
29788
  const source = readFileSync9(fullPath, "utf-8");
29679
- const relPath = statSync5(basePath).isFile() ? relative3(resolve12(basePath, ".."), fullPath) : file;
29789
+ const relPath = statSync5(basePath).isFile() ? relative3(resolve13(basePath, ".."), fullPath) : file;
29680
29790
  const comments = extractFromSource(source, relPath, tags);
29681
29791
  allComments.push(...comments);
29682
29792
  } catch {}
@@ -29770,7 +29880,7 @@ async function watchSourceTodos(options, onRun) {
29770
29880
  const interval = Math.max(100, options.interval_ms || 2000);
29771
29881
  const once = options.once !== false && (!options.max_runs || options.max_runs <= 1);
29772
29882
  const maxRuns = options.max_runs ?? (once ? 1 : Number.POSITIVE_INFINITY);
29773
- const root = resolve12(options.path);
29883
+ const root = resolve13(options.path);
29774
29884
  const runs = [];
29775
29885
  let previous = new Map;
29776
29886
  for (let runNumber = 1;runNumber <= maxRuns; runNumber++) {
@@ -31002,11 +31112,12 @@ var init_todos_md = __esm(() => {
31002
31112
  // src/cli/commands/project-commands.ts
31003
31113
  var exports_project_commands = {};
31004
31114
  __export(exports_project_commands, {
31005
- registerProjectCommands: () => registerProjectCommands
31115
+ registerProjectCommands: () => registerProjectCommands,
31116
+ findCaseVariantProjectPaths: () => findCaseVariantProjectPaths
31006
31117
  });
31007
31118
  import chalk5 from "chalk";
31008
31119
  import { readFileSync as readFileSync10, statSync as statSync6 } from "fs";
31009
- import { basename as basename5, resolve as resolve13, sep as sep3 } from "path";
31120
+ import { basename as basename5, resolve as resolve14, sep as sep3 } from "path";
31010
31121
  function collectOption(value, previous = []) {
31011
31122
  return [...previous, value];
31012
31123
  }
@@ -31085,10 +31196,22 @@ function countTasksForDeregistration(tasks) {
31085
31196
  };
31086
31197
  }
31087
31198
  function pathIsWithinPrefix(projectPath, prefix) {
31088
- const normalizedPath = resolve13(projectPath);
31089
- const normalizedPrefix = resolve13(prefix);
31199
+ const normalizedPath = resolve14(projectPath);
31200
+ const normalizedPrefix = resolve14(prefix);
31090
31201
  return normalizedPath === normalizedPrefix || normalizedPath.startsWith(`${normalizedPrefix}${sep3}`);
31091
31202
  }
31203
+ function findCaseVariantProjectPaths(projects, requestedPath) {
31204
+ const raw = requestedPath.trim();
31205
+ if (!raw)
31206
+ return [];
31207
+ const folded = raw.toLowerCase();
31208
+ if (projects.some((project) => project.path === raw))
31209
+ return [];
31210
+ return projects.filter((project) => project.path.toLowerCase() === folded);
31211
+ }
31212
+ function describePathCollisions(variants) {
31213
+ return variants.map((v) => `${v.name ?? v.id} (${v.id}) at ${v.path}`).join("; ");
31214
+ }
31092
31215
  function resolveTaskListFilter(input, projectId) {
31093
31216
  if (!input)
31094
31217
  return;
@@ -31298,7 +31421,7 @@ function registerProjectCommands(program2) {
31298
31421
  }
31299
31422
  let content;
31300
31423
  if (opts.file !== undefined) {
31301
- const commentFilePath = resolve13(opts.file);
31424
+ const commentFilePath = resolve14(opts.file);
31302
31425
  let isRegularFile = false;
31303
31426
  try {
31304
31427
  isRegularFile = statSync6(commentFilePath).isFile();
@@ -31723,13 +31846,20 @@ function registerProjectCommands(program2) {
31723
31846
  if (opts.update) {
31724
31847
  const patch = {
31725
31848
  ...opts.name !== undefined ? { name: opts.name } : {},
31726
- ...opts.path !== undefined ? { path: resolve13(opts.path) } : {},
31849
+ ...opts.path !== undefined ? { path: resolve14(opts.path) } : {},
31727
31850
  ...opts.description !== undefined ? { description: opts.description } : {}
31728
31851
  };
31729
31852
  if (Object.keys(patch).length === 0) {
31730
31853
  handleError(new Error("projects --update requires --name, --path, or --description"));
31731
31854
  }
31732
31855
  const current = cloud ? await cloudResolveProject(cloud, opts.update) : resolveExplicitProject(opts.update);
31856
+ if (patch.path !== undefined) {
31857
+ const allProjects = cloud ? await cloudListProjects(cloud) : listProjects();
31858
+ const variants = findCaseVariantProjectPaths(allProjects.filter((project2) => project2.id !== current.id), patch.path);
31859
+ if (variants.length > 0) {
31860
+ handleError(new Error(`Refusing to update ${current.name} path to ${patch.path}: it is a letter-case variant of an existing project (${describePathCollisions(variants)}). Path-casing drift is how duplicate project rows are minted; use the existing project's exact path instead.`));
31861
+ }
31862
+ }
31733
31863
  const project = cloud ? await cloudUpdateProject(cloud, current.id, patch) : updateProject(current.id, patch);
31734
31864
  outputRecord(project, Boolean(globalOpts.json), "Project updated:");
31735
31865
  return;
@@ -31767,14 +31897,15 @@ function registerProjectCommands(program2) {
31767
31897
  return;
31768
31898
  }
31769
31899
  if (opts.add) {
31770
- const projectPath = resolve13(opts.add);
31900
+ const projectPath = resolve14(opts.add);
31771
31901
  const name = opts.name || basename5(projectPath);
31772
31902
  let parentId;
31773
31903
  if (opts.parent !== undefined) {
31774
31904
  const parent = cloud ? await cloudResolveProject(cloud, opts.parent) : resolveExplicitProject(opts.parent);
31775
31905
  parentId = parent.id;
31776
31906
  }
31777
- const existing = cloud ? (await cloudListProjects(cloud)).find((project2) => project2.path === projectPath) : getProjectByPath(projectPath);
31907
+ const allProjects = cloud ? await cloudListProjects(cloud) : listProjects();
31908
+ const existing = cloud ? allProjects.find((project2) => project2.path === projectPath) : getProjectByPath(projectPath);
31778
31909
  let project;
31779
31910
  if (existing) {
31780
31911
  project = existing;
@@ -31792,6 +31923,10 @@ function registerProjectCommands(program2) {
31792
31923
  project = renameProject(existing.id, { new_slug: opts.taskListId }).project;
31793
31924
  }
31794
31925
  } else {
31926
+ const variants = findCaseVariantProjectPaths(allProjects, projectPath);
31927
+ if (variants.length > 0) {
31928
+ handleError(new Error(`Refusing to register ${projectPath}: this path is a letter-case variant of an existing project (${describePathCollisions(variants)}). Path-casing drift is how duplicate project rows are minted; use \`todos projects --update <id> --path <path>\` to correct the existing row's path instead.`));
31929
+ }
31795
31930
  const input = { name, path: projectPath, description: opts.description, task_list_id: opts.taskListId, parent_id: parentId };
31796
31931
  project = cloud ? await cloudCreateProject(cloud, input) : createProject(input);
31797
31932
  }
@@ -31896,7 +32031,7 @@ function registerProjectCommands(program2) {
31896
32031
  if (!resolved) {
31897
32032
  handleError(new Error(`Project not found: ${projectId}`));
31898
32033
  }
31899
- const entry = setMachineLocalPath2(resolved, resolve13(projectPath));
32034
+ const entry = setMachineLocalPath2(resolved, resolve14(projectPath));
31900
32035
  if (useJson) {
31901
32036
  output(entry, true);
31902
32037
  } else {
@@ -31958,7 +32093,7 @@ function registerProjectCommands(program2) {
31958
32093
  const patterns = opts.pattern ? opts.pattern.split(",").map((t) => t.trim().toUpperCase()) : undefined;
31959
32094
  const taskListId = opts.list ? resolveTaskListId(opts.list) : undefined;
31960
32095
  const result = extractTodos2({
31961
- path: resolve13(scanPath),
32096
+ path: resolve14(scanPath),
31962
32097
  patterns,
31963
32098
  project_id: projectId,
31964
32099
  task_list_id: taskListId,
@@ -32019,7 +32154,7 @@ Indexed ${result.index.files.length} file(s), ${result.index.total_symbols} symb
32019
32154
  const taskListId = opts.list ? resolveTaskListId(opts.list) : undefined;
32020
32155
  const maxRuns = opts.maxRuns ? parseInt(opts.maxRuns, 10) : 1;
32021
32156
  const result = await watchSourceTodos2({
32022
- path: resolve13(scanPath),
32157
+ path: resolve14(scanPath),
32023
32158
  patterns,
32024
32159
  project_id: projectId,
32025
32160
  task_list_id: taskListId,
@@ -32057,7 +32192,7 @@ Indexed ${result.index.files.length} file(s), ${result.index.total_symbols} symb
32057
32192
  const writeOutput = async (content) => {
32058
32193
  if (opts.output) {
32059
32194
  const { writeFileSync: writeFileSync7 } = await import("fs");
32060
- writeFileSync7(resolve13(opts.output), content.endsWith(`
32195
+ writeFileSync7(resolve14(opts.output), content.endsWith(`
32061
32196
  `) ? content : `${content}
32062
32197
  `);
32063
32198
  } else {
@@ -32075,12 +32210,12 @@ Indexed ${result.index.files.length} file(s), ${result.index.total_symbols} symb
32075
32210
  const exported = opts.encrypt ? createEncryptedBridgeBundle2(bundle, { profile: opts.encryptionProfile }) : bundle;
32076
32211
  const json = JSON.stringify(exported, null, 2);
32077
32212
  await writeOutput(json);
32078
- emitLocalEventHooksQuiet3({ type: "export.finished", payload: { format: "bridge", encrypted: Boolean(opts.encrypt), project_id: projectId, output: opts.output ? resolve13(opts.output) : null, stats: bundle.stats } });
32213
+ emitLocalEventHooksQuiet3({ type: "export.finished", payload: { format: "bridge", encrypted: Boolean(opts.encrypt), project_id: projectId, output: opts.output ? resolve14(opts.output) : null, stats: bundle.stats } });
32079
32214
  if (!opts.encrypt && !opts.allowPlaintextSensitive) {
32080
32215
  console.error(chalk5.dim("Bridge export redacted sensitive fields. Use --encrypt for an encrypted local bundle when a lossless legacy snapshot is required."));
32081
32216
  }
32082
32217
  if (opts.output && !globalOpts.json) {
32083
- console.log(chalk5.green(`${opts.encrypt ? "Encrypted bridge export" : "Bridge export"} written to ${resolve13(opts.output)}`));
32218
+ console.log(chalk5.green(`${opts.encrypt ? "Encrypted bridge export" : "Bridge export"} written to ${resolve14(opts.output)}`));
32084
32219
  }
32085
32220
  return;
32086
32221
  }
@@ -32093,7 +32228,7 @@ Indexed ${result.index.files.length} file(s), ${result.index.total_symbols} symb
32093
32228
  await writeOutput(JSON.stringify(tasks, null, 2));
32094
32229
  }
32095
32230
  const { emitLocalEventHooksQuiet: emitLocalEventHooksQuiet2 } = await Promise.resolve().then(() => (init_event_hooks(), exports_event_hooks));
32096
- emitLocalEventHooksQuiet2({ type: "export.finished", payload: { format: opts.format, project_id: projectId, output: opts.output ? resolve13(opts.output) : null, count: exportedCount } });
32231
+ emitLocalEventHooksQuiet2({ type: "export.finished", payload: { format: opts.format, project_id: projectId, output: opts.output ? resolve14(opts.output) : null, count: exportedCount } });
32097
32232
  });
32098
32233
  program2.command("bridge-import <file>").description("Dry-run or apply a local hasna/todos bridge export bundle").option("--apply", "Apply the import. Defaults to dry-run.").option("--decrypt", "Decrypt an encrypted bridge export before importing").option("--resolve-conflicts", "Safely merge existing local tasks by filling blank fields, unioning tags, and recording unresolved divergences").action(async (file, opts) => {
32099
32234
  const globalOpts = program2.opts();
@@ -32101,13 +32236,13 @@ Indexed ${result.index.files.length} file(s), ${result.index.total_symbols} symb
32101
32236
  const { readFileSync: readFileSync11 } = await import("fs");
32102
32237
  const { importLocalBridgeBundle: importLocalBridgeBundle2 } = await Promise.resolve().then(() => (init_local_bridge(), exports_local_bridge));
32103
32238
  const { decryptBridgeBundle: decryptBridgeBundle2, isEncryptedBridgeBundle: isEncryptedBridgeBundle2 } = await Promise.resolve().then(() => (init_local_encryption(), exports_local_encryption));
32104
- const parsed = JSON.parse(readFileSync11(resolve13(file), "utf-8"));
32239
+ const parsed = JSON.parse(readFileSync11(resolve14(file), "utf-8"));
32105
32240
  const bundle = isEncryptedBridgeBundle2(parsed) ? opts.decrypt ? decryptBridgeBundle2(parsed) : (() => {
32106
32241
  throw new Error("Bridge bundle is encrypted. Re-run with --decrypt and the configured key environment variable set.");
32107
32242
  })() : parsed;
32108
32243
  const result = importLocalBridgeBundle2(bundle, { dryRun: !opts.apply, conflictStrategy: opts.resolveConflicts ? "safe_merge" : "skip" });
32109
32244
  const { emitLocalEventHooksQuiet: emitLocalEventHooksQuiet2 } = await Promise.resolve().then(() => (init_event_hooks(), exports_event_hooks));
32110
- emitLocalEventHooksQuiet2({ type: "import.finished", payload: { file: resolve13(file), dry_run: result.dry_run, ok: result.ok, inserted: result.inserted, skipped: result.skipped, conflicts: result.conflicts.length, issues: result.issues.length } });
32245
+ emitLocalEventHooksQuiet2({ type: "import.finished", payload: { file: resolve14(file), dry_run: result.dry_run, ok: result.ok, inserted: result.inserted, skipped: result.skipped, conflicts: result.conflicts.length, issues: result.issues.length } });
32111
32246
  if (globalOpts.json) {
32112
32247
  output(result, true);
32113
32248
  return;
@@ -32137,9 +32272,9 @@ Indexed ${result.index.files.length} file(s), ${result.index.total_symbols} symb
32137
32272
  try {
32138
32273
  const { readFileSync: readFileSync11 } = await import("fs");
32139
32274
  const { importTodosMarkdown: importTodosMarkdown2 } = await Promise.resolve().then(() => (init_todos_md(), exports_todos_md));
32140
- const result = importTodosMarkdown2(readFileSync11(resolve13(file), "utf-8"), { dryRun: !opts.apply, conflictStrategy: opts.resolveConflicts ? "safe_merge" : "skip" });
32275
+ const result = importTodosMarkdown2(readFileSync11(resolve14(file), "utf-8"), { dryRun: !opts.apply, conflictStrategy: opts.resolveConflicts ? "safe_merge" : "skip" });
32141
32276
  const { emitLocalEventHooksQuiet: emitLocalEventHooksQuiet2 } = await Promise.resolve().then(() => (init_event_hooks(), exports_event_hooks));
32142
- emitLocalEventHooksQuiet2({ type: "import.finished", payload: { file: resolve13(file), format: "todos.md", dry_run: result.dry_run, ok: result.ok, inserted: result.inserted, skipped: result.skipped, issues: result.issues.length } });
32277
+ emitLocalEventHooksQuiet2({ type: "import.finished", payload: { file: resolve14(file), format: "todos.md", dry_run: result.dry_run, ok: result.ok, inserted: result.inserted, skipped: result.skipped, issues: result.issues.length } });
32143
32278
  if (globalOpts.json) {
32144
32279
  output(result, true);
32145
32280
  return;
@@ -35272,7 +35407,7 @@ async function retryOnTransientPostgresError(fn, attempts = 2, delayMs = 150) {
35272
35407
  lastError = error;
35273
35408
  if (!isTransientPostgresError(error) || attempt === attempts)
35274
35409
  throw error;
35275
- await new Promise((resolve14) => setTimeout(resolve14, delayMs * attempt));
35410
+ await new Promise((resolve15) => setTimeout(resolve15, delayMs * attempt));
35276
35411
  }
35277
35412
  }
35278
35413
  throw lastError;
@@ -36401,8 +36536,8 @@ class SqliteTodosProjectRegistrationBackend {
36401
36536
  async transaction(fn) {
36402
36537
  const previous = sqliteTransactionTails.get(this.db) ?? Promise.resolve();
36403
36538
  let release;
36404
- const current = new Promise((resolve14) => {
36405
- release = resolve14;
36539
+ const current = new Promise((resolve15) => {
36540
+ release = resolve15;
36406
36541
  });
36407
36542
  sqliteTransactionTails.set(this.db, current);
36408
36543
  await previous;
@@ -39165,8 +39300,8 @@ var init_ai = __esm(() => {
39165
39300
  });
39166
39301
 
39167
39302
  // src/lib/access-profiles.ts
39168
- function resolveAccessProfile(envValue) {
39169
- const raw = (envValue ?? process.env["TODOS_PROFILE"] ?? "full").toLowerCase();
39303
+ function resolveAccessProfile(envValue2) {
39304
+ const raw = (envValue2 ?? process.env["TODOS_PROFILE"] ?? "full").toLowerCase();
39170
39305
  if (ACCESS_PROFILES.includes(raw))
39171
39306
  return raw;
39172
39307
  if (raw === "readonly")
@@ -40791,7 +40926,7 @@ function errorResult(runId, error) {
40791
40926
  return createTodosAiFailureResult(runId, "internal_error", "optional AI runtime failed");
40792
40927
  }
40793
40928
  async function withLoadDeadline(operation, controller, timeoutMs) {
40794
- return new Promise((resolve14, reject) => {
40929
+ return new Promise((resolve15, reject) => {
40795
40930
  let settled = false;
40796
40931
  const settle = (callback) => {
40797
40932
  if (settled)
@@ -40805,11 +40940,11 @@ async function withLoadDeadline(operation, controller, timeoutMs) {
40805
40940
  controller.abort(error);
40806
40941
  settle(() => reject(error));
40807
40942
  }, timeoutMs);
40808
- Promise.resolve().then(operation).then((value) => settle(() => resolve14(value)), (error) => settle(() => reject(error)));
40943
+ Promise.resolve().then(operation).then((value) => settle(() => resolve15(value)), (error) => settle(() => reject(error)));
40809
40944
  });
40810
40945
  }
40811
40946
  async function withInterrupt(operation, controller) {
40812
- return new Promise((resolve14, reject) => {
40947
+ return new Promise((resolve15, reject) => {
40813
40948
  let settled = false;
40814
40949
  const settle = (callback) => {
40815
40950
  if (settled)
@@ -40824,7 +40959,7 @@ async function withInterrupt(operation, controller) {
40824
40959
  settle(() => reject(error));
40825
40960
  };
40826
40961
  process.once("SIGINT", onInterrupt);
40827
- Promise.resolve().then(operation).then((value) => settle(() => resolve14(value)), (error) => settle(() => reject(error)));
40962
+ Promise.resolve().then(operation).then((value) => settle(() => resolve15(value)), (error) => settle(() => reject(error)));
40828
40963
  });
40829
40964
  }
40830
40965
  function throwIfAborted(signal) {
@@ -41112,14 +41247,14 @@ async function findFreePort(start) {
41112
41247
  var DEFAULT_PORT = 19427;
41113
41248
 
41114
41249
  // src/lib/db-backup.ts
41115
- import { existsSync as existsSync15, copyFileSync, mkdirSync as mkdirSync8, readFileSync as readFileSync12, renameSync, statSync as statSync7, writeFileSync as writeFileSync7, unlinkSync } from "fs";
41116
- import { dirname as dirname7, join as join14, resolve as resolve14 } from "path";
41250
+ import { existsSync as existsSync16, copyFileSync, mkdirSync as mkdirSync8, readFileSync as readFileSync12, renameSync, statSync as statSync7, writeFileSync as writeFileSync7, unlinkSync } from "fs";
41251
+ import { dirname as dirname7, join as join16, resolve as resolve15 } from "path";
41117
41252
  import { Database as Database2 } from "bun:sqlite";
41118
41253
  function resolveDbPath(dbPath) {
41119
41254
  if (dbPath)
41120
- return resolve14(dbPath);
41255
+ return resolve15(dbPath);
41121
41256
  if (process.env["TODOS_DB_PATH"] && process.env["TODOS_DB_PATH"] !== ":memory:") {
41122
- return resolve14(process.env["TODOS_DB_PATH"]);
41257
+ return resolve15(process.env["TODOS_DB_PATH"]);
41123
41258
  }
41124
41259
  const db = getDatabase();
41125
41260
  const filename = db.filename;
@@ -41129,7 +41264,7 @@ function resolveDbPath(dbPath) {
41129
41264
  }
41130
41265
  function backupDatabase(outputPath, sourcePath) {
41131
41266
  const source = resolveDbPath(sourcePath);
41132
- if (!existsSync15(source))
41267
+ if (!existsSync16(source))
41133
41268
  throw new Error(`Database not found: ${source}`);
41134
41269
  mkdirSync8(dirname7(outputPath), { recursive: true });
41135
41270
  closeDatabase();
@@ -41152,9 +41287,9 @@ function backupDatabase(outputPath, sourcePath) {
41152
41287
  };
41153
41288
  }
41154
41289
  function checkDatabaseIntegrity(dbPath) {
41155
- const path = dbPath ? resolve14(dbPath) : resolveDbPath();
41290
+ const path = dbPath ? resolve15(dbPath) : resolveDbPath();
41156
41291
  const errors = [];
41157
- if (!existsSync15(path)) {
41292
+ if (!existsSync16(path)) {
41158
41293
  return {
41159
41294
  schema_version: DB_BACKUP_SCHEMA,
41160
41295
  path,
@@ -41217,9 +41352,9 @@ function checkDatabaseIntegrity(dbPath) {
41217
41352
  };
41218
41353
  }
41219
41354
  function defaultBackupPath(dbPath) {
41220
- const base = dbPath ? dirname7(resolve14(dbPath)) : dirname7(resolveDbPath());
41355
+ const base = dbPath ? dirname7(resolve15(dbPath)) : dirname7(resolveDbPath());
41221
41356
  const stamp = new Date().toISOString().replace(/[:.]/g, "-");
41222
- return join14(base, "backups", `todos-${stamp}.db`);
41357
+ return join16(base, "backups", `todos-${stamp}.db`);
41223
41358
  }
41224
41359
  var DB_BACKUP_SCHEMA = "todos.db_backup.v1";
41225
41360
  var init_db_backup = __esm(() => {
@@ -41236,7 +41371,7 @@ __export(exports_evidence_redaction, {
41236
41371
  TODOS_EVIDENCE_REDACTION_CONFIRM: () => TODOS_EVIDENCE_REDACTION_CONFIRM
41237
41372
  });
41238
41373
  import { chmodSync } from "fs";
41239
- import { join as join15 } from "path";
41374
+ import { join as join17 } from "path";
41240
41375
  function unique4(values) {
41241
41376
  return Array.from(new Set((values ?? []).map((value) => value.trim()).filter(Boolean)));
41242
41377
  }
@@ -41583,7 +41718,7 @@ function redactEvidenceRows(options, db) {
41583
41718
  }
41584
41719
  function defaultEvidenceRedactionBackupPath(dbPath) {
41585
41720
  const base = defaultBackupPath(dbPath);
41586
- return join15(base.replace(/\.db$/, ""), "pre-redaction.db");
41721
+ return join17(base.replace(/\.db$/, ""), "pre-redaction.db");
41587
41722
  }
41588
41723
  var TODOS_EVIDENCE_REDACTION_SCHEMA = "todos.evidence_redaction.v1", TODOS_EVIDENCE_REDACTION_CONFIRM = "REDACT_TODOS_EVIDENCE";
41589
41724
  var init_evidence_redaction = __esm(() => {
@@ -41599,7 +41734,7 @@ __export(exports_retention_cleanup, {
41599
41734
  applyRetentionCleanup: () => applyRetentionCleanup,
41600
41735
  RETENTION_CLEANUP_CONFIRMATION: () => RETENTION_CLEANUP_CONFIRMATION
41601
41736
  });
41602
- import { existsSync as existsSync16, unlinkSync as unlinkSync2 } from "fs";
41737
+ import { existsSync as existsSync17, unlinkSync as unlinkSync2 } from "fs";
41603
41738
  function normalizeScopes(scopes) {
41604
41739
  if (!scopes || scopes.length === 0)
41605
41740
  return [...ALL_SCOPES];
@@ -41802,7 +41937,7 @@ function applyRetentionCleanup(input, db) {
41802
41937
  for (const artifact of report.candidates.artifact_files) {
41803
41938
  try {
41804
41939
  const path = artifactStorePath(artifact.relative_path);
41805
- if (!existsSync16(path)) {
41940
+ if (!existsSync17(path)) {
41806
41941
  report.warnings.push(`stored artifact already missing: ${artifact.relative_path}`);
41807
41942
  continue;
41808
41943
  }
@@ -42478,8 +42613,8 @@ __export(exports_local_extensions, {
42478
42613
  discoverLocalExtensions: () => discoverLocalExtensions
42479
42614
  });
42480
42615
  import { createHash as createHash13, createVerify } from "crypto";
42481
- import { existsSync as existsSync17, readdirSync as readdirSync3, readFileSync as readFileSync13, statSync as statSync8 } from "fs";
42482
- import { basename as basename6, join as join16, resolve as resolve15 } from "path";
42616
+ import { existsSync as existsSync18, readdirSync as readdirSync3, readFileSync as readFileSync13, statSync as statSync8 } from "fs";
42617
+ import { basename as basename6, join as join18, resolve as resolve16 } from "path";
42483
42618
  function isObject(value) {
42484
42619
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
42485
42620
  }
@@ -42737,11 +42872,11 @@ function verifyExtensionSignature(input) {
42737
42872
  return verifier.verify(input.public_key, decodeSignature(input.signature));
42738
42873
  }
42739
42874
  function inspectExtensionSource(source2) {
42740
- const resolved = resolve15(source2);
42741
- if (!existsSync17(resolved))
42875
+ const resolved = resolve16(source2);
42876
+ if (!existsSync18(resolved))
42742
42877
  throw new Error(`extension source not found: ${source2}`);
42743
42878
  const stat = statSync8(resolved);
42744
- const manifestPath = stat.isDirectory() ? [join16(resolved, "todos.extension.json"), join16(resolved, "extension.json")].find(existsSync17) : resolved;
42879
+ const manifestPath = stat.isDirectory() ? [join18(resolved, "todos.extension.json"), join18(resolved, "extension.json")].find(existsSync18) : resolved;
42745
42880
  if (!manifestPath)
42746
42881
  throw new Error(`extension directory ${source2} is missing todos.extension.json`);
42747
42882
  const raw = readFileSync13(manifestPath);
@@ -42835,26 +42970,26 @@ function testExtensionCompatibility(sourceOrManifest) {
42835
42970
  function projectExtensionSources(projectPath) {
42836
42971
  if (!projectPath)
42837
42972
  return [];
42838
- const root = resolve15(projectPath);
42973
+ const root = resolve16(projectPath);
42839
42974
  const candidates = [
42840
- join16(root, "todos.extension.json"),
42841
- join16(root, ".todos", "todos.extension.json")
42975
+ join18(root, "todos.extension.json"),
42976
+ join18(root, ".todos", "todos.extension.json")
42842
42977
  ];
42843
- const extensionDir = join16(root, ".todos", "extensions");
42844
- if (existsSync17(extensionDir)) {
42978
+ const extensionDir = join18(root, ".todos", "extensions");
42979
+ if (existsSync18(extensionDir)) {
42845
42980
  for (const entry2 of readdirSync3(extensionDir)) {
42846
42981
  if (entry2.startsWith("."))
42847
42982
  continue;
42848
- const full = join16(extensionDir, entry2);
42983
+ const full = join18(extensionDir, entry2);
42849
42984
  if (statSync8(full).isDirectory() || entry2.endsWith(".json"))
42850
42985
  candidates.push(full);
42851
42986
  }
42852
42987
  }
42853
- return candidates.filter(existsSync17);
42988
+ return candidates.filter(existsSync18);
42854
42989
  }
42855
42990
  function discoverLocalExtensions(options = {}) {
42856
42991
  const config = loadConfig();
42857
- const projectPath = options.project_path ? resolve15(options.project_path) : null;
42992
+ const projectPath = options.project_path ? resolve16(options.project_path) : null;
42858
42993
  const configuredSources = [
42859
42994
  ...config.extension_sources || [],
42860
42995
  ...projectPath ? config.project_overrides?.[projectPath]?.extension_sources || [] : []
@@ -42862,7 +42997,7 @@ function discoverLocalExtensions(options = {}) {
42862
42997
  const sources = Array.from(new Set([
42863
42998
  ...configuredSources,
42864
42999
  ...projectExtensionSources(projectPath || undefined)
42865
- ])).map((source2) => projectPath && !source2.startsWith("/") ? resolve15(projectPath, source2) : resolve15(source2));
43000
+ ])).map((source2) => projectPath && !source2.startsWith("/") ? resolve16(projectPath, source2) : resolve16(source2));
42866
43001
  const warnings = [];
42867
43002
  const discovered = [];
42868
43003
  for (const source2 of sources) {
@@ -43122,9 +43257,9 @@ __export(exports_policy_packs, {
43122
43257
  getPolicyPack: () => getPolicyPack,
43123
43258
  explainPolicyPack: () => explainPolicyPack
43124
43259
  });
43125
- import { relative as relative4, resolve as resolve16 } from "path";
43260
+ import { relative as relative4, resolve as resolve17 } from "path";
43126
43261
  function normalizePath3(path) {
43127
- return resolve16(path);
43262
+ return resolve17(path);
43128
43263
  }
43129
43264
  function unique6(values) {
43130
43265
  return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
@@ -43179,7 +43314,7 @@ function commandMatches(commands, pattern) {
43179
43314
  }
43180
43315
  function pathMatches(paths, pattern, root) {
43181
43316
  return paths.filter((path) => {
43182
- const candidate = path.startsWith("/") ? path : resolve16(root, path);
43317
+ const candidate = path.startsWith("/") ? path : resolve17(root, path);
43183
43318
  if (!isPathInside3(root, candidate))
43184
43319
  return matchesPattern3(path, pattern);
43185
43320
  return matchesPattern3(path, pattern) || matchesPattern3(relative4(root, candidate), pattern);
@@ -48923,8 +49058,8 @@ class SqliteTodosTaskManifestBackend {
48923
49058
  async serialized(run) {
48924
49059
  const previous = sqliteTails.get(this.db) ?? Promise.resolve();
48925
49060
  let release;
48926
- const current = new Promise((resolve17) => {
48927
- release = resolve17;
49061
+ const current = new Promise((resolve18) => {
49062
+ release = resolve18;
48928
49063
  });
48929
49064
  const tail = previous.then(() => current);
48930
49065
  sqliteTails.set(this.db, tail);
@@ -51267,8 +51402,8 @@ class SqliteTodosTaskSubtreeTransferBackend {
51267
51402
  async serialized(run) {
51268
51403
  const previous = sqliteTails2.get(this.db) ?? Promise.resolve();
51269
51404
  let release;
51270
- const current = new Promise((resolve17) => {
51271
- release = resolve17;
51405
+ const current = new Promise((resolve18) => {
51406
+ release = resolve18;
51272
51407
  });
51273
51408
  const tail = previous.then(() => current);
51274
51409
  sqliteTails2.set(this.db, tail);
@@ -52703,8 +52838,8 @@ var exports_doctor = {};
52703
52838
  __export(exports_doctor, {
52704
52839
  runTodosDoctor: () => runTodosDoctor
52705
52840
  });
52706
- import { chmodSync as chmodSync2, copyFileSync as copyFileSync2, existsSync as existsSync18, mkdirSync as mkdirSync9, statSync as statSync9 } from "fs";
52707
- import { basename as basename7, dirname as dirname8, join as join17 } from "path";
52841
+ import { chmodSync as chmodSync2, copyFileSync as copyFileSync2, existsSync as existsSync19, mkdirSync as mkdirSync9, statSync as statSync9 } from "fs";
52842
+ import { basename as basename7, dirname as dirname8, join as join19 } from "path";
52708
52843
  function tableExists3(db, table) {
52709
52844
  return Boolean(db.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(table));
52710
52845
  }
@@ -52798,7 +52933,7 @@ function findMissingProjectRoots(db) {
52798
52933
  continue;
52799
52934
  if (!row.path.startsWith("/"))
52800
52935
  continue;
52801
- if (!existsSync18(row.path))
52936
+ if (!existsSync19(row.path))
52802
52937
  missing++;
52803
52938
  }
52804
52939
  return missing;
@@ -52858,16 +52993,16 @@ function databasePermissionsAreUnsafe(dbPath) {
52858
52993
  function createBackup(dbPath) {
52859
52994
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
52860
52995
  return;
52861
- if (!existsSync18(dbPath))
52996
+ if (!existsSync19(dbPath))
52862
52997
  return;
52863
52998
  const stamp = now().replace(/[:.]/g, "-");
52864
- const backupDir = join17(dirname8(dbPath), `${basename7(dbPath)}.backup-${stamp}`);
52999
+ const backupDir = join19(dirname8(dbPath), `${basename7(dbPath)}.backup-${stamp}`);
52865
53000
  const files = [];
52866
53001
  mkdirSync9(backupDir, { recursive: true });
52867
53002
  for (const source2 of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
52868
- if (!existsSync18(source2))
53003
+ if (!existsSync19(source2))
52869
53004
  continue;
52870
- const target = join17(backupDir, basename7(source2));
53005
+ const target = join19(backupDir, basename7(source2));
52871
53006
  copyFileSync2(source2, target);
52872
53007
  files.push(target);
52873
53008
  }
@@ -53126,7 +53261,7 @@ var init_doctor = __esm(() => {
53126
53261
  });
53127
53262
 
53128
53263
  // src/server/routes.ts
53129
- import { join as join18, resolve as resolve17, sep as sep4 } from "path";
53264
+ import { join as join20, resolve as resolve18, sep as sep4 } from "path";
53130
53265
  function parseFieldsParam(url) {
53131
53266
  const fieldsParam = url.searchParams.get("fields");
53132
53267
  return fieldsParam ? fieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
@@ -53963,9 +54098,9 @@ function handleStaticFiles(path, method, ctx, json5, serveStaticFile2) {
53963
54098
  if (!ctx.dashboardExists || method !== "GET" && method !== "HEAD")
53964
54099
  return null;
53965
54100
  if (path !== "/") {
53966
- const filePath = join18(ctx.dashboardDir, path);
53967
- const resolvedFile = resolve17(filePath);
53968
- const resolvedBase = resolve17(ctx.dashboardDir);
54101
+ const filePath = join20(ctx.dashboardDir, path);
54102
+ const resolvedFile = resolve18(filePath);
54103
+ const resolvedBase = resolve18(ctx.dashboardDir);
53969
54104
  if (!resolvedFile.startsWith(resolvedBase + sep4) && resolvedFile !== resolvedBase) {
53970
54105
  return json5({ error: "Forbidden" }, 403);
53971
54106
  }
@@ -53973,7 +54108,7 @@ function handleStaticFiles(path, method, ctx, json5, serveStaticFile2) {
53973
54108
  if (res2)
53974
54109
  return res2;
53975
54110
  }
53976
- const indexPath = join18(ctx.dashboardDir, "index.html");
54111
+ const indexPath = join20(ctx.dashboardDir, "index.html");
53977
54112
  const res = serveStaticFile2(indexPath);
53978
54113
  if (res)
53979
54114
  return res;
@@ -54080,7 +54215,7 @@ class TodosShadowOutbox {
54080
54215
  const remaining = deadline - Date.now();
54081
54216
  if (remaining <= 0)
54082
54217
  break;
54083
- await new Promise((resolve18) => setTimeout(resolve18, Math.min(200, remaining)));
54218
+ await new Promise((resolve19) => setTimeout(resolve19, Math.min(200, remaining)));
54084
54219
  }
54085
54220
  }
54086
54221
  return this.getStats();
@@ -60972,8 +61107,8 @@ var exports_mention_resolver = {};
60972
61107
  __export(exports_mention_resolver, {
60973
61108
  resolveMentions: () => resolveMentions
60974
61109
  });
60975
- import { existsSync as existsSync19, readdirSync as readdirSync4, readFileSync as readFileSync14, statSync as statSync10 } from "fs";
60976
- import { basename as basename8, isAbsolute, join as join19, relative as relative5, resolve as resolve18, sep as sep5 } from "path";
61110
+ import { existsSync as existsSync20, readdirSync as readdirSync4, readFileSync as readFileSync14, statSync as statSync10 } from "fs";
61111
+ import { basename as basename8, isAbsolute, join as join21, relative as relative5, resolve as resolve19, sep as sep5 } from "path";
60977
61112
  function blankResolution(parsed) {
60978
61113
  return {
60979
61114
  input: parsed.input,
@@ -60996,7 +61131,7 @@ function backlink(kind, key2, label, target = key2) {
60996
61131
  return { kind, key: key2, label, target };
60997
61132
  }
60998
61133
  function normalizeWorkspace(workspace) {
60999
- return resolve18(workspace || process.cwd());
61134
+ return resolve19(workspace || process.cwd());
61000
61135
  }
61001
61136
  function isInside(root, absolutePath) {
61002
61137
  const rel = relative5(root, absolutePath);
@@ -61064,14 +61199,14 @@ function resolveFile(parsed, workspace) {
61064
61199
  resolution.warnings.push("path is empty or escapes the workspace");
61065
61200
  return resolution;
61066
61201
  }
61067
- const absolutePath = resolve18(workspace, relPath);
61202
+ const absolutePath = resolve19(workspace, relPath);
61068
61203
  if (!isInside(workspace, absolutePath)) {
61069
61204
  resolution.path = relPath;
61070
61205
  resolution.warnings.push("path escapes the workspace");
61071
61206
  return resolution;
61072
61207
  }
61073
61208
  resolution.path = relPath;
61074
- if (!existsSync19(absolutePath)) {
61209
+ if (!existsSync20(absolutePath)) {
61075
61210
  resolution.warnings.push("file does not exist in the local workspace");
61076
61211
  return resolution;
61077
61212
  }
@@ -61104,7 +61239,7 @@ function walkSourceFiles(root, current = root, files = []) {
61104
61239
  if (SKIP_DIRS2.has(entry2.name))
61105
61240
  continue;
61106
61241
  }
61107
- const absolutePath = join19(current, entry2.name);
61242
+ const absolutePath = join21(current, entry2.name);
61108
61243
  if (entry2.isDirectory()) {
61109
61244
  if (!SKIP_DIRS2.has(entry2.name))
61110
61245
  walkSourceFiles(root, absolutePath, files);
@@ -63920,7 +64055,7 @@ __export(exports_release_compatibility, {
63920
64055
  LOCAL_RELEASE_COMPATIBILITY_SCHEMA_VERSION: () => LOCAL_RELEASE_COMPATIBILITY_SCHEMA_VERSION
63921
64056
  });
63922
64057
  import { readFileSync as readFileSync15 } from "fs";
63923
- import { join as join20, resolve as resolve19 } from "path";
64058
+ import { join as join22, resolve as resolve20 } from "path";
63924
64059
  import { Database as Database3 } from "bun:sqlite";
63925
64060
  function pass(id, message, details) {
63926
64061
  return { id, status: "passed", message, details };
@@ -63932,7 +64067,7 @@ function warn(id, message, details) {
63932
64067
  return { id, status: "warning", message, details };
63933
64068
  }
63934
64069
  function readPackageJson2(root) {
63935
- return JSON.parse(readFileSync15(join20(root, "package.json"), "utf8"));
64070
+ return JSON.parse(readFileSync15(join22(root, "package.json"), "utf8"));
63936
64071
  }
63937
64072
  function sortedKeys(value) {
63938
64073
  return Object.keys(value ?? {}).sort((left, right) => left.localeCompare(right));
@@ -64028,7 +64163,7 @@ function checkChangelog() {
64028
64163
  ];
64029
64164
  }
64030
64165
  function createReleaseCompatibilityReport(options = {}) {
64031
- const root = resolve19(options.root ?? process.cwd());
64166
+ const root = resolve20(options.root ?? process.cwd());
64032
64167
  const packageJson = readPackageJson2(root);
64033
64168
  const simulatedLevels = options.simulated_levels ?? defaultSimulationLevels();
64034
64169
  const checks = [
@@ -69903,7 +70038,7 @@ __export(exports_verification_providers, {
69903
70038
  getVerificationRecord: () => getVerificationRecord,
69904
70039
  discoverVerificationProviderCapabilities: () => discoverVerificationProviderCapabilities
69905
70040
  });
69906
- import { existsSync as existsSync20, readFileSync as readFileSync16 } from "fs";
70041
+ import { existsSync as existsSync21, readFileSync as readFileSync16 } from "fs";
69907
70042
  function normalizeName6(name) {
69908
70043
  const normalized = name.trim().toLowerCase();
69909
70044
  if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)) {
@@ -70000,7 +70135,7 @@ function classifyLog(text) {
70000
70135
  async function sleep3(ms) {
70001
70136
  if (ms <= 0)
70002
70137
  return;
70003
- await new Promise((resolve20) => setTimeout(resolve20, ms));
70138
+ await new Promise((resolve21) => setTimeout(resolve21, ms));
70004
70139
  }
70005
70140
  async function runCommandProvider(provider, input) {
70006
70141
  const commandTemplate = input.command || provider.command;
@@ -70055,7 +70190,7 @@ Timed out after ${provider.timeout_ms}ms`);
70055
70190
  };
70056
70191
  }
70057
70192
  function runCiLogProvider(input) {
70058
- const text = input.log_text ?? (input.log_path && existsSync20(input.log_path) ? readFileSync16(input.log_path, "utf-8") : "");
70193
+ const text = input.log_text ?? (input.log_path && existsSync21(input.log_path) ? readFileSync16(input.log_path, "utf-8") : "");
70059
70194
  return {
70060
70195
  status: classifyLog(text),
70061
70196
  attempts: 1,
@@ -70067,7 +70202,7 @@ function runBrowserProvider(input) {
70067
70202
  if (!input.artifact_path) {
70068
70203
  return { status: "unknown", attempts: 1, exit_code: null, output_summary: "browser provider needs a screenshot or artifact path" };
70069
70204
  }
70070
- if (!existsSync20(input.artifact_path)) {
70205
+ if (!existsSync21(input.artifact_path)) {
70071
70206
  return { status: "failed", attempts: 1, exit_code: null, output_summary: `artifact not found: ${input.artifact_path}` };
70072
70207
  }
70073
70208
  return {
@@ -72359,7 +72494,7 @@ __export(exports_local_backups, {
72359
72494
  });
72360
72495
  import { createHash as createHash16 } from "crypto";
72361
72496
  import { readFileSync as readFileSync17, writeFileSync as writeFileSync8 } from "fs";
72362
- import { dirname as dirname9, resolve as resolve20 } from "path";
72497
+ import { dirname as dirname9, resolve as resolve21 } from "path";
72363
72498
  import { mkdirSync as mkdirSync10 } from "fs";
72364
72499
  function stableJson2(value) {
72365
72500
  if (value === null || typeof value !== "object")
@@ -72461,14 +72596,14 @@ function createLocalBackup(options = {}, db) {
72461
72596
  return backup;
72462
72597
  }
72463
72598
  function writeLocalBackupFile(backup, outputPath) {
72464
- const path = resolve20(outputPath);
72599
+ const path = resolve21(outputPath);
72465
72600
  mkdirSync10(dirname9(path), { recursive: true });
72466
72601
  writeFileSync8(path, `${JSON.stringify(backup, null, 2)}
72467
72602
  `);
72468
72603
  return path;
72469
72604
  }
72470
72605
  function readLocalBackupFile(path) {
72471
- return JSON.parse(readFileSync17(resolve20(path), "utf-8"));
72606
+ return JSON.parse(readFileSync17(resolve21(path), "utf-8"));
72472
72607
  }
72473
72608
  function verifyLocalBackup(value, options = {}, db) {
72474
72609
  const verifiedAt = options.verified_at ?? now();
@@ -72665,7 +72800,7 @@ __export(exports_onboarding_fixtures, {
72665
72800
  TODOS_ONBOARDING_FIXTURE_LIBRARY_VERSION: () => TODOS_ONBOARDING_FIXTURE_LIBRARY_VERSION
72666
72801
  });
72667
72802
  import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync9 } from "fs";
72668
- import { join as join21 } from "path";
72803
+ import { join as join23 } from "path";
72669
72804
  function emptyData() {
72670
72805
  return {
72671
72806
  projects: [],
@@ -73001,7 +73136,7 @@ function writeOnboardingFixtureFiles(directory) {
73001
73136
  mkdirSync11(directory, { recursive: true });
73002
73137
  const files = [];
73003
73138
  for (const fixture of allFixtures()) {
73004
- const path = join21(directory, `${fixture.summary.name}.bridge.json`);
73139
+ const path = join23(directory, `${fixture.summary.name}.bridge.json`);
73005
73140
  writeFileSync9(path, `${JSON.stringify(fixture.bundle, null, 2)}
73006
73141
  `, "utf-8");
73007
73142
  files.push(path);
@@ -78669,7 +78804,7 @@ Last seen: ${agent.last_seen_at}`
78669
78804
  `Suggested names: ${suggestions.slice(0, 8).join(", ")}`,
78670
78805
  allActive.length > 0 ? `Active agents (avoid these names): ${allActive.map((a) => `${a.name} (seen ${Math.round((Date.now() - new Date(a.last_seen_at).getTime()) / 60000)}m ago)`).join(", ")}` : "No active agents.",
78671
78806
  `
78672
- To restrict names, configure agent_pool or project_pools in ~/.hasna/todos/config.json`
78807
+ To restrict names, configure agent_pool or project_pools in the todos data home config file (default <data home>/config.json)`
78673
78808
  ];
78674
78809
  return { content: [{ type: "text", text: lines2.join(`
78675
78810
  `) }] };
@@ -79221,16 +79356,16 @@ __export(exports_environment_snapshots, {
79221
79356
  captureEnvironmentSnapshot: () => captureEnvironmentSnapshot
79222
79357
  });
79223
79358
  import { createHash as createHash20 } from "crypto";
79224
- import { existsSync as existsSync21, readFileSync as readFileSync19, statSync as statSync11 } from "fs";
79359
+ import { existsSync as existsSync22, readFileSync as readFileSync19, statSync as statSync11 } from "fs";
79225
79360
  import { hostname as hostname2, platform, arch } from "os";
79226
- import { dirname as dirname10, join as join22, resolve as resolve21 } from "path";
79361
+ import { dirname as dirname10, join as join24, resolve as resolve22 } from "path";
79227
79362
  import { tmpdir as tmpdir4 } from "os";
79228
79363
  function sha2567(value) {
79229
79364
  return createHash20("sha256").update(value).digest("hex");
79230
79365
  }
79231
79366
  function fileRecord(root, relativePath) {
79232
- const path = join22(root, relativePath);
79233
- if (!existsSync21(path))
79367
+ const path = join24(root, relativePath);
79368
+ if (!existsSync22(path))
79234
79369
  return null;
79235
79370
  const stat = statSync11(path);
79236
79371
  if (!stat.isFile())
@@ -79242,7 +79377,7 @@ function manifestRecord(root, relativePath) {
79242
79377
  const base = fileRecord(root, relativePath);
79243
79378
  if (!base)
79244
79379
  return null;
79245
- const parsed = readJsonFile(join22(root, relativePath));
79380
+ const parsed = readJsonFile(join24(root, relativePath));
79246
79381
  if (!parsed)
79247
79382
  return { ...base, redacted: {} };
79248
79383
  const redacted = redactValue({
@@ -79337,15 +79472,15 @@ function commandEnv(env, includeValues) {
79337
79472
  function defaultSnapshotDir() {
79338
79473
  const dbPath = getDatabasePath();
79339
79474
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
79340
- return join22(tmpdir4(), "hasna-todos", "environment-snapshots");
79341
- return join22(dirname10(resolve21(dbPath)), "environment-snapshots");
79475
+ return join24(tmpdir4(), "hasna-todos", "environment-snapshots");
79476
+ return join24(dirname10(resolve22(dbPath)), "environment-snapshots");
79342
79477
  }
79343
79478
  function snapshotWithId(snapshot) {
79344
79479
  const digest4 = sha2567(JSON.stringify(snapshot)).slice(0, 24);
79345
79480
  return { id: `env_${digest4}`, ...snapshot };
79346
79481
  }
79347
79482
  function captureEnvironmentSnapshot(input = {}) {
79348
- const root = resolve21(input.root || process.cwd());
79483
+ const root = resolve22(input.root || process.cwd());
79349
79484
  const env = input.env || process.env;
79350
79485
  const warnings = [];
79351
79486
  const manifests = MANIFEST_FILES.map((file) => manifestRecord(root, file)).filter((file) => Boolean(file));
@@ -79385,13 +79520,13 @@ function captureEnvironmentSnapshot(input = {}) {
79385
79520
  });
79386
79521
  }
79387
79522
  function writeEnvironmentSnapshot(snapshot, outputPath) {
79388
- const path = outputPath ? resolve21(outputPath) : join22(defaultSnapshotDir(), `${snapshot.id}.json`);
79523
+ const path = outputPath ? resolve22(outputPath) : join24(defaultSnapshotDir(), `${snapshot.id}.json`);
79389
79524
  ensureDir(dirname10(path));
79390
79525
  writeJsonFile(path, snapshot);
79391
79526
  return path;
79392
79527
  }
79393
79528
  function readEnvironmentSnapshot(path) {
79394
- const snapshot = readJsonFile(resolve21(path));
79529
+ const snapshot = readJsonFile(resolve22(path));
79395
79530
  if (!snapshot || snapshot.schema_version !== 1 || typeof snapshot.id !== "string") {
79396
79531
  throw new Error(`Invalid environment snapshot: ${path}`);
79397
79532
  }
@@ -80102,8 +80237,8 @@ class SqlitePrGroupLedgerPersistence {
80102
80237
  async transaction(fn) {
80103
80238
  const previous = sqliteTransactionTails2.get(this.db) ?? Promise.resolve();
80104
80239
  let release;
80105
- const current = new Promise((resolve22) => {
80106
- release = resolve22;
80240
+ const current = new Promise((resolve23) => {
80241
+ release = resolve23;
80107
80242
  });
80108
80243
  sqliteTransactionTails2.set(this.db, current);
80109
80244
  await previous;
@@ -80201,27 +80336,27 @@ __export(exports_serve, {
80201
80336
  SECURITY_HEADERS: () => SECURITY_HEADERS,
80202
80337
  MIME_TYPES: () => MIME_TYPES
80203
80338
  });
80204
- import { existsSync as existsSync22 } from "fs";
80205
- import { join as join23, dirname as dirname11, extname } from "path";
80339
+ import { existsSync as existsSync23 } from "fs";
80340
+ import { join as join25, dirname as dirname11, extname } from "path";
80206
80341
  import { fileURLToPath } from "url";
80207
80342
  function resolveDashboardDir() {
80208
80343
  const candidates = [];
80209
80344
  try {
80210
80345
  const scriptDir = dirname11(fileURLToPath(import.meta.url));
80211
- candidates.push(join23(scriptDir, "..", "dashboard", "dist"));
80212
- candidates.push(join23(scriptDir, "..", "..", "dashboard", "dist"));
80346
+ candidates.push(join25(scriptDir, "..", "dashboard", "dist"));
80347
+ candidates.push(join25(scriptDir, "..", "..", "dashboard", "dist"));
80213
80348
  } catch {}
80214
80349
  if (process.argv[1]) {
80215
80350
  const mainDir = dirname11(process.argv[1]);
80216
- candidates.push(join23(mainDir, "..", "dashboard", "dist"));
80217
- candidates.push(join23(mainDir, "..", "..", "dashboard", "dist"));
80351
+ candidates.push(join25(mainDir, "..", "dashboard", "dist"));
80352
+ candidates.push(join25(mainDir, "..", "..", "dashboard", "dist"));
80218
80353
  }
80219
- candidates.push(join23(process.cwd(), "dashboard", "dist"));
80354
+ candidates.push(join25(process.cwd(), "dashboard", "dist"));
80220
80355
  for (const candidate of candidates) {
80221
- if (existsSync22(candidate))
80356
+ if (existsSync23(candidate))
80222
80357
  return candidate;
80223
80358
  }
80224
- return join23(process.cwd(), "dashboard", "dist");
80359
+ return join25(process.cwd(), "dashboard", "dist");
80225
80360
  }
80226
80361
  function getProvidedApiKey(req) {
80227
80362
  const headerKey = req.headers.get("x-api-key");
@@ -80293,7 +80428,7 @@ function json4(data, status3 = 200, headers) {
80293
80428
  });
80294
80429
  }
80295
80430
  function serveStaticFile(filePath) {
80296
- if (!existsSync22(filePath))
80431
+ if (!existsSync23(filePath))
80297
80432
  return null;
80298
80433
  const ext = extname(filePath);
80299
80434
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
@@ -80394,7 +80529,7 @@ data: ${data}
80394
80529
  filteredSseClients.delete(client);
80395
80530
  }
80396
80531
  const dashboardDir = resolveDashboardDir();
80397
- const dashboardExists = existsSync22(dashboardDir);
80532
+ const dashboardExists = existsSync23(dashboardDir);
80398
80533
  if (!dashboardExists) {
80399
80534
  console.error(`
80400
80535
  Dashboard not found at: ${dashboardDir}`);
@@ -82281,12 +82416,12 @@ __export(exports_config_serve_commands, {
82281
82416
  registerConfigServeCommands: () => registerConfigServeCommands
82282
82417
  });
82283
82418
  import chalk7 from "chalk";
82284
- import { existsSync as existsSync23, mkdirSync as mkdirSync12, readFileSync as readFileSync20, writeFileSync as writeFileSync10 } from "fs";
82285
- import { dirname as dirname12, join as join24 } from "path";
82419
+ import { existsSync as existsSync24, mkdirSync as mkdirSync12, readFileSync as readFileSync20, writeFileSync as writeFileSync10 } from "fs";
82420
+ import { dirname as dirname12, join as join26 } from "path";
82286
82421
  function registerConfigServeCommands(program2) {
82287
82422
  program2.command("config").description("View or update configuration").option("--get <key>", "Get a config value").option("--set <key=value>", "Set a config value (e.g. completion_guard.enabled=true)").action((opts) => {
82288
82423
  const globalOpts = program2.opts();
82289
- const configPath = join24(getTodosGlobalDir(), "config.json");
82424
+ const configPath = join26(getTodosGlobalDir(), "config.json");
82290
82425
  if (opts.get) {
82291
82426
  const config2 = loadConfig();
82292
82427
  const keys = opts.get.split(".");
@@ -82323,7 +82458,7 @@ function registerConfigServeCommands(program2) {
82323
82458
  }
82324
82459
  obj[keys[keys.length - 1]] = parsedValue;
82325
82460
  const dir = dirname12(configPath);
82326
- if (!existsSync23(dir))
82461
+ if (!existsSync24(dir))
82327
82462
  mkdirSync12(dir, { recursive: true });
82328
82463
  writeFileSync10(configPath, JSON.stringify(config2, null, 2));
82329
82464
  if (globalOpts.json) {
@@ -83503,8 +83638,8 @@ __export(exports_routing_doctor, {
83503
83638
  classifyTaskRouting: () => classifyTaskRouting,
83504
83639
  TODOS_ROUTING_DOCTOR_SCHEMA_VERSION: () => TODOS_ROUTING_DOCTOR_SCHEMA_VERSION
83505
83640
  });
83506
- import { copyFileSync as copyFileSync3, existsSync as existsSync24, mkdirSync as mkdirSync13, writeFileSync as writeFileSync11 } from "fs";
83507
- import { basename as basename9, dirname as dirname13, join as join25 } from "path";
83641
+ import { copyFileSync as copyFileSync3, existsSync as existsSync25, mkdirSync as mkdirSync13, writeFileSync as writeFileSync11 } from "fs";
83642
+ import { basename as basename9, dirname as dirname13, join as join27 } from "path";
83508
83643
  function normalizePath5(path) {
83509
83644
  if (!path)
83510
83645
  return null;
@@ -83806,16 +83941,16 @@ function evaluateTaskRouting(ctx) {
83806
83941
  function createBackup2(dbPath, generatedAt) {
83807
83942
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
83808
83943
  return;
83809
- if (!existsSync24(dbPath))
83944
+ if (!existsSync25(dbPath))
83810
83945
  return;
83811
83946
  const stamp = generatedAt.replace(/[:.]/g, "-");
83812
- const backupDir = join25(dirname13(dbPath), `${basename9(dbPath)}.routing-doctor-backup-${stamp}`);
83947
+ const backupDir = join27(dirname13(dbPath), `${basename9(dbPath)}.routing-doctor-backup-${stamp}`);
83813
83948
  const files = [];
83814
83949
  mkdirSync13(backupDir, { recursive: true });
83815
83950
  for (const source3 of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
83816
- if (!existsSync24(source3))
83951
+ if (!existsSync25(source3))
83817
83952
  continue;
83818
- const target = join25(backupDir, basename9(source3));
83953
+ const target = join27(backupDir, basename9(source3));
83819
83954
  copyFileSync3(source3, target);
83820
83955
  files.push(target);
83821
83956
  }
@@ -83919,7 +84054,7 @@ function runRoutingDoctor(options = {}) {
83919
84054
  }
83920
84055
  const applied = repairs.filter((r) => r.applied);
83921
84056
  if (applied.length > 0) {
83922
- const undoPath = options.undoRecordPath ?? join25(process.cwd(), `todos-routing-doctor-undo-${generatedAt.replace(/[:.]/g, "-")}.json`);
84057
+ const undoPath = options.undoRecordPath ?? join27(process.cwd(), `todos-routing-doctor-undo-${generatedAt.replace(/[:.]/g, "-")}.json`);
83923
84058
  const undoRecord = {
83924
84059
  schema_version: TODOS_ROUTING_DOCTOR_SCHEMA_VERSION,
83925
84060
  purpose: "Undo record for routing-doctor --apply. Restore each field with the prior value below.",
@@ -84018,10 +84153,10 @@ __export(exports_task_route_sources, {
84018
84153
  });
84019
84154
  import { Database as Database4 } from "bun:sqlite";
84020
84155
  import { createHash as createHash21 } from "crypto";
84021
- import { existsSync as existsSync25, readdirSync as readdirSync5, statSync as statSync12 } from "fs";
84022
- import { basename as basename10, dirname as dirname14, join as join26, resolve as resolve22 } from "path";
84156
+ import { existsSync as existsSync26, readdirSync as readdirSync5, statSync as statSync12 } from "fs";
84157
+ import { basename as basename10, dirname as dirname14, join as join28, resolve as resolve23 } from "path";
84023
84158
  function normalizePath6(input) {
84024
- return resolve22(input);
84159
+ return resolve23(input);
84025
84160
  }
84026
84161
  function sourceStoreId(sourceDbPath) {
84027
84162
  const digest4 = createHash21("sha256").update(sourceDbPath).digest("hex").slice(0, 16);
@@ -84083,8 +84218,8 @@ function discoverStoresUnderRoot(sourceRoot) {
84083
84218
  const rootPath = normalizePath6(sourceRoot);
84084
84219
  const errors2 = [];
84085
84220
  const stores = [];
84086
- if (!existsSync25(rootPath)) {
84087
- const ref = createStoreRef(join26(rootPath, TODO_STORE_RELATIVE_PATH));
84221
+ if (!existsSync26(rootPath)) {
84222
+ const ref = createStoreRef(join28(rootPath, TODO_STORE_RELATIVE_PATH));
84088
84223
  errors2.push({
84089
84224
  ...ref,
84090
84225
  code: "SOURCE_ROOT_MISSING",
@@ -84096,7 +84231,7 @@ function discoverStoresUnderRoot(sourceRoot) {
84096
84231
  try {
84097
84232
  rootStat = statSync12(rootPath);
84098
84233
  } catch (error2) {
84099
- const ref = createStoreRef(join26(rootPath, TODO_STORE_RELATIVE_PATH));
84234
+ const ref = createStoreRef(join28(rootPath, TODO_STORE_RELATIVE_PATH));
84100
84235
  errors2.push({
84101
84236
  ...ref,
84102
84237
  code: "SOURCE_ROOT_UNREADABLE",
@@ -84109,8 +84244,8 @@ function discoverStoresUnderRoot(sourceRoot) {
84109
84244
  return { stores, errors: errors2 };
84110
84245
  }
84111
84246
  function scanDirectory(dir, depth) {
84112
- const candidate = join26(dir, TODO_STORE_RELATIVE_PATH);
84113
- if (existsSync25(candidate)) {
84247
+ const candidate = join28(dir, TODO_STORE_RELATIVE_PATH);
84248
+ if (existsSync26(candidate)) {
84114
84249
  stores.push(createStoreRef(candidate));
84115
84250
  }
84116
84251
  if (depth >= ROOT_SCAN_MAX_DEPTH)
@@ -84130,7 +84265,7 @@ function discoverStoresUnderRoot(sourceRoot) {
84130
84265
  for (const entry2 of entries) {
84131
84266
  if (!entry2.isDirectory() || SKIPPED_SCAN_DIRS.has(entry2.name))
84132
84267
  continue;
84133
- scanDirectory(join26(dir, entry2.name), depth + 1);
84268
+ scanDirectory(join28(dir, entry2.name), depth + 1);
84134
84269
  }
84135
84270
  }
84136
84271
  scanDirectory(rootPath, 0);
@@ -84156,7 +84291,7 @@ function collectStoreRefs(input) {
84156
84291
  };
84157
84292
  }
84158
84293
  function openReadonlyStore(ref) {
84159
- if (!existsSync25(ref.source_db_path)) {
84294
+ if (!existsSync26(ref.source_db_path)) {
84160
84295
  throw Object.assign(new Error(`Store does not exist: ${ref.source_db_path}`), { code: "STORE_MISSING" });
84161
84296
  }
84162
84297
  return new Database4(ref.source_db_path, { readonly: true, create: false });
@@ -84330,7 +84465,7 @@ var init_task_route_sources = __esm(() => {
84330
84465
  init_task_crud();
84331
84466
  init_redaction();
84332
84467
  init_task_routing();
84333
- TODO_STORE_RELATIVE_PATH = join26(".hasna", "todos", "todos.db");
84468
+ TODO_STORE_RELATIVE_PATH = join28(".hasna", "todos", "todos.db");
84334
84469
  SKIPPED_SCAN_DIRS = new Set([
84335
84470
  ".git",
84336
84471
  ".hg",
@@ -85301,9 +85436,14 @@ Blocked:`));
85301
85436
  const globalOpts = program2.opts();
85302
85437
  const json7 = opts.json || globalOpts.json;
85303
85438
  const filters = {};
85304
- if (opts.project)
85305
- filters.project_id = opts.project;
85439
+ const projectRef = opts.project ?? globalOpts.project;
85440
+ if (typeof projectRef === "string" && projectRef.trim() === "") {
85441
+ handleError(new Error("--project requires a non-empty project reference"));
85442
+ }
85306
85443
  const cloud = getTodosCloudClient();
85444
+ if (projectRef) {
85445
+ filters.project_id = cloud ? await cloudResolveProjectRef(cloud, projectRef) : resolveExplicitProject(projectRef).id;
85446
+ }
85307
85447
  const work = cloud ? await cloudActiveWork(cloud, Object.keys(filters).length ? filters : {}) : getActiveWork(Object.keys(filters).length ? filters : undefined, getDatabase());
85308
85448
  if (json7) {
85309
85449
  console.log(JSON.stringify(work, null, 2));
@@ -85869,9 +86009,9 @@ Findings`));
85869
86009
  const db = getDatabase();
85870
86010
  const row = db.query("SELECT COUNT(*) as count FROM tasks").get();
85871
86011
  const { statSync: statSync13 } = await import("fs");
85872
- const { join: join27 } = await import("path");
85873
- const { getHomeDir: getHomeDir2 } = await Promise.resolve().then(() => (init_sync_utils(), exports_sync_utils));
85874
- const dbPath = process.env["HASNA_TODOS_DB_PATH"] || process.env["TODOS_DB_PATH"] || join27(getHomeDir2(), ".hasna", "todos", "todos.db");
86012
+ const { join: join29 } = await import("path");
86013
+ const { getTodosGlobalDir: getTodosGlobalDir2 } = await Promise.resolve().then(() => (init_sync_utils(), exports_sync_utils));
86014
+ const dbPath = process.env["HASNA_TODOS_DB_PATH"] || process.env["TODOS_DB_PATH"] || join29(getTodosGlobalDir2(), "todos.db");
85875
86015
  let size = "unknown";
85876
86016
  try {
85877
86017
  size = `${(statSync13(dbPath).size / 1024 / 1024).toFixed(1)} MB`;
@@ -88029,21 +88169,21 @@ __export(exports_mcp_hooks_commands, {
88029
88169
  });
88030
88170
  import chalk10 from "chalk";
88031
88171
  import { execSync as execSync3 } from "child_process";
88032
- import { existsSync as existsSync26, readFileSync as readFileSync22, writeFileSync as writeFileSync13, mkdirSync as mkdirSync14, chmodSync as chmodSync3 } from "fs";
88033
- import { dirname as dirname15, join as join27 } from "path";
88172
+ import { existsSync as existsSync27, readFileSync as readFileSync22, writeFileSync as writeFileSync13, mkdirSync as mkdirSync14, chmodSync as chmodSync3 } from "fs";
88173
+ import { dirname as dirname15, join as join29 } from "path";
88034
88174
  function getMcpBinaryPath() {
88035
88175
  try {
88036
88176
  const p = execSync3("which todos-mcp", { encoding: "utf-8" }).trim();
88037
88177
  if (p)
88038
88178
  return p;
88039
88179
  } catch {}
88040
- const bunBin = join27(HOME2, ".bun", "bin", "todos-mcp");
88041
- if (existsSync26(bunBin))
88180
+ const bunBin = join29(HOME2, ".bun", "bin", "todos-mcp");
88181
+ if (existsSync27(bunBin))
88042
88182
  return bunBin;
88043
88183
  return "todos-mcp";
88044
88184
  }
88045
88185
  function readJsonFile2(path) {
88046
- if (!existsSync26(path))
88186
+ if (!existsSync27(path))
88047
88187
  return {};
88048
88188
  try {
88049
88189
  return JSON.parse(readFileSync22(path, "utf-8"));
@@ -88053,19 +88193,19 @@ function readJsonFile2(path) {
88053
88193
  }
88054
88194
  function writeJsonFile2(path, data) {
88055
88195
  const dir = dirname15(path);
88056
- if (!existsSync26(dir))
88196
+ if (!existsSync27(dir))
88057
88197
  mkdirSync14(dir, { recursive: true });
88058
88198
  writeFileSync13(path, JSON.stringify(data, null, 2) + `
88059
88199
  `);
88060
88200
  }
88061
88201
  function readTomlFile(path) {
88062
- if (!existsSync26(path))
88202
+ if (!existsSync27(path))
88063
88203
  return "";
88064
88204
  return readFileSync22(path, "utf-8");
88065
88205
  }
88066
88206
  function writeTomlFile(path, content) {
88067
88207
  const dir = dirname15(path);
88068
- if (!existsSync26(dir))
88208
+ if (!existsSync27(dir))
88069
88209
  mkdirSync14(dir, { recursive: true });
88070
88210
  writeFileSync13(path, content);
88071
88211
  }
@@ -88130,7 +88270,7 @@ function unregisterClaude(_global) {
88130
88270
  }
88131
88271
  }
88132
88272
  function registerCodex(binPath) {
88133
- const configPath = join27(HOME2, ".codex", "config.toml");
88273
+ const configPath = join29(HOME2, ".codex", "config.toml");
88134
88274
  let content = readTomlFile(configPath);
88135
88275
  content = removeTomlBlock(content, "mcp_servers.todos");
88136
88276
  const block = `
@@ -88144,7 +88284,7 @@ args = ["--stdio"]
88144
88284
  console.log(chalk10.green(`Codex CLI: registered in ${configPath}`));
88145
88285
  }
88146
88286
  function unregisterCodex() {
88147
- const configPath = join27(HOME2, ".codex", "config.toml");
88287
+ const configPath = join29(HOME2, ".codex", "config.toml");
88148
88288
  let content = readTomlFile(configPath);
88149
88289
  if (!content.includes("[mcp_servers.todos]")) {
88150
88290
  console.log(chalk10.dim(`Codex CLI: todos not found in ${configPath}`));
@@ -88156,7 +88296,7 @@ function unregisterCodex() {
88156
88296
  console.log(chalk10.green(`Codex CLI: unregistered from ${configPath}`));
88157
88297
  }
88158
88298
  function registerGemini(binPath) {
88159
- const configPath = join27(HOME2, ".gemini", "settings.json");
88299
+ const configPath = join29(HOME2, ".gemini", "settings.json");
88160
88300
  const config = readJsonFile2(configPath);
88161
88301
  if (!config["mcpServers"]) {
88162
88302
  config["mcpServers"] = {};
@@ -88170,7 +88310,7 @@ function registerGemini(binPath) {
88170
88310
  console.log(chalk10.green(`Gemini CLI: registered in ${configPath}`));
88171
88311
  }
88172
88312
  function unregisterGemini() {
88173
- const configPath = join27(HOME2, ".gemini", "settings.json");
88313
+ const configPath = join29(HOME2, ".gemini", "settings.json");
88174
88314
  const config = readJsonFile2(configPath);
88175
88315
  const servers = config["mcpServers"];
88176
88316
  if (!servers || !("todos" in servers)) {
@@ -88204,7 +88344,7 @@ function unregisterTakumi(global) {
88204
88344
  }
88205
88345
  }
88206
88346
  function cursorConfigPath(global) {
88207
- return global ? join27(HOME2, ".cursor", "mcp.json") : join27(process.cwd(), ".cursor", "mcp.json");
88347
+ return global ? join29(HOME2, ".cursor", "mcp.json") : join29(process.cwd(), ".cursor", "mcp.json");
88208
88348
  }
88209
88349
  function registerCursor(binPath, global) {
88210
88350
  const configPath = cursorConfigPath(global);
@@ -88290,8 +88430,8 @@ function registerMcpHooksCommands(program2) {
88290
88430
  if (p)
88291
88431
  todosBin = p;
88292
88432
  } catch {}
88293
- const hooksDir = join27(process.cwd(), ".claude", "hooks");
88294
- if (!existsSync26(hooksDir))
88433
+ const hooksDir = join29(process.cwd(), ".claude", "hooks");
88434
+ if (!existsSync27(hooksDir))
88295
88435
  mkdirSync14(hooksDir, { recursive: true });
88296
88436
  const hookScript = `#!/usr/bin/env bash
88297
88437
  # Auto-generated by: todos hooks install
@@ -88316,11 +88456,11 @@ esac
88316
88456
 
88317
88457
  exit 0
88318
88458
  `;
88319
- const hookPath = join27(hooksDir, "todos-sync.sh");
88459
+ const hookPath = join29(hooksDir, "todos-sync.sh");
88320
88460
  writeFileSync13(hookPath, hookScript);
88321
88461
  execSync3(`chmod +x "${hookPath}"`);
88322
88462
  console.log(chalk10.green(`Hook script created: ${hookPath}`));
88323
- const settingsPath = join27(process.cwd(), ".claude", "settings.json");
88463
+ const settingsPath = join29(process.cwd(), ".claude", "settings.json");
88324
88464
  const settings = readJsonFile2(settingsPath);
88325
88465
  if (!settings["hooks"]) {
88326
88466
  settings["hooks"] = {};
@@ -89251,7 +89391,7 @@ Artifacts:`));
89251
89391
  const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
89252
89392
  const hookPath = `${gitDir}/hooks/post-commit`;
89253
89393
  const marker = "# todos-auto-link";
89254
- if (existsSync26(hookPath)) {
89394
+ if (existsSync27(hookPath)) {
89255
89395
  const existing = readFileSync22(hookPath, "utf-8");
89256
89396
  if (existing.includes(marker)) {
89257
89397
  console.log(chalk10.yellow("Hook already installed."));
@@ -89278,7 +89418,7 @@ $(dirname "$0")/../../scripts/post-commit-hook.sh
89278
89418
  const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
89279
89419
  const hookPath = `${gitDir}/hooks/post-commit`;
89280
89420
  const marker = "# todos-auto-link";
89281
- if (!existsSync26(hookPath)) {
89421
+ if (!existsSync27(hookPath)) {
89282
89422
  console.log(chalk10.dim("No post-commit hook found."));
89283
89423
  return;
89284
89424
  }
@@ -89534,10 +89674,10 @@ var init_delegation_brief = () => {};
89534
89674
 
89535
89675
  // src/lib/delegation-policy.ts
89536
89676
  import { readFileSync as readFileSync23 } from "fs";
89537
- import { homedir as homedir4 } from "os";
89538
- import { join as join28 } from "path";
89677
+ import { homedir as homedir6 } from "os";
89678
+ import { join as join30 } from "path";
89539
89679
  function defaultDelegationEmbargoPath() {
89540
- return process.env["TODOS_DELEGATION_EMBARGO_PATH"] || join28(homedir4(), ".hasna", "identities", "delegation-embargo.json");
89680
+ return process.env["TODOS_DELEGATION_EMBARGO_PATH"] || join30(homedir6(), ".hasna", "identities", "delegation-embargo.json");
89541
89681
  }
89542
89682
  function loadDelegationEmbargo(path = defaultDelegationEmbargoPath()) {
89543
89683
  try {
@@ -89882,7 +90022,7 @@ import chalk13 from "chalk";
89882
90022
  import { execSync as execSync4 } from "child_process";
89883
90023
  import { readFileSync as readFileSync25, unlinkSync as unlinkSync3, writeFileSync as writeFileSync14 } from "fs";
89884
90024
  import { tmpdir as tmpdir5 } from "os";
89885
- import { join as join29 } from "path";
90025
+ import { join as join31 } from "path";
89886
90026
  function getOrCreateLocalMachineName() {
89887
90027
  return process.env["TODOS_MACHINE_NAME"] || __require("os").hostname() || "unknown";
89888
90028
  }
@@ -89920,7 +90060,7 @@ function remoteTempPath(sshAddress) {
89920
90060
  }
89921
90061
  function readRemoteBridgeBundle(sshAddress) {
89922
90062
  const remotePath = remoteTempPath(sshAddress);
89923
- const localPath = join29(tmpdir5(), `todos-bridge-pull-${uuid()}.json`);
90063
+ const localPath = join31(tmpdir5(), `todos-bridge-pull-${uuid()}.json`);
89924
90064
  try {
89925
90065
  runSsh(sshAddress, `todos export --format bridge --allow-plaintext-sensitive --output ${shellQuote(remotePath)}`, 120000);
89926
90066
  scpFromRemote(sshAddress, remotePath, localPath);
@@ -89935,7 +90075,7 @@ function readRemoteBridgeBundle(sshAddress) {
89935
90075
  }
89936
90076
  }
89937
90077
  function writeLocalBridgeBundle() {
89938
- const localPath = join29(tmpdir5(), `todos-bridge-push-${uuid()}.json`);
90078
+ const localPath = join31(tmpdir5(), `todos-bridge-push-${uuid()}.json`);
89939
90079
  writeFileSync14(localPath, JSON.stringify(createLocalBridgeBundle(), null, 2));
89940
90080
  return localPath;
89941
90081
  }
@@ -90994,7 +91134,7 @@ __export(exports_onboarding_commands, {
90994
91134
  registerOnboardingCommands: () => registerOnboardingCommands
90995
91135
  });
90996
91136
  import chalk20 from "chalk";
90997
- import { resolve as resolve23 } from "path";
91137
+ import { resolve as resolve24 } from "path";
90998
91138
  function registerOnboardingCommands(program2) {
90999
91139
  program2.command("onboarding").alias("demo-fixtures").description("List, show, write, or import bundled local onboarding fixtures").option("--show <name>", "Show one fixture bridge bundle as JSON").option("--write <dir>", "Write all bundled fixture bridge bundles to a directory").option("--import <name>", "Dry-run or apply an onboarding fixture import").option("--apply", "Apply an onboarding fixture import. Defaults to dry-run.").option("--resolve-conflicts", "Safely merge existing local tasks while preserving divergent fields").action(async (opts) => {
91000
91140
  const globalOpts = program2.opts();
@@ -91010,7 +91150,7 @@ function registerOnboardingCommands(program2) {
91010
91150
  return;
91011
91151
  }
91012
91152
  if (opts.write) {
91013
- const result = writeOnboardingFixtureFiles2(resolve23(opts.write));
91153
+ const result = writeOnboardingFixtureFiles2(resolve24(opts.write));
91014
91154
  if (globalOpts.json) {
91015
91155
  output(result, true);
91016
91156
  return;
@@ -94511,7 +94651,7 @@ __export(exports_sdk_integration_fixtures, {
94511
94651
  TODOS_SDK_INTEGRATION_FIXTURE_GENERATED_AT: () => TODOS_SDK_INTEGRATION_FIXTURE_GENERATED_AT
94512
94652
  });
94513
94653
  import { mkdirSync as mkdirSync15, writeFileSync as writeFileSync15 } from "fs";
94514
- import { join as join30 } from "path";
94654
+ import { join as join32 } from "path";
94515
94655
  function source5(version) {
94516
94656
  return {
94517
94657
  packageName: "@hasna/todos",
@@ -94618,7 +94758,7 @@ function writeSdkIntegrationFixtures(directory, options = {}) {
94618
94758
  ];
94619
94759
  const written = [];
94620
94760
  for (const [name, payload] of files) {
94621
- const file = join30(directory, name);
94761
+ const file = join32(directory, name);
94622
94762
  writeFileSync15(file, `${JSON.stringify(payload, null, 2)}
94623
94763
  `, "utf-8");
94624
94764
  written.push(file);
@@ -94642,7 +94782,7 @@ __export(exports_sdk_fixture_commands, {
94642
94782
  registerSdkFixtureCommands: () => registerSdkFixtureCommands
94643
94783
  });
94644
94784
  import chalk22 from "chalk";
94645
- import { resolve as resolve24 } from "path";
94785
+ import { resolve as resolve25 } from "path";
94646
94786
  function registerSdkFixtureCommands(program2) {
94647
94787
  program2.command("sdk-fixtures").description("List, show, or write local SDK integration fixtures").option("--show", "Print the full fixture pack JSON").option("--write <dir>", "Write fixture pack, bridge fixture, contract snapshots, and example index to a directory").action(async (opts) => {
94648
94788
  const globalOpts = program2.opts();
@@ -94653,7 +94793,7 @@ function registerSdkFixtureCommands(program2) {
94653
94793
  writeSdkIntegrationFixtures: writeSdkIntegrationFixtures2
94654
94794
  } = await Promise.resolve().then(() => (init_sdk_integration_fixtures(), exports_sdk_integration_fixtures));
94655
94795
  if (opts.write) {
94656
- const result = writeSdkIntegrationFixtures2(resolve24(opts.write));
94796
+ const result = writeSdkIntegrationFixtures2(resolve25(opts.write));
94657
94797
  if (globalOpts.json) {
94658
94798
  console.log(JSON.stringify(result));
94659
94799
  return;
@@ -95472,7 +95612,7 @@ __export(exports_local_backup_commands, {
95472
95612
  registerLocalBackupCommands: () => registerLocalBackupCommands
95473
95613
  });
95474
95614
  import chalk28 from "chalk";
95475
- import { resolve as resolve25 } from "path";
95615
+ import { resolve as resolve26 } from "path";
95476
95616
  function globalOptions8(program2) {
95477
95617
  const command = program2;
95478
95618
  return command.optsWithGlobals?.() ?? program2.opts();
@@ -95494,10 +95634,10 @@ function registerLocalBackupCommands(program2) {
95494
95634
  const projectId = opts.projectId ?? autoProject(globalOpts);
95495
95635
  const backupBundle = createLocalBackup2({
95496
95636
  project_id: projectId,
95497
- output_path: opts.output ? resolve25(opts.output) : undefined
95637
+ output_path: opts.output ? resolve26(opts.output) : undefined
95498
95638
  });
95499
95639
  const result = {
95500
- output_path: opts.output ? resolve25(opts.output) : null,
95640
+ output_path: opts.output ? resolve26(opts.output) : null,
95501
95641
  backup: backupBundle
95502
95642
  };
95503
95643
  if (opts.json || globalOpts.json) {
@@ -95668,8 +95808,8 @@ class TodosShadowMirror {
95668
95808
  async flush() {
95669
95809
  if (this.idle())
95670
95810
  return;
95671
- await new Promise((resolve26) => {
95672
- this.idleResolvers.push(resolve26);
95811
+ await new Promise((resolve27) => {
95812
+ this.idleResolvers.push(resolve27);
95673
95813
  this.pump();
95674
95814
  });
95675
95815
  }
@@ -95681,8 +95821,8 @@ class TodosShadowMirror {
95681
95821
  return;
95682
95822
  const resolvers = this.idleResolvers;
95683
95823
  this.idleResolvers = [];
95684
- for (const resolve26 of resolvers)
95685
- resolve26();
95824
+ for (const resolve27 of resolvers)
95825
+ resolve27();
95686
95826
  }
95687
95827
  pump() {
95688
95828
  if (this.pumping)
@@ -98192,6 +98332,41 @@ async function registerOptionalEventsCommands(program3) {
98192
98332
  }
98193
98333
  registerUnavailableEventsCommands(program3);
98194
98334
  }
98335
+ function commandForArgs(root, args) {
98336
+ let command = root;
98337
+ for (let index = 0;index < args.length; index += 1) {
98338
+ const arg = args[index];
98339
+ if (arg.startsWith("-")) {
98340
+ const exact = command.options.find((candidate) => candidate.long === arg || candidate.short === arg);
98341
+ const inline = command.options.find((candidate) => candidate.long !== undefined && arg.startsWith(`${candidate.long}=`) || candidate.short !== undefined && arg.startsWith(`${candidate.short}=`));
98342
+ const option = exact ?? inline;
98343
+ if (option?.required || option?.optional) {
98344
+ if (exact)
98345
+ index += 1;
98346
+ } else if (!option && !arg.includes("=")) {
98347
+ const next = args[index + 1];
98348
+ const nextIsCommand = next !== undefined && !next.startsWith("-") && command.commands.some((candidate) => candidate.name() === next || candidate.aliases().includes(next));
98349
+ if (next !== undefined && !next.startsWith("-") && !nextIsCommand)
98350
+ index += 1;
98351
+ }
98352
+ continue;
98353
+ }
98354
+ const child = command.commands.find((candidate) => candidate.name() === arg || candidate.aliases().includes(arg));
98355
+ if (!child)
98356
+ break;
98357
+ command = child;
98358
+ }
98359
+ return command;
98360
+ }
98361
+ function unsupportedActiveFormatOption(command, args) {
98362
+ if (command.name() !== "active")
98363
+ return null;
98364
+ const activeIndex = args.indexOf("active");
98365
+ if (activeIndex < 0)
98366
+ return null;
98367
+ const format = (arg) => arg === "--format" || arg.startsWith("--format=");
98368
+ return args.slice(activeIndex + 1).find(format) ?? args.slice(0, activeIndex).find(format) ?? null;
98369
+ }
98195
98370
  program2.name("todos").description("Universal task management for AI coding agents").version(getPackageVersion()).option("--project <path>", "Project path").option("-j, --json", "Output as JSON").option("--agent <name>", "Agent name").option("--session <id>", "Session ID");
98196
98371
  var authority;
98197
98372
  try {
@@ -98323,6 +98498,10 @@ await registerOptionalEventsCommands(program2);
98323
98498
  registerHelpCommands2(program2, authority.route, remoteCommandCapabilities);
98324
98499
  applyTodosCliHelpVisibility(program2, authority.route, remoteCommandCapabilities);
98325
98500
  try {
98501
+ const activeFormat = unsupportedActiveFormatOption(commandForArgs(program2, process.argv.slice(2)), process.argv.slice(2));
98502
+ if (activeFormat) {
98503
+ throw new Error(`ACTIVE_FORMAT_UNSUPPORTED: ${activeFormat} is not supported by todos active; use --json for machine-readable output`);
98504
+ }
98326
98505
  if (metadataRequested) {
98327
98506
  const unavailableCommand = getUnavailableTodosCliRemoteMetadataCommand(authority.route, remoteCommandCapabilities, process.argv.slice(2));
98328
98507
  if (unavailableCommand) {