@hasna/todos 0.13.12 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +2 -0
  2. package/dist/cli/commands/agent-commands.d.ts +39 -0
  3. package/dist/cli/commands/agent-commands.d.ts.map +1 -1
  4. package/dist/cli/commands/config-serve-commands.d.ts.map +1 -1
  5. package/dist/cli/commands/dispatch.d.ts.map +1 -1
  6. package/dist/cli/commands/mcp-hooks-commands.d.ts.map +1 -1
  7. package/dist/cli/commands/project-commands.d.ts.map +1 -1
  8. package/dist/cli/commands/task-commands.d.ts.map +1 -1
  9. package/dist/cli/helpers.d.ts +64 -2
  10. package/dist/cli/helpers.d.ts.map +1 -1
  11. package/dist/cli/index.js +624 -98
  12. package/dist/cli/stage-a.d.ts.map +1 -1
  13. package/dist/contracts.js +123 -116
  14. package/dist/db/database.d.ts.map +1 -1
  15. package/dist/db/task-crud.d.ts.map +1 -1
  16. package/dist/index.js +135 -127
  17. package/dist/lib/bulk-tags.d.ts +57 -0
  18. package/dist/lib/bulk-tags.d.ts.map +1 -0
  19. package/dist/lib/enum-vocabulary.d.ts +72 -0
  20. package/dist/lib/enum-vocabulary.d.ts.map +1 -0
  21. package/dist/lib/run-records.d.ts.map +1 -1
  22. package/dist/lib/sandbox-profiles.d.ts.map +1 -1
  23. package/dist/lib/saved-search-views.d.ts +12 -1
  24. package/dist/lib/saved-search-views.d.ts.map +1 -1
  25. package/dist/lib/sync-utils.d.ts +1 -1
  26. package/dist/lib/sync-utils.d.ts.map +1 -1
  27. package/dist/mcp/index.d.ts.map +1 -1
  28. package/dist/mcp/index.js +289 -82
  29. package/dist/mcp/tools/agents.d.ts.map +1 -1
  30. package/dist/mcp/tools/task-auto-tools.d.ts.map +1 -1
  31. package/dist/mcp/tools/task-crud.d.ts +1 -0
  32. package/dist/mcp/tools/task-crud.d.ts.map +1 -1
  33. package/dist/mcp.js +1 -1
  34. package/dist/registry.js +123 -116
  35. package/dist/release-provenance.json +5 -5
  36. package/dist/sdk/index.js +3 -2
  37. package/dist/sdk/v1.generated.d.ts +2 -2
  38. package/dist/sdk/v1.generated.d.ts.map +1 -1
  39. package/dist/server/index.js +245 -38
  40. package/dist/server/openapi.d.ts +58 -0
  41. package/dist/server/openapi.d.ts.map +1 -1
  42. package/dist/server/routes.d.ts.map +1 -1
  43. package/dist/server/v1.d.ts.map +1 -1
  44. package/dist/storage.js +124 -115
  45. package/dist/types/index.d.ts +11 -0
  46. package/dist/types/index.d.ts.map +1 -1
  47. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -3430,6 +3430,9 @@ var init_machines = __esm(() => {
3430
3430
  function isBlockingDependencyStatus(status) {
3431
3431
  return status !== "completed" && status !== "cancelled";
3432
3432
  }
3433
+ function isTerminalStatus(status) {
3434
+ return status === "completed" || status === "failed" || status === "cancelled";
3435
+ }
3433
3436
  var TASK_STATUSES, TASK_PRIORITIES, PLAN_STATUSES, VersionConflictError, TaskNotFoundError, TaskNotStartableError, TaskReferenceAmbiguousError, ProjectNotFoundError, ResourceConflictError, PlanNotFoundError, LockError, AgentNotFoundError, IdentityAliasAmbiguousError, IdentityIdImmutableError, TaskListNotFoundError, DependencyCycleError, CompletionGuardError, DISPATCH_STATUSES, DispatchNotFoundError;
3434
3437
  var init_types = __esm(() => {
3435
3438
  TASK_STATUSES = [
@@ -3787,6 +3790,103 @@ var init_identity_mapping = __esm(() => {
3787
3790
  });
3788
3791
  });
3789
3792
 
3793
+ // src/lib/sync-utils.ts
3794
+ import { existsSync as existsSync2, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
3795
+ import { createHash } from "crypto";
3796
+ import { homedir } from "os";
3797
+ import { join } from "path";
3798
+ function getHomeDir() {
3799
+ return process.env["HOME"] || process.env["USERPROFILE"] || homedir();
3800
+ }
3801
+ function getTodosGlobalDir() {
3802
+ return join(getHomeDir(), ".hasna", "todos");
3803
+ }
3804
+ function ensureDir(dir) {
3805
+ if (!existsSync2(dir))
3806
+ mkdirSync(dir, { recursive: true });
3807
+ }
3808
+ function listJsonFiles(dir) {
3809
+ if (!existsSync2(dir))
3810
+ return [];
3811
+ return readdirSync(dir).filter((f) => f.endsWith(".json"));
3812
+ }
3813
+ function readJsonFile(path) {
3814
+ try {
3815
+ return JSON.parse(readFileSync(path, "utf-8"));
3816
+ } catch {
3817
+ return null;
3818
+ }
3819
+ }
3820
+ function writeJsonFile(path, data) {
3821
+ writeFileSync(path, JSON.stringify(data, null, 2) + `
3822
+ `);
3823
+ }
3824
+ function readHighWaterMark(dir) {
3825
+ const path = join(dir, ".highwatermark");
3826
+ if (!existsSync2(path))
3827
+ return 1;
3828
+ const val = parseInt(readFileSync(path, "utf-8").trim(), 10);
3829
+ return isNaN(val) ? 1 : val;
3830
+ }
3831
+ function writeHighWaterMark(dir, value) {
3832
+ writeFileSync(join(dir, ".highwatermark"), String(value));
3833
+ }
3834
+ function getFileMtimeMs(path) {
3835
+ try {
3836
+ return statSync(path).mtimeMs;
3837
+ } catch {
3838
+ return null;
3839
+ }
3840
+ }
3841
+ function parseTimestamp(value) {
3842
+ if (typeof value !== "string")
3843
+ return null;
3844
+ const parsed = Date.parse(value);
3845
+ return Number.isNaN(parsed) ? null : parsed;
3846
+ }
3847
+ function appendSyncConflict(metadata, conflict, limit = 5) {
3848
+ const current = Array.isArray(metadata["sync_conflicts"]) ? metadata["sync_conflicts"] : [];
3849
+ const next = [conflict, ...current].slice(0, limit);
3850
+ return { ...metadata, sync_conflicts: next };
3851
+ }
3852
+ function canonicalize(value) {
3853
+ if (Array.isArray(value))
3854
+ return value.map(canonicalize);
3855
+ if (!value || typeof value !== "object")
3856
+ return value;
3857
+ const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== undefined).sort(([a], [b]) => a.localeCompare(b));
3858
+ return Object.fromEntries(entries.map(([key, entryValue]) => [key, canonicalize(entryValue)]));
3859
+ }
3860
+ function withoutSyncFingerprintMetadata(metadata) {
3861
+ const { [TODO_SYNC_FINGERPRINT_KEY]: _fingerprint, ...rest } = metadata;
3862
+ return rest;
3863
+ }
3864
+ function syncFingerprint(record) {
3865
+ const metadata = withoutSyncFingerprintMetadata(record.metadata || {});
3866
+ const canonical = canonicalize({ ...record, metadata });
3867
+ return `sha256:${createHash("sha256").update(JSON.stringify(canonical)).digest("hex")}`;
3868
+ }
3869
+ function withSyncFingerprint(record) {
3870
+ const metadata = withoutSyncFingerprintMetadata(record.metadata);
3871
+ return {
3872
+ ...record,
3873
+ metadata: {
3874
+ ...metadata,
3875
+ [TODO_SYNC_FINGERPRINT_KEY]: syncFingerprint({ ...record, metadata })
3876
+ }
3877
+ };
3878
+ }
3879
+ function hasSyncFingerprintChanged(record) {
3880
+ const stored = record.metadata?.[TODO_SYNC_FINGERPRINT_KEY];
3881
+ if (typeof stored !== "string" || stored.length === 0)
3882
+ return null;
3883
+ return stored !== syncFingerprint(record);
3884
+ }
3885
+ var TODO_SYNC_FINGERPRINT_KEY = "todos_sync_fingerprint", HOME;
3886
+ var init_sync_utils = __esm(() => {
3887
+ HOME = getHomeDir();
3888
+ });
3889
+
3790
3890
  // src/storage/config.ts
3791
3891
  var exports_config = {};
3792
3892
  __export(exports_config, {
@@ -4117,8 +4217,8 @@ __export(exports_database, {
4117
4217
  LOCK_EXPIRY_MINUTES: () => LOCK_EXPIRY_MINUTES
4118
4218
  });
4119
4219
  import { Database } from "bun:sqlite";
4120
- import { existsSync as existsSync2, mkdirSync } from "fs";
4121
- import { dirname, join, resolve as resolve2 } from "path";
4220
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2 } from "fs";
4221
+ import { dirname, join as join2, resolve as resolve2 } from "path";
4122
4222
  function isInMemoryDb(path) {
4123
4223
  return path === ":memory:" || path.startsWith("file::memory:");
4124
4224
  }
@@ -4127,8 +4227,8 @@ function findNearestProjectDb(startDir) {
4127
4227
  const stopAt = gitRoot ? resolve2(gitRoot) : resolve2(startDir);
4128
4228
  let dir = resolve2(startDir);
4129
4229
  while (true) {
4130
- const candidate = join(dir, ".hasna", "todos", "todos.db");
4131
- if (existsSync2(candidate))
4230
+ const candidate = join2(dir, ".hasna", "todos", "todos.db");
4231
+ if (existsSync3(candidate))
4132
4232
  return candidate;
4133
4233
  if (dir === stopAt)
4134
4234
  break;
@@ -4142,7 +4242,7 @@ function findNearestProjectDb(startDir) {
4142
4242
  function findGitRoot(startDir) {
4143
4243
  let dir = resolve2(startDir);
4144
4244
  while (true) {
4145
- if (existsSync2(join(dir, ".git")))
4245
+ if (existsSync3(join2(dir, ".git")))
4146
4246
  return dir;
4147
4247
  const parent = dirname(dir);
4148
4248
  if (parent === dir)
@@ -4152,8 +4252,7 @@ function findGitRoot(startDir) {
4152
4252
  return null;
4153
4253
  }
4154
4254
  function getGlobalDbPath() {
4155
- const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
4156
- return join(home, ".hasna", "todos", "todos.db");
4255
+ return join2(getHomeDir(), ".hasna", "todos", "todos.db");
4157
4256
  }
4158
4257
  function hasExplicitProjectArg(args = process.argv.slice(2)) {
4159
4258
  return args.some((arg) => arg === "--project" || arg.startsWith("--project="));
@@ -4191,7 +4290,7 @@ function getDbPath() {
4191
4290
  if (process.env["TODOS_DB_SCOPE"] === "project") {
4192
4291
  const gitRoot = findGitRoot(cwd);
4193
4292
  if (gitRoot && canCreateScopedProjectDb()) {
4194
- return join(gitRoot, ".hasna", "todos", "todos.db");
4293
+ return join2(gitRoot, ".hasna", "todos", "todos.db");
4195
4294
  }
4196
4295
  }
4197
4296
  return getGlobalDbPath();
@@ -4199,16 +4298,16 @@ function getDbPath() {
4199
4298
  function getDatabasePath() {
4200
4299
  return getDbPath();
4201
4300
  }
4202
- function ensureDir(filePath) {
4301
+ function ensureDir2(filePath) {
4203
4302
  if (isInMemoryDb(filePath))
4204
4303
  return;
4205
4304
  const dir = dirname(resolve2(filePath));
4206
- if (!existsSync2(dir)) {
4207
- mkdirSync(dir, { recursive: true });
4305
+ if (!existsSync3(dir)) {
4306
+ mkdirSync2(dir, { recursive: true });
4208
4307
  }
4209
4308
  }
4210
4309
  function openDatabase(path) {
4211
- ensureDir(path);
4310
+ ensureDir2(path);
4212
4311
  const db = new Database(path);
4213
4312
  db.run("PRAGMA busy_timeout = 5000");
4214
4313
  db.run("PRAGMA journal_mode = WAL");
@@ -4375,105 +4474,10 @@ var init_database = __esm(() => {
4375
4474
  init_machines();
4376
4475
  init_identity_mapping();
4377
4476
  init_types();
4477
+ init_sync_utils();
4378
4478
  ALLOWED_TABLES = new Set(["tasks", "projects", "agents", "plans", "task_lists", "task_templates", "project_knowledge_records", "project_risks", "local_retrospectives"]);
4379
4479
  });
4380
4480
 
4381
- // src/lib/sync-utils.ts
4382
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
4383
- import { createHash } from "crypto";
4384
- import { join as join2 } from "path";
4385
- function getHomeDir() {
4386
- return process.env["HOME"] || process.env["USERPROFILE"] || "~";
4387
- }
4388
- function getTodosGlobalDir() {
4389
- return join2(getHomeDir(), ".hasna", "todos");
4390
- }
4391
- function ensureDir2(dir) {
4392
- if (!existsSync3(dir))
4393
- mkdirSync2(dir, { recursive: true });
4394
- }
4395
- function listJsonFiles(dir) {
4396
- if (!existsSync3(dir))
4397
- return [];
4398
- return readdirSync(dir).filter((f) => f.endsWith(".json"));
4399
- }
4400
- function readJsonFile(path) {
4401
- try {
4402
- return JSON.parse(readFileSync(path, "utf-8"));
4403
- } catch {
4404
- return null;
4405
- }
4406
- }
4407
- function writeJsonFile(path, data) {
4408
- writeFileSync(path, JSON.stringify(data, null, 2) + `
4409
- `);
4410
- }
4411
- function readHighWaterMark(dir) {
4412
- const path = join2(dir, ".highwatermark");
4413
- if (!existsSync3(path))
4414
- return 1;
4415
- const val = parseInt(readFileSync(path, "utf-8").trim(), 10);
4416
- return isNaN(val) ? 1 : val;
4417
- }
4418
- function writeHighWaterMark(dir, value) {
4419
- writeFileSync(join2(dir, ".highwatermark"), String(value));
4420
- }
4421
- function getFileMtimeMs(path) {
4422
- try {
4423
- return statSync(path).mtimeMs;
4424
- } catch {
4425
- return null;
4426
- }
4427
- }
4428
- function parseTimestamp(value) {
4429
- if (typeof value !== "string")
4430
- return null;
4431
- const parsed = Date.parse(value);
4432
- return Number.isNaN(parsed) ? null : parsed;
4433
- }
4434
- function appendSyncConflict(metadata, conflict, limit = 5) {
4435
- const current = Array.isArray(metadata["sync_conflicts"]) ? metadata["sync_conflicts"] : [];
4436
- const next = [conflict, ...current].slice(0, limit);
4437
- return { ...metadata, sync_conflicts: next };
4438
- }
4439
- function canonicalize(value) {
4440
- if (Array.isArray(value))
4441
- return value.map(canonicalize);
4442
- if (!value || typeof value !== "object")
4443
- return value;
4444
- const entries = Object.entries(value).filter(([, entryValue]) => entryValue !== undefined).sort(([a], [b]) => a.localeCompare(b));
4445
- return Object.fromEntries(entries.map(([key, entryValue]) => [key, canonicalize(entryValue)]));
4446
- }
4447
- function withoutSyncFingerprintMetadata(metadata) {
4448
- const { [TODO_SYNC_FINGERPRINT_KEY]: _fingerprint, ...rest } = metadata;
4449
- return rest;
4450
- }
4451
- function syncFingerprint(record) {
4452
- const metadata = withoutSyncFingerprintMetadata(record.metadata || {});
4453
- const canonical = canonicalize({ ...record, metadata });
4454
- return `sha256:${createHash("sha256").update(JSON.stringify(canonical)).digest("hex")}`;
4455
- }
4456
- function withSyncFingerprint(record) {
4457
- const metadata = withoutSyncFingerprintMetadata(record.metadata);
4458
- return {
4459
- ...record,
4460
- metadata: {
4461
- ...metadata,
4462
- [TODO_SYNC_FINGERPRINT_KEY]: syncFingerprint({ ...record, metadata })
4463
- }
4464
- };
4465
- }
4466
- function hasSyncFingerprintChanged(record) {
4467
- const stored = record.metadata?.[TODO_SYNC_FINGERPRINT_KEY];
4468
- if (typeof stored !== "string" || stored.length === 0)
4469
- return null;
4470
- return stored !== syncFingerprint(record);
4471
- }
4472
- var TODO_SYNC_FINGERPRINT_KEY = "todos_sync_fingerprint", HOME;
4473
- var init_sync_utils = __esm(() => {
4474
- HOME = process.env["HOME"] || process.env["USERPROFILE"] || "~";
4475
- });
4476
-
4477
4481
  // src/lib/config.ts
4478
4482
  import { existsSync as existsSync4 } from "fs";
4479
4483
  import { dirname as dirname2, join as join3 } from "path";
@@ -4499,7 +4503,7 @@ function loadConfig() {
4499
4503
  }
4500
4504
  function saveConfig(config) {
4501
4505
  const configPath = getConfigPath();
4502
- ensureDir2(dirname2(configPath));
4506
+ ensureDir(dirname2(configPath));
4503
4507
  writeJsonFile(configPath, config);
4504
4508
  cached = config;
4505
4509
  return config;
@@ -5844,7 +5848,7 @@ var init_event_hooks = __esm(() => {
5844
5848
  // node_modules/.bun/@hasna+events@0.1.11/node_modules/@hasna/events/dist/index.js
5845
5849
  import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
5846
5850
  import { existsSync as existsSync5 } from "fs";
5847
- import { homedir } from "os";
5851
+ import { homedir as homedir2 } from "os";
5848
5852
  import { join as join4 } from "path";
5849
5853
  import { createHmac, timingSafeEqual } from "crypto";
5850
5854
  import { randomUUID as randomUUID2 } from "crypto";
@@ -5946,7 +5950,7 @@ function channelMatchesEvent(channel, event) {
5946
5950
  return channel.filters.some((filter) => eventMatchesFilter(event, filter));
5947
5951
  }
5948
5952
  function getEventsDataDir(override) {
5949
- return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join4(homedir(), ".hasna", "events");
5953
+ return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join4(homedir2(), ".hasna", "events");
5950
5954
  }
5951
5955
 
5952
5956
  class JsonEventsStore {
@@ -9286,11 +9290,13 @@ function updateTask(id, input, db) {
9286
9290
  }
9287
9291
  sets.push("status = ?");
9288
9292
  params.push(input.status);
9293
+ if (isTerminalStatus(input.status)) {
9294
+ sets.push("locked_by = NULL");
9295
+ sets.push("locked_at = NULL");
9296
+ }
9289
9297
  if (input.status === "completed") {
9290
9298
  sets.push("completed_at = ?");
9291
9299
  params.push(completionTimestamp);
9292
- sets.push("locked_by = NULL");
9293
- sets.push("locked_at = NULL");
9294
9300
  } else if (task.status === "completed" && input.completed_at === undefined) {
9295
9301
  sets.push("completed_at = NULL");
9296
9302
  }
@@ -9414,6 +9420,7 @@ function updateTask(id, input, db) {
9414
9420
  logTaskChange(id, "approve", "approved_by", null, input.approved_by, agentId, d);
9415
9421
  const reopened = input.status !== undefined && input.status !== "completed" && task.status === "completed" && input.completed_at === undefined;
9416
9422
  const completedNow = input.status === "completed";
9423
+ const terminalNow = input.status !== undefined && isTerminalStatus(input.status);
9417
9424
  const updatedTask = {
9418
9425
  ...task,
9419
9426
  ...Object.fromEntries(Object.entries(input).filter(([, v]) => v !== undefined)),
@@ -9421,8 +9428,8 @@ function updateTask(id, input, db) {
9421
9428
  metadata: input.metadata ?? task.metadata,
9422
9429
  version: task.version + 1,
9423
9430
  updated_at: timestamp2,
9424
- locked_by: completedNow ? null : task.locked_by,
9425
- locked_at: completedNow ? null : task.locked_at,
9431
+ locked_by: terminalNow ? null : task.locked_by,
9432
+ locked_at: terminalNow ? null : task.locked_at,
9426
9433
  completed_at: completedNow ? completionTimestamp : reopened ? null : input.completed_at !== undefined ? input.completed_at : task.completed_at,
9427
9434
  sla_minutes: input.sla_minutes !== undefined ? input.sla_minutes : task.sla_minutes,
9428
9435
  actual_minutes: input.actual_minutes ?? task.actual_minutes,
@@ -12138,7 +12145,7 @@ var init_dispatches = __esm(() => {
12138
12145
  // package.json
12139
12146
  var package_default = {
12140
12147
  name: "@hasna/todos",
12141
- version: "0.13.12",
12148
+ version: "0.15.1",
12142
12149
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
12143
12150
  type: "module",
12144
12151
  main: "dist/index.js",
@@ -26735,9 +26742,11 @@ async function updateTask2(id, input, store) {
26735
26742
  throw new Error(`Task ${id} version conflict: expected ${existing.version}, got ${input.version}`);
26736
26743
  }
26737
26744
  const reopened = existing.status === "completed" && input.status !== undefined && input.status !== "completed" && input.completed_at === undefined;
26745
+ const terminalNow = input.status !== undefined && isTerminalStatus(input.status);
26738
26746
  const task2 = {
26739
26747
  ...existing,
26740
26748
  ...definedPatch(input),
26749
+ ...terminalNow ? { locked_by: null, locked_at: null } : {},
26741
26750
  version: existing.version + 1,
26742
26751
  updated_at: new Date().toISOString(),
26743
26752
  tags: input.tags ?? existing.tags,
@@ -36059,6 +36068,7 @@ function resourceDiagnostics() {
36059
36068
  };
36060
36069
  }
36061
36070
  // src/lib/sandbox-profiles.ts
36071
+ init_sync_utils();
36062
36072
  import { existsSync as existsSync14, readFileSync as readFileSync10, writeFileSync as writeFileSync9, mkdirSync as mkdirSync11 } from "fs";
36063
36073
  import { join as join12, dirname as dirname9 } from "path";
36064
36074
  var SANDBOX_PROFILE_VERSION = "todos.sandbox-profile.v1";
@@ -36072,8 +36082,7 @@ function getProfilesPath() {
36072
36082
  return local;
36073
36083
  if (existsSync14(local))
36074
36084
  return local;
36075
- const home = process.env["HOME"] || "~";
36076
- return join12(home, ".hasna", "todos", "sandbox-profiles.json");
36085
+ return join12(getHomeDir(), ".hasna", "todos", "sandbox-profiles.json");
36077
36086
  }
36078
36087
  var cached2 = null;
36079
36088
  function resetSandboxProfileCache() {
@@ -39758,6 +39767,7 @@ todos import issues ./linear.json --source linear --dry-run
39758
39767
  `;
39759
39768
  }
39760
39769
  // src/lib/run-records.ts
39770
+ init_sync_utils();
39761
39771
  init_database();
39762
39772
  init_secret_redaction();
39763
39773
  import { existsSync as existsSync19, mkdirSync as mkdirSync12, writeFileSync as writeFileSync10 } from "fs";
@@ -39997,8 +40007,7 @@ function getDefaultReplayDir() {
39997
40007
  const local = join14(process.cwd(), ".todos", "replays");
39998
40008
  if (existsSync19(join14(process.cwd(), ".todos")))
39999
40009
  return local;
40000
- const home = process.env["HOME"] || "~";
40001
- return join14(home, ".hasna", "todos", "replays");
40010
+ return join14(getHomeDir(), ".hasna", "todos", "replays");
40002
40011
  }
40003
40012
  // src/lib/release-checks.ts
40004
40013
  init_secret_redaction();
@@ -44371,7 +44380,7 @@ function captureEnvironmentSnapshot(input = {}) {
44371
44380
  }
44372
44381
  function writeEnvironmentSnapshot(snapshot, outputPath) {
44373
44382
  const path = outputPath ? resolve17(outputPath) : join18(defaultSnapshotDir(), `${snapshot.id}.json`);
44374
- ensureDir2(dirname15(path));
44383
+ ensureDir(dirname15(path));
44375
44384
  writeJsonFile(path, snapshot);
44376
44385
  return path;
44377
44386
  }
@@ -48156,6 +48165,7 @@ function queryTasksByLocalFields(query, db) {
48156
48165
  }
48157
48166
 
48158
48167
  // src/lib/saved-search-views.ts
48168
+ var SAVED_SEARCH_SCOPES = ["all", "tasks", "projects", "plans", "runs", "comments"];
48159
48169
  function parseFilters(value) {
48160
48170
  if (!value)
48161
48171
  return {};
@@ -48174,10 +48184,8 @@ function rowToSavedSearchView(row) {
48174
48184
  };
48175
48185
  }
48176
48186
  function normalizeScope(scope) {
48177
- if (scope === "all" || scope === "tasks" || scope === "projects" || scope === "plans" || scope === "runs" || scope === "comments") {
48178
- return scope;
48179
- }
48180
- return "tasks";
48187
+ const candidate = (scope ?? "").trim().toLowerCase();
48188
+ return SAVED_SEARCH_SCOPES.includes(candidate) ? candidate : "tasks";
48181
48189
  }
48182
48190
  function normalizeName3(name) {
48183
48191
  const normalized = name.trim();
@@ -48540,7 +48548,7 @@ function taskToClaudeTask(task2, claudeTaskId, existingMeta) {
48540
48548
  function pushToClaudeTaskList(taskListId, projectId, options = {}) {
48541
48549
  const dir = getTaskListDir(taskListId);
48542
48550
  if (!existsSync25(dir))
48543
- ensureDir2(dir);
48551
+ ensureDir(dir);
48544
48552
  const filter = {};
48545
48553
  if (projectId)
48546
48554
  filter["project_id"] = projectId;
@@ -48767,7 +48775,7 @@ function metadataKey(agent) {
48767
48775
  function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
48768
48776
  const dir = getTaskListDir2(agent, taskListId);
48769
48777
  if (!existsSync26(dir))
48770
- ensureDir2(dir);
48778
+ ensureDir(dir);
48771
48779
  const filter = {};
48772
48780
  if (projectId)
48773
48781
  filter["project_id"] = projectId;
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Tag resolution for `todos bulk tag|untag`.
3
+ *
4
+ * Kept as pure functions with no database or transport dependency so both the
5
+ * remote (/v1) and local (SQLite) bulk paths resolve tags identically. A tag
6
+ * that merges on one transport and replaces on the other would corrupt rows
7
+ * depending on which machine ran the backfill.
8
+ */
9
+ export type BulkTagAction = "tag" | "untag";
10
+ export interface BulkTagResolution {
11
+ /** The full tag list to persist. */
12
+ tags: string[];
13
+ /**
14
+ * False when the row already satisfies the request. Callers skip the write
15
+ * entirely, so a re-run after a partial failure does not bump row versions
16
+ * or emit audit noise for rows that were already correct.
17
+ */
18
+ changed: boolean;
19
+ }
20
+ /**
21
+ * Parse a comma-separated `--tag` argument.
22
+ *
23
+ * Splits ONLY on commas: `:` and `/` are legal inside a tag and are load
24
+ * bearing for the namespaced tags this fleet already stores (`repo:secrets`,
25
+ * `gh:hasna/todos`, `directive:k_msd4cz8t_ste6f4`).
26
+ */
27
+ export declare function parseTagList(raw: string | undefined | null): string[];
28
+ export type TagArgumentResolution = {
29
+ ok: true;
30
+ raw: string | undefined;
31
+ } | {
32
+ ok: false;
33
+ conflict: {
34
+ tag: string;
35
+ tags: string;
36
+ };
37
+ };
38
+ /**
39
+ * Reconcile the `--tag` / `--tags` spellings.
40
+ *
41
+ * Both are accepted because the rest of the CLI accepts both, but when they are
42
+ * both present and name DIFFERENT sets the run is refused rather than picking a
43
+ * winner. Silently dropping one of two explicitly-passed tag arguments is the
44
+ * wrong failure mode for a command whose purpose is stamping thousands of rows:
45
+ * the operator sees rc=0 and a success count while the tags they asked for are
46
+ * simply absent.
47
+ */
48
+ export declare function resolveTagArgument(tag: string | undefined, tags: string | undefined): TagArgumentResolution;
49
+ /**
50
+ * Apply `action` to `current`, returning the tag list to persist.
51
+ *
52
+ * `tag` MERGES — it never replaces. `todos update --tags` replaces the list,
53
+ * and reusing that semantic for a bulk provenance backfill would strip every
54
+ * unrelated tag from every row it touched.
55
+ */
56
+ export declare function resolveBulkTags(current: readonly string[] | undefined | null, action: BulkTagAction, tags: readonly string[]): BulkTagResolution;
57
+ //# sourceMappingURL=bulk-tags.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bulk-tags.d.ts","sourceRoot":"","sources":["../../src/lib/bulk-tags.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,MAAM,aAAa,GAAG,KAAK,GAAG,OAAO,CAAC;AAE5C,MAAM,WAAW,iBAAiB;IAChC,oCAAoC;IACpC,IAAI,EAAE,MAAM,EAAE,CAAC;IACf;;;;OAIG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,GAAG,MAAM,EAAE,CAWrE;AAED,MAAM,MAAM,qBAAqB,GAC7B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,GACrC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC;AAE3D;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,IAAI,EAAE,MAAM,GAAG,SAAS,GACvB,qBAAqB,CAUvB;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,IAAI,EAC7C,MAAM,EAAE,aAAa,EACrB,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,iBAAiB,CAiBnB"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Closed-vocabulary (enum) value validation for CLI flags and API query params.
3
+ *
4
+ * Why this module exists
5
+ * ----------------------
6
+ * An unrecognised enum value used to flow straight into the storage filter, where
7
+ * it matched no rows. The command then printed "No tasks found." and exited 0 — a
8
+ * silent empty result set that reads as "there is no work here". `todos list
9
+ * --status open` (`open` is not in the vocabulary) reported zero tasks on a project
10
+ * that had 27, and that empty set was relayed to a human as fact.
11
+ *
12
+ * The rule this module enforces: an out-of-vocabulary value is an ERROR, never an
13
+ * empty result. Callers surface it non-zero (CLI) or as HTTP 400 (API).
14
+ *
15
+ * The vocabulary is ALWAYS supplied by the caller from the single exported source
16
+ * of truth (`TASK_STATUSES`, `TASK_PRIORITIES`, `PLAN_STATUSES`, `DISPATCH_STATUSES`
17
+ * in src/types/index.ts, or a subsystem's own exported constant). Nothing in this
18
+ * file re-types a vocabulary, so a value added to a constant is accepted here with
19
+ * no further edit.
20
+ */
21
+ /**
22
+ * Vocabulary members plausibly meant by `value`, nearest first.
23
+ *
24
+ * Deliberately conservative: a substring/prefix relation, or an edit distance
25
+ * within a third of the word's length. A wrong suggestion is worse than none,
26
+ * because the error already lists every valid value.
27
+ */
28
+ export declare function suggestVocabularyMatches(value: string, vocabulary: readonly string[], limit?: number): string[];
29
+ export interface EnumVocabularySpec<T extends string> {
30
+ /** Flag or query-param name as the user typed it, e.g. "--status" or "status". */
31
+ readonly name: string;
32
+ /** The single source of truth for this vocabulary. Never re-typed locally. */
33
+ readonly vocabulary: readonly T[];
34
+ /**
35
+ * Canonicalizer applied per element before the membership check — this is where
36
+ * documented aliases (`done` -> `completed`) and case folding live. Must be a
37
+ * pure string mapping; a value it does not recognise is returned unchanged so it
38
+ * still fails the membership check rather than being silently rewritten.
39
+ */
40
+ readonly normalize?: (value: string) => string;
41
+ /**
42
+ * Remediation text for inputs that are NOT vocabulary members but that operators
43
+ * plausibly type, keyed by the normalized input. These are not accepted values —
44
+ * they only make the rejection actionable (e.g. `all` -> point at `--all`).
45
+ */
46
+ readonly hints?: Readonly<Record<string, string>>;
47
+ /** Whether a comma-separated list is accepted. Defaults to true. */
48
+ readonly allowList?: boolean;
49
+ }
50
+ export type EnumVocabularyResult<T extends string> = {
51
+ readonly ok: true;
52
+ readonly values: T[];
53
+ } | {
54
+ readonly ok: false;
55
+ readonly message: string;
56
+ readonly invalid: string[];
57
+ };
58
+ /**
59
+ * Split, canonicalize and validate a raw flag/query value against its vocabulary.
60
+ *
61
+ * Every element of a comma-separated list is validated: a single bad element fails
62
+ * the whole value rather than being dropped, because dropping it produced a result
63
+ * set that looked authoritative but silently ignored part of what was asked for.
64
+ */
65
+ export declare function resolveEnumVocabulary<T extends string>(raw: string, spec: EnumVocabularySpec<T>): EnumVocabularyResult<T>;
66
+ /**
67
+ * Collapse a validated list back to the shape the storage filter expects: a bare
68
+ * string for a single value, an array for a real list. Keeps the emitted SQL/query
69
+ * identical to what single-value callers produced before validation was added.
70
+ */
71
+ export declare function collapseEnumValues<T extends string>(values: T[]): T | T[];
72
+ //# sourceMappingURL=enum-vocabulary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"enum-vocabulary.d.ts","sourceRoot":"","sources":["../../src/lib/enum-vocabulary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAsBH;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,SAAS,MAAM,EAAE,EAC7B,KAAK,SAAI,GACR,MAAM,EAAE,CAuBV;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,MAAM;IAClD,kFAAkF;IAClF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,CAAC;IAClC;;;;;OAKG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C;;;;OAIG;IACH,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAClD,oEAAoE;IACpE,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,MAAM,IAC7C;IAAE,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,CAAA;CAAE,GAC3C;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEjF;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,CAAC,SAAS,MAAM,EACpD,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAC1B,oBAAoB,CAAC,CAAC,CAAC,CAqEzB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,EAAE,CAEzE"}
@@ -1 +1 @@
1
- {"version":3,"file":"run-records.d.ts","sourceRoot":"","sources":["../../src/lib/run-records.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"run-records.d.ts","sourceRoot":"","sources":["../../src/lib/run-records.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox-profiles.d.ts","sourceRoot":"","sources":["../../src/lib/sandbox-profiles.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,eAAO,MAAM,uBAAuB,6BAA6B,CAAC;AAElE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAgBD,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED,wBAAgB,yBAAyB,IAAI,cAAc,EAAE,CAqB5D;AAED,wBAAgB,mBAAmB,IAAI,cAAc,EAAE,CAUtD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAErE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,IAAI,CAKpE;AAcD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,iBAAiB,EACxB,WAAW,SAAY,EACvB,MAAM,UAAQ,GACb,kBAAkB,CA0CpB"}
1
+ {"version":3,"file":"sandbox-profiles.d.ts","sourceRoot":"","sources":["../../src/lib/sandbox-profiles.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,eAAO,MAAM,uBAAuB,6BAA6B,CAAC;AAElE,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAeD,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAED,wBAAgB,yBAAyB,IAAI,cAAc,EAAE,CAqB5D;AAED,wBAAgB,mBAAmB,IAAI,cAAc,EAAE,CAUtD;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAErE;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,IAAI,CAKpE;AAcD,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,iBAAiB,EACxB,WAAW,SAAY,EACvB,MAAM,UAAQ,GACb,kBAAkB,CA0CpB"}
@@ -2,7 +2,12 @@ import type { Database } from "bun:sqlite";
2
2
  import type { Plan, Project, Task, TaskComment, TaskPriority } from "../types/index.js";
3
3
  import type { TaskRun } from "../db/task-runs.js";
4
4
  import { type LocalTaskFieldQuery } from "./local-fields.js";
5
- export type SavedSearchScope = "all" | "tasks" | "projects" | "plans" | "runs" | "comments";
5
+ /**
6
+ * The single source of truth for search scopes. Both the type and `normalizeScope`
7
+ * derive from it, so `--scope` validation cannot drift from what the union allows.
8
+ */
9
+ export declare const SAVED_SEARCH_SCOPES: readonly ["all", "tasks", "projects", "plans", "runs", "comments"];
10
+ export type SavedSearchScope = (typeof SAVED_SEARCH_SCOPES)[number];
6
11
  export interface SavedSearchFilters {
7
12
  query?: string;
8
13
  project_id?: string;
@@ -50,6 +55,12 @@ export interface SaveSearchViewInput {
50
55
  scope?: SavedSearchScope;
51
56
  filters?: SavedSearchFilters;
52
57
  }
58
+ /**
59
+ * Coerce a stored/incoming scope to a member of `SAVED_SEARCH_SCOPES`, defaulting
60
+ * to "tasks". The default keeps persisted views readable if a scope is ever
61
+ * retired; it is NOT a substitute for validating user input, which the CLI does
62
+ * up front so a mistyped `--scope` fails loudly instead of silently searching tasks.
63
+ */
53
64
  export declare function normalizeScope(scope: string | undefined | null): SavedSearchScope;
54
65
  export declare function runSavedSearch(filters?: SavedSearchFilters, scope?: SavedSearchScope, db?: Database): SavedSearchRunResult;
55
66
  export declare function saveSearchView(input: SaveSearchViewInput, db?: Database): SavedSearchView;
@@ -1 +1 @@
1
- {"version":3,"file":"saved-search-views.d.ts","sourceRoot":"","sources":["../../src/lib/saved-search-views.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"saved-search-views.d.ts","sourceRoot":"","sources":["../../src/lib/saved-search-views.ts"],"names":[],"mappings":"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"}