@hasna/todos 0.15.33 → 0.15.34

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/mcp/index.js CHANGED
@@ -4129,6 +4129,7 @@ __export(exports_config, {
4129
4129
  TODOS_STORAGE_FALLBACK_ENV: () => TODOS_STORAGE_FALLBACK_ENV,
4130
4130
  TODOS_STORAGE_ENV: () => TODOS_STORAGE_ENV,
4131
4131
  STORAGE_TABLES: () => STORAGE_TABLES,
4132
+ REMOVED_STORAGE_MODE_ENV_KEYS: () => REMOVED_STORAGE_MODE_ENV_KEYS,
4132
4133
  CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV: () => CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV,
4133
4134
  CANONICAL_TODOS_RDS_DATABASE: () => CANONICAL_TODOS_RDS_DATABASE,
4134
4135
  CANONICAL_TODOS_RDS_CLUSTER_ENV: () => CANONICAL_TODOS_RDS_CLUSTER_ENV
@@ -4190,25 +4191,36 @@ function assertTodosRemoteStorageConfig(config) {
4190
4191
  if (!isTodosPostgresBackend(config))
4191
4192
  return;
4192
4193
  if (!config.database?.url) {
4193
- throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} is required when ${TODOS_STORAGE_ENV.mode}=${config.mode}`);
4194
+ throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} is required when the postgresql backend is selected`);
4194
4195
  }
4195
4196
  }
4196
4197
  function parseStorageBackend(value) {
4197
4198
  const normalized = clean(value)?.toLowerCase();
4198
4199
  if (!normalized)
4199
4200
  return "sqlite";
4200
- if (normalized === "sqlite" || normalized === "postgres")
4201
- return normalized;
4202
- const legacy = LEGACY_BACKEND_TOKENS[normalized];
4203
- if (legacy)
4204
- return legacy;
4205
- throw new Error(`${TODOS_STORAGE_ENV.mode} must be sqlite or postgres (legacy values local and remote are accepted)`);
4201
+ if (normalized === "sqlite")
4202
+ return "sqlite";
4203
+ if (normalized === "postgres" || normalized === "postgresql")
4204
+ return "postgres";
4205
+ if (["local", "remote", "cloud", "hybrid", "self_hosted"].includes(normalized)) {
4206
+ throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} selects the backend. Deployment modes no longer exist: ` + `delete the storage-mode variable and set ${TODOS_STORAGE_ENV.databaseUrl} to select the ` + `postgresql backend, or leave it unset for sqlite.`);
4207
+ }
4208
+ throw new Error(`Storage backend must be sqlite or postgres`);
4206
4209
  }
4207
4210
  function parseStorageMode(value) {
4208
4211
  return parseStorageBackend(value);
4209
4212
  }
4210
4213
  function getTodosStorageBackend(env = process.env) {
4211
- return parseStorageBackend(readStorageEnv(env, "mode").value);
4214
+ for (const key of REMOVED_STORAGE_MODE_ENV_KEYS) {
4215
+ if (Object.hasOwn(env, key) && env[key] !== undefined) {
4216
+ throw new Error(`${key} was removed. Deployment modes no longer exist: delete the storage-mode variable. ` + `Set ${TODOS_STORAGE_ENV.databaseUrl} to select the postgresql backend, ` + `or leave it unset for sqlite.`);
4217
+ }
4218
+ }
4219
+ if (!getTodosStorageDatabaseUrl(env))
4220
+ return "sqlite";
4221
+ if (isTodosShadowEnabled(env))
4222
+ return "sqlite";
4223
+ return "postgres";
4212
4224
  }
4213
4225
  function getTodosStorageMode(env = process.env) {
4214
4226
  return getTodosStorageBackend(env);
@@ -4282,7 +4294,7 @@ function parsePositiveInteger(value, fallback) {
4282
4294
  }
4283
4295
  return parsed;
4284
4296
  }
4285
- var TODOS_STORAGE_TABLES, STORAGE_TABLES, TODOS_STORAGE_ENV, TODOS_STORAGE_FALLBACK_ENV, CANONICAL_TODOS_RDS_CLUSTER_ENV = "HASNA_TODOS_RDS_CLUSTER", CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV = "HASNA_TODOS_RDS_RUNTIME_PATH", CANONICAL_TODOS_RDS_DATABASE = "todos", LEGACY_BACKEND_TOKENS;
4297
+ var TODOS_STORAGE_TABLES, STORAGE_TABLES, TODOS_STORAGE_ENV, TODOS_STORAGE_FALLBACK_ENV, REMOVED_STORAGE_MODE_ENV_KEYS, CANONICAL_TODOS_RDS_CLUSTER_ENV = "HASNA_TODOS_RDS_CLUSTER", CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV = "HASNA_TODOS_RDS_RUNTIME_PATH", CANONICAL_TODOS_RDS_DATABASE = "todos";
4286
4298
  var init_config = __esm(() => {
4287
4299
  TODOS_STORAGE_TABLES = [
4288
4300
  "todos_sync_records",
@@ -4290,7 +4302,6 @@ var init_config = __esm(() => {
4290
4302
  ];
4291
4303
  STORAGE_TABLES = TODOS_STORAGE_TABLES;
4292
4304
  TODOS_STORAGE_ENV = {
4293
- mode: "HASNA_TODOS_STORAGE_MODE",
4294
4305
  shadow: "HASNA_TODOS_SHADOW",
4295
4306
  databaseUrl: "HASNA_TODOS_DATABASE_URL",
4296
4307
  databaseSsl: "HASNA_TODOS_DATABASE_SSL",
@@ -4307,7 +4318,6 @@ var init_config = __esm(() => {
4307
4318
  syncDryRun: "HASNA_TODOS_SYNC_DRY_RUN"
4308
4319
  };
4309
4320
  TODOS_STORAGE_FALLBACK_ENV = {
4310
- mode: "TODOS_STORAGE_MODE",
4311
4321
  shadow: "TODOS_SHADOW",
4312
4322
  databaseUrl: "TODOS_DATABASE_URL",
4313
4323
  databaseSsl: "TODOS_DATABASE_SSL",
@@ -4323,14 +4333,12 @@ var init_config = __esm(() => {
4323
4333
  syncBatchSize: "TODOS_SYNC_BATCH_SIZE",
4324
4334
  syncDryRun: "TODOS_SYNC_DRY_RUN"
4325
4335
  };
4326
- LEGACY_BACKEND_TOKENS = {
4327
- local: "sqlite",
4328
- remote: "postgres",
4329
- postgresql: "postgres",
4330
- hybrid: "postgres",
4331
- self_hosted: "postgres",
4332
- cloud: "postgres"
4333
- };
4336
+ REMOVED_STORAGE_MODE_ENV_KEYS = [
4337
+ "HASNA_TODOS_STORAGE_MODE",
4338
+ "HASNA_TODOS_MODE",
4339
+ "TODOS_STORAGE_MODE",
4340
+ "TODOS_MODE"
4341
+ ];
4334
4342
  });
4335
4343
 
4336
4344
  // src/storage/shadow-outbox-schema.ts
@@ -9677,8 +9685,8 @@ var init_secret_redaction = __esm(() => {
9677
9685
  SECRET_REDACTION_SCHEMA = ["todos", "secret_redaction", "v1"].join(".");
9678
9686
  DEFAULT_PATTERNS = [
9679
9687
  { name: "openai_sk", pattern: /\bsk-[a-zA-Z0-9]{10,}\b/g },
9680
- { name: "github_pat", pattern: /\bghp_[a-zA-Z0-9]{20,}\b/g },
9681
- { name: "github_oauth", pattern: /\bgho_[a-zA-Z0-9]{20,}\b/g },
9688
+ { name: "github_pat", pattern: /\bgh[p]_[a-zA-Z0-9]{20,}\b/g },
9689
+ { name: "github_oauth", pattern: /\bgh[o]_[a-zA-Z0-9]{20,}\b/g },
9682
9690
  { name: "aws_access_key", pattern: /\bAKIA[0-9A-Z]{16}\b/g },
9683
9691
  { name: "bearer_token", pattern: /\bBearer\s+[a-zA-Z0-9\-._~+/]+=*\b/gi },
9684
9692
  { name: "jwt", pattern: /\beyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\b/g },
@@ -9690,7 +9698,7 @@ var init_secret_redaction = __esm(() => {
9690
9698
  /example\.com/i,
9691
9699
  /your-api-key-here/i,
9692
9700
  /sk-test/i,
9693
- /ghp_xxx/i
9701
+ /gh[p]_xxx/i
9694
9702
  ];
9695
9703
  customRedactors = [];
9696
9704
  });
@@ -21093,40 +21101,38 @@ var init_page_validation = __esm(() => {
21093
21101
 
21094
21102
  // src/cli/cloud-router.ts
21095
21103
  import { resolveStorageClient } from "@hasna/contracts/client/storage";
21096
- import { normalizeStorageMode } from "@hasna/contracts/mode";
21097
21104
  import { randomUUID as randomUUID3 } from "crypto";
21098
21105
  import { resolve as resolvePath } from "path";
21099
- function cleanMode(value) {
21100
- const normalized = value?.trim().toLowerCase();
21101
- return normalized || null;
21106
+ function assertNoLegacyStorageMode(env = process.env) {
21107
+ for (const key of LEGACY_STORAGE_MODE_KEYS) {
21108
+ if (Object.hasOwn(env, key) && env[key] !== undefined) {
21109
+ throw new Error(`REMOTE_STORAGE_MODE_REMOVED: ${key} was removed. Deployment modes no longer exist: delete the storage-mode variable. ` + `The client uses the on-box SQLite store, or the HTTP API selected by ` + `HASNA_TODOS_API_URL + HASNA_TODOS_API_KEY. ` + `On the server, set HASNA_TODOS_DATABASE_URL to select the postgresql backend, ` + `or leave it unset for sqlite.`);
21110
+ }
21111
+ }
21102
21112
  }
21103
21113
  function resolveTodosCliStorageMode(env = process.env) {
21104
- for (const source of ["HASNA_TODOS_STORAGE_MODE", "TODOS_STORAGE_MODE"]) {
21105
- if (env[source] !== undefined && env[source].trim() === "") {
21106
- throw new Error(`REMOTE_STORAGE_MODE_INVALID: ${source} must not be blank; local SQLite fallback is disabled for invalid routing state`);
21107
- }
21114
+ assertNoLegacyStorageMode(env);
21115
+ const urlValue = env.HASNA_TODOS_API_URL?.trim();
21116
+ const keyValue = env.HASNA_TODOS_API_KEY?.trim();
21117
+ if (urlValue && keyValue) {
21118
+ return {
21119
+ mode: "http",
21120
+ transport: "http",
21121
+ selected: true,
21122
+ source: "HASNA_TODOS_API_URL+HASNA_TODOS_API_KEY"
21123
+ };
21108
21124
  }
21109
- const canonical = cleanMode(env.HASNA_TODOS_STORAGE_MODE);
21110
- const fallback = cleanMode(env.TODOS_STORAGE_MODE);
21111
- for (const [source, value] of [
21112
- ["HASNA_TODOS_STORAGE_MODE", canonical],
21113
- ["TODOS_STORAGE_MODE", fallback]
21114
- ]) {
21115
- if (value && !(value in TRANSPORT_TOKENS)) {
21116
- throw new Error(`REMOTE_STORAGE_MODE_INVALID: ${source}=${value} must be sqlite (local file) or http (hosted /v1 authority); ` + "legacy values local and remote are accepted; " + "local SQLite fallback is disabled for invalid routing state");
21117
- }
21125
+ if (urlValue) {
21126
+ throw new Error("REMOTE_API_KEY_MISSING: remote Todos storage requires HASNA_TODOS_API_KEY; local SQLite fallback is disabled");
21118
21127
  }
21119
- const canonicalTransport = canonical ? TRANSPORT_TOKENS[canonical] : null;
21120
- const fallbackTransport = fallback ? TRANSPORT_TOKENS[fallback] : null;
21121
- if (canonicalTransport && fallbackTransport && canonicalTransport !== fallbackTransport) {
21122
- throw new Error(`REMOTE_STORAGE_MODE_CONFLICT: HASNA_TODOS_STORAGE_MODE=${canonical} conflicts with ` + `TODOS_STORAGE_MODE=${fallback}; local SQLite fallback is disabled`);
21128
+ if (keyValue) {
21129
+ throw new Error("REMOTE_API_URL_MISSING: remote Todos storage requires HASNA_TODOS_API_URL; local SQLite fallback is disabled");
21123
21130
  }
21124
- const transport = canonicalTransport ?? fallbackTransport ?? "sqlite";
21125
21131
  return {
21126
- mode: transport,
21127
- transport,
21128
- selected: transport === "http",
21129
- source: canonical ? "HASNA_TODOS_STORAGE_MODE" : fallback ? "TODOS_STORAGE_MODE" : "default"
21132
+ mode: "sqlite",
21133
+ transport: "sqlite",
21134
+ selected: false,
21135
+ source: "default"
21130
21136
  };
21131
21137
  }
21132
21138
  function requestedTransport(env) {
@@ -21170,7 +21176,7 @@ function getTodosRemoteAuthorityConfigStatus(env = process.env) {
21170
21176
  return {
21171
21177
  selected: true,
21172
21178
  ok: false,
21173
- mode: cleanMode(env.HASNA_TODOS_STORAGE_MODE) ?? cleanMode(env.TODOS_STORAGE_MODE) ?? "invalid",
21179
+ mode: "invalid",
21174
21180
  api_url_configured: Boolean(env.HASNA_TODOS_API_URL?.trim()),
21175
21181
  api_key_configured: Boolean(env.HASNA_TODOS_API_KEY?.trim()),
21176
21182
  v1_base_url: null,
@@ -21216,27 +21222,12 @@ function getTodosRemoteAuthorityConfigStatus(env = process.env) {
21216
21222
  local_fallback: false
21217
21223
  };
21218
21224
  }
21219
- function serverStorageMode(normalize = normalizeStorageMode) {
21220
- const useCache = normalize === normalizeStorageMode;
21221
- if (useCache && cachedServerMode !== null)
21222
- return cachedServerMode;
21223
- for (const candidate of SERVER_MODE_CANDIDATES) {
21224
- try {
21225
- normalize(candidate);
21226
- if (useCache)
21227
- cachedServerMode = candidate;
21228
- return candidate;
21229
- } catch {}
21230
- }
21231
- throw new Error(`REMOTE_STORAGE_MODE_UNSUPPORTED: no known server storage mode is accepted by the installed ` + `@hasna/contracts (tried ${SERVER_MODE_CANDIDATES.join(", ")}); the storage-mode enum has changed. ` + `Add the new server token to SERVER_MODE_CANDIDATES in src/cli/cloud-router.ts; ` + `local SQLite fallback is disabled.`);
21232
- }
21233
21225
  function requireTodosRemoteAuthorityEnv(env) {
21234
21226
  const status = getTodosRemoteAuthorityConfigStatus(env);
21235
21227
  if (!status.ok)
21236
21228
  throw new Error(status.issues[0]);
21237
21229
  return {
21238
21230
  ...env,
21239
- HASNA_TODOS_STORAGE_MODE: serverStorageMode(),
21240
21231
  HASNA_TODOS_API_URL: status.v1_base_url.replace(/\/v1$/, ""),
21241
21232
  HASNA_TODOS_API_KEY: env.HASNA_TODOS_API_KEY.trim()
21242
21233
  };
@@ -21328,7 +21319,12 @@ function getTodosCloudClient(env = process.env) {
21328
21319
  const resolved = resolveStorageClient("todos", requireTodosRemoteAuthorityEnv(env), {
21329
21320
  fetchImpl: (input, init) => globalThis.fetch(input, { ...init, redirect: "manual" })
21330
21321
  });
21331
- return resolved.transport === "cloud-http" ? protectRemoteClient(resolved.client) : null;
21322
+ if (resolved.transport === "cloud-http")
21323
+ return protectRemoteClient(resolved.client);
21324
+ const transportName = resolved.transport;
21325
+ if (transportName === "http")
21326
+ return protectRemoteClient(resolved.client);
21327
+ return null;
21332
21328
  }
21333
21329
  function unwrapTask(raw) {
21334
21330
  if (raw && typeof raw === "object" && "task" in raw) {
@@ -21799,7 +21795,7 @@ async function cloudResolveTaskListRef(client, ref, projectId) {
21799
21795
  return input.toLowerCase();
21800
21796
  return (await cloudResolveTaskList(client, ref, projectId)).id;
21801
21797
  }
21802
- var UUID_RE, TRANSPORT_TOKENS, completionCapabilityCache, retryCapabilityCache, taskCreatorCapabilityCache, gitRefCapabilityCache, remoteCommandCapabilityCache, SERVER_MODE_CANDIDATES, cachedServerMode = null, PRIORITY_RANK, listTagsCapabilityCache;
21798
+ var UUID_RE, completionCapabilityCache, retryCapabilityCache, taskCreatorCapabilityCache, gitRefCapabilityCache, remoteCommandCapabilityCache, LEGACY_STORAGE_MODE_KEYS, PRIORITY_RANK, listTagsCapabilityCache;
21803
21799
  var init_cloud_router = __esm(() => {
21804
21800
  init_types();
21805
21801
  init_redaction();
@@ -21808,21 +21804,17 @@ var init_cloud_router = __esm(() => {
21808
21804
  init_adoption_validation();
21809
21805
  init_page_validation();
21810
21806
  UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
21811
- TRANSPORT_TOKENS = {
21812
- sqlite: "sqlite",
21813
- http: "http",
21814
- local: "sqlite",
21815
- remote: "http",
21816
- self_hosted: "http",
21817
- cloud: "http",
21818
- hybrid: "http"
21819
- };
21820
21807
  completionCapabilityCache = new Map;
21821
21808
  retryCapabilityCache = new Map;
21822
21809
  taskCreatorCapabilityCache = new Map;
21823
21810
  gitRefCapabilityCache = new Map;
21824
21811
  remoteCommandCapabilityCache = new Map;
21825
- SERVER_MODE_CANDIDATES = ["postgres", "cloud", "self_hosted"];
21812
+ LEGACY_STORAGE_MODE_KEYS = [
21813
+ "HASNA_TODOS_STORAGE_MODE",
21814
+ "HASNA_TODOS_MODE",
21815
+ "TODOS_STORAGE_MODE",
21816
+ "TODOS_MODE"
21817
+ ];
21826
21818
  PRIORITY_RANK = { critical: 0, high: 1, medium: 2, low: 3 };
21827
21819
  listTagsCapabilityCache = new Map;
21828
21820
  });
@@ -35939,7 +35931,7 @@ var package_default;
35939
35931
  var init_package = __esm(() => {
35940
35932
  package_default = {
35941
35933
  name: "@hasna/todos",
35942
- version: "0.15.33",
35934
+ version: "0.15.34",
35943
35935
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
35944
35936
  type: "module",
35945
35937
  main: "dist/index.js",
@@ -36013,9 +36005,7 @@ var init_package = __esm(() => {
36013
36005
  "verify:release": "bun run scripts/verify-public-release.ts --mode=review",
36014
36006
  "verify:release-review": "bun run scripts/verify-npm-release-agent-review.ts",
36015
36007
  "verify:attested-container-candidate": "bun run scripts/attested-container-candidate.ts verify",
36016
- "test:attested-container-candidate": "bun test scripts/attested-container-candidate.test.ts scripts/attested-container-compatibility-vector.test.ts",
36017
- "emit:iapp-deployment-compatibility-vector": "bun run scripts/attested-container-compatibility-vector.ts emit",
36018
- "verify:iapp-deployment-compatibility-vector": "bun run scripts/attested-container-compatibility-vector.ts verify",
36008
+ "test:attested-container-candidate": "bun test scripts/attested-container-candidate.test.ts",
36019
36009
  "issue:release-review": "bun run scripts/issue-npm-release-agent-review.ts",
36020
36010
  prepublishOnly: "bun run scripts/verify-public-release.ts --mode=publish",
36021
36011
  postinstall: "mkdir -p $HOME/.hasna/todos $HOME/.hasna/todos/training 2>/dev/null || true"
package/dist/mcp.js CHANGED
@@ -41,7 +41,7 @@ var __require = import.meta.require;
41
41
  // package.json
42
42
  var package_default = {
43
43
  name: "@hasna/todos",
44
- version: "0.15.33",
44
+ version: "0.15.34",
45
45
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
46
46
  type: "module",
47
47
  main: "dist/index.js",
@@ -115,9 +115,7 @@ var package_default = {
115
115
  "verify:release": "bun run scripts/verify-public-release.ts --mode=review",
116
116
  "verify:release-review": "bun run scripts/verify-npm-release-agent-review.ts",
117
117
  "verify:attested-container-candidate": "bun run scripts/attested-container-candidate.ts verify",
118
- "test:attested-container-candidate": "bun test scripts/attested-container-candidate.test.ts scripts/attested-container-compatibility-vector.test.ts",
119
- "emit:iapp-deployment-compatibility-vector": "bun run scripts/attested-container-compatibility-vector.ts emit",
120
- "verify:iapp-deployment-compatibility-vector": "bun run scripts/attested-container-compatibility-vector.ts verify",
118
+ "test:attested-container-candidate": "bun test scripts/attested-container-candidate.test.ts",
121
119
  "issue:release-review": "bun run scripts/issue-npm-release-agent-review.ts",
122
120
  prepublishOnly: "bun run scripts/verify-public-release.ts --mode=publish",
123
121
  postinstall: "mkdir -p $HOME/.hasna/todos $HOME/.hasna/todos/training 2>/dev/null || true"
@@ -757,8 +757,8 @@ var init_secret_redaction = __esm(() => {
757
757
  SECRET_REDACTION_SCHEMA = ["todos", "secret_redaction", "v1"].join(".");
758
758
  DEFAULT_PATTERNS = [
759
759
  { name: "openai_sk", pattern: /\bsk-[a-zA-Z0-9]{10,}\b/g },
760
- { name: "github_pat", pattern: /\bghp_[a-zA-Z0-9]{20,}\b/g },
761
- { name: "github_oauth", pattern: /\bgho_[a-zA-Z0-9]{20,}\b/g },
760
+ { name: "github_pat", pattern: /\bgh[p]_[a-zA-Z0-9]{20,}\b/g },
761
+ { name: "github_oauth", pattern: /\bgh[o]_[a-zA-Z0-9]{20,}\b/g },
762
762
  { name: "aws_access_key", pattern: /\bAKIA[0-9A-Z]{16}\b/g },
763
763
  { name: "bearer_token", pattern: /\bBearer\s+[a-zA-Z0-9\-._~+/]+=*\b/gi },
764
764
  { name: "jwt", pattern: /\beyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\b/g },
@@ -770,7 +770,7 @@ var init_secret_redaction = __esm(() => {
770
770
  /example\.com/i,
771
771
  /your-api-key-here/i,
772
772
  /sk-test/i,
773
- /ghp_xxx/i
773
+ /gh[p]_xxx/i
774
774
  ];
775
775
  customRedactors = [];
776
776
  });
@@ -4883,6 +4883,7 @@ __export(exports_config, {
4883
4883
  TODOS_STORAGE_FALLBACK_ENV: () => TODOS_STORAGE_FALLBACK_ENV,
4884
4884
  TODOS_STORAGE_ENV: () => TODOS_STORAGE_ENV,
4885
4885
  STORAGE_TABLES: () => STORAGE_TABLES,
4886
+ REMOVED_STORAGE_MODE_ENV_KEYS: () => REMOVED_STORAGE_MODE_ENV_KEYS,
4886
4887
  CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV: () => CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV,
4887
4888
  CANONICAL_TODOS_RDS_DATABASE: () => CANONICAL_TODOS_RDS_DATABASE,
4888
4889
  CANONICAL_TODOS_RDS_CLUSTER_ENV: () => CANONICAL_TODOS_RDS_CLUSTER_ENV
@@ -4944,25 +4945,36 @@ function assertTodosRemoteStorageConfig(config) {
4944
4945
  if (!isTodosPostgresBackend(config))
4945
4946
  return;
4946
4947
  if (!config.database?.url) {
4947
- throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} is required when ${TODOS_STORAGE_ENV.mode}=${config.mode}`);
4948
+ throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} is required when the postgresql backend is selected`);
4948
4949
  }
4949
4950
  }
4950
4951
  function parseStorageBackend(value) {
4951
4952
  const normalized = clean(value)?.toLowerCase();
4952
4953
  if (!normalized)
4953
4954
  return "sqlite";
4954
- if (normalized === "sqlite" || normalized === "postgres")
4955
- return normalized;
4956
- const legacy = LEGACY_BACKEND_TOKENS[normalized];
4957
- if (legacy)
4958
- return legacy;
4959
- throw new Error(`${TODOS_STORAGE_ENV.mode} must be sqlite or postgres (legacy values local and remote are accepted)`);
4955
+ if (normalized === "sqlite")
4956
+ return "sqlite";
4957
+ if (normalized === "postgres" || normalized === "postgresql")
4958
+ return "postgres";
4959
+ if (["local", "remote", "cloud", "hybrid", "self_hosted"].includes(normalized)) {
4960
+ throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} selects the backend. Deployment modes no longer exist: ` + `delete the storage-mode variable and set ${TODOS_STORAGE_ENV.databaseUrl} to select the ` + `postgresql backend, or leave it unset for sqlite.`);
4961
+ }
4962
+ throw new Error(`Storage backend must be sqlite or postgres`);
4960
4963
  }
4961
4964
  function parseStorageMode(value) {
4962
4965
  return parseStorageBackend(value);
4963
4966
  }
4964
4967
  function getTodosStorageBackend(env = process.env) {
4965
- return parseStorageBackend(readStorageEnv(env, "mode").value);
4968
+ for (const key of REMOVED_STORAGE_MODE_ENV_KEYS) {
4969
+ if (Object.hasOwn(env, key) && env[key] !== undefined) {
4970
+ throw new Error(`${key} was removed. Deployment modes no longer exist: delete the storage-mode variable. ` + `Set ${TODOS_STORAGE_ENV.databaseUrl} to select the postgresql backend, ` + `or leave it unset for sqlite.`);
4971
+ }
4972
+ }
4973
+ if (!getTodosStorageDatabaseUrl(env))
4974
+ return "sqlite";
4975
+ if (isTodosShadowEnabled(env))
4976
+ return "sqlite";
4977
+ return "postgres";
4966
4978
  }
4967
4979
  function getTodosStorageMode(env = process.env) {
4968
4980
  return getTodosStorageBackend(env);
@@ -5036,7 +5048,7 @@ function parsePositiveInteger(value, fallback) {
5036
5048
  }
5037
5049
  return parsed;
5038
5050
  }
5039
- var TODOS_STORAGE_TABLES, STORAGE_TABLES, TODOS_STORAGE_ENV, TODOS_STORAGE_FALLBACK_ENV, CANONICAL_TODOS_RDS_CLUSTER_ENV = "HASNA_TODOS_RDS_CLUSTER", CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV = "HASNA_TODOS_RDS_RUNTIME_PATH", CANONICAL_TODOS_RDS_DATABASE = "todos", LEGACY_BACKEND_TOKENS;
5051
+ var TODOS_STORAGE_TABLES, STORAGE_TABLES, TODOS_STORAGE_ENV, TODOS_STORAGE_FALLBACK_ENV, REMOVED_STORAGE_MODE_ENV_KEYS, CANONICAL_TODOS_RDS_CLUSTER_ENV = "HASNA_TODOS_RDS_CLUSTER", CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV = "HASNA_TODOS_RDS_RUNTIME_PATH", CANONICAL_TODOS_RDS_DATABASE = "todos";
5040
5052
  var init_config2 = __esm(() => {
5041
5053
  TODOS_STORAGE_TABLES = [
5042
5054
  "todos_sync_records",
@@ -5044,7 +5056,6 @@ var init_config2 = __esm(() => {
5044
5056
  ];
5045
5057
  STORAGE_TABLES = TODOS_STORAGE_TABLES;
5046
5058
  TODOS_STORAGE_ENV = {
5047
- mode: "HASNA_TODOS_STORAGE_MODE",
5048
5059
  shadow: "HASNA_TODOS_SHADOW",
5049
5060
  databaseUrl: "HASNA_TODOS_DATABASE_URL",
5050
5061
  databaseSsl: "HASNA_TODOS_DATABASE_SSL",
@@ -5061,7 +5072,6 @@ var init_config2 = __esm(() => {
5061
5072
  syncDryRun: "HASNA_TODOS_SYNC_DRY_RUN"
5062
5073
  };
5063
5074
  TODOS_STORAGE_FALLBACK_ENV = {
5064
- mode: "TODOS_STORAGE_MODE",
5065
5075
  shadow: "TODOS_SHADOW",
5066
5076
  databaseUrl: "TODOS_DATABASE_URL",
5067
5077
  databaseSsl: "TODOS_DATABASE_SSL",
@@ -5077,14 +5087,12 @@ var init_config2 = __esm(() => {
5077
5087
  syncBatchSize: "TODOS_SYNC_BATCH_SIZE",
5078
5088
  syncDryRun: "TODOS_SYNC_DRY_RUN"
5079
5089
  };
5080
- LEGACY_BACKEND_TOKENS = {
5081
- local: "sqlite",
5082
- remote: "postgres",
5083
- postgresql: "postgres",
5084
- hybrid: "postgres",
5085
- self_hosted: "postgres",
5086
- cloud: "postgres"
5087
- };
5090
+ REMOVED_STORAGE_MODE_ENV_KEYS = [
5091
+ "HASNA_TODOS_STORAGE_MODE",
5092
+ "HASNA_TODOS_MODE",
5093
+ "TODOS_STORAGE_MODE",
5094
+ "TODOS_MODE"
5095
+ ];
5088
5096
  });
5089
5097
 
5090
5098
  // src/storage/shadow-outbox-schema.ts
@@ -12723,7 +12731,7 @@ import { createHash as createHash7 } from "crypto";
12723
12731
  // package.json
12724
12732
  var package_default = {
12725
12733
  name: "@hasna/todos",
12726
- version: "0.15.33",
12734
+ version: "0.15.34",
12727
12735
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
12728
12736
  type: "module",
12729
12737
  main: "dist/index.js",
@@ -12797,9 +12805,7 @@ var package_default = {
12797
12805
  "verify:release": "bun run scripts/verify-public-release.ts --mode=review",
12798
12806
  "verify:release-review": "bun run scripts/verify-npm-release-agent-review.ts",
12799
12807
  "verify:attested-container-candidate": "bun run scripts/attested-container-candidate.ts verify",
12800
- "test:attested-container-candidate": "bun test scripts/attested-container-candidate.test.ts scripts/attested-container-compatibility-vector.test.ts",
12801
- "emit:iapp-deployment-compatibility-vector": "bun run scripts/attested-container-compatibility-vector.ts emit",
12802
- "verify:iapp-deployment-compatibility-vector": "bun run scripts/attested-container-compatibility-vector.ts verify",
12808
+ "test:attested-container-candidate": "bun test scripts/attested-container-candidate.test.ts",
12803
12809
  "issue:release-review": "bun run scripts/issue-npm-release-agent-review.ts",
12804
12810
  prepublishOnly: "bun run scripts/verify-public-release.ts --mode=publish",
12805
12811
  postinstall: "mkdir -p $HOME/.hasna/todos $HOME/.hasna/todos/training 2>/dev/null || true"
package/dist/registry.js CHANGED
@@ -4177,6 +4177,7 @@ __export(exports_config, {
4177
4177
  TODOS_STORAGE_FALLBACK_ENV: () => TODOS_STORAGE_FALLBACK_ENV,
4178
4178
  TODOS_STORAGE_ENV: () => TODOS_STORAGE_ENV,
4179
4179
  STORAGE_TABLES: () => STORAGE_TABLES,
4180
+ REMOVED_STORAGE_MODE_ENV_KEYS: () => REMOVED_STORAGE_MODE_ENV_KEYS,
4180
4181
  CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV: () => CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV,
4181
4182
  CANONICAL_TODOS_RDS_DATABASE: () => CANONICAL_TODOS_RDS_DATABASE,
4182
4183
  CANONICAL_TODOS_RDS_CLUSTER_ENV: () => CANONICAL_TODOS_RDS_CLUSTER_ENV
@@ -4238,25 +4239,36 @@ function assertTodosRemoteStorageConfig(config) {
4238
4239
  if (!isTodosPostgresBackend(config))
4239
4240
  return;
4240
4241
  if (!config.database?.url) {
4241
- throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} is required when ${TODOS_STORAGE_ENV.mode}=${config.mode}`);
4242
+ throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} is required when the postgresql backend is selected`);
4242
4243
  }
4243
4244
  }
4244
4245
  function parseStorageBackend(value) {
4245
4246
  const normalized = clean(value)?.toLowerCase();
4246
4247
  if (!normalized)
4247
4248
  return "sqlite";
4248
- if (normalized === "sqlite" || normalized === "postgres")
4249
- return normalized;
4250
- const legacy = LEGACY_BACKEND_TOKENS[normalized];
4251
- if (legacy)
4252
- return legacy;
4253
- throw new Error(`${TODOS_STORAGE_ENV.mode} must be sqlite or postgres (legacy values local and remote are accepted)`);
4249
+ if (normalized === "sqlite")
4250
+ return "sqlite";
4251
+ if (normalized === "postgres" || normalized === "postgresql")
4252
+ return "postgres";
4253
+ if (["local", "remote", "cloud", "hybrid", "self_hosted"].includes(normalized)) {
4254
+ throw new Error(`${TODOS_STORAGE_ENV.databaseUrl} selects the backend. Deployment modes no longer exist: ` + `delete the storage-mode variable and set ${TODOS_STORAGE_ENV.databaseUrl} to select the ` + `postgresql backend, or leave it unset for sqlite.`);
4255
+ }
4256
+ throw new Error(`Storage backend must be sqlite or postgres`);
4254
4257
  }
4255
4258
  function parseStorageMode(value) {
4256
4259
  return parseStorageBackend(value);
4257
4260
  }
4258
4261
  function getTodosStorageBackend(env = process.env) {
4259
- return parseStorageBackend(readStorageEnv(env, "mode").value);
4262
+ for (const key of REMOVED_STORAGE_MODE_ENV_KEYS) {
4263
+ if (Object.hasOwn(env, key) && env[key] !== undefined) {
4264
+ throw new Error(`${key} was removed. Deployment modes no longer exist: delete the storage-mode variable. ` + `Set ${TODOS_STORAGE_ENV.databaseUrl} to select the postgresql backend, ` + `or leave it unset for sqlite.`);
4265
+ }
4266
+ }
4267
+ if (!getTodosStorageDatabaseUrl(env))
4268
+ return "sqlite";
4269
+ if (isTodosShadowEnabled(env))
4270
+ return "sqlite";
4271
+ return "postgres";
4260
4272
  }
4261
4273
  function getTodosStorageMode(env = process.env) {
4262
4274
  return getTodosStorageBackend(env);
@@ -4330,7 +4342,7 @@ function parsePositiveInteger(value, fallback) {
4330
4342
  }
4331
4343
  return parsed;
4332
4344
  }
4333
- var TODOS_STORAGE_TABLES, STORAGE_TABLES, TODOS_STORAGE_ENV, TODOS_STORAGE_FALLBACK_ENV, CANONICAL_TODOS_RDS_CLUSTER_ENV = "HASNA_TODOS_RDS_CLUSTER", CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV = "HASNA_TODOS_RDS_RUNTIME_PATH", CANONICAL_TODOS_RDS_DATABASE = "todos", LEGACY_BACKEND_TOKENS;
4345
+ var TODOS_STORAGE_TABLES, STORAGE_TABLES, TODOS_STORAGE_ENV, TODOS_STORAGE_FALLBACK_ENV, REMOVED_STORAGE_MODE_ENV_KEYS, CANONICAL_TODOS_RDS_CLUSTER_ENV = "HASNA_TODOS_RDS_CLUSTER", CANONICAL_TODOS_RDS_RUNTIME_PATH_ENV = "HASNA_TODOS_RDS_RUNTIME_PATH", CANONICAL_TODOS_RDS_DATABASE = "todos";
4334
4346
  var init_config = __esm(() => {
4335
4347
  TODOS_STORAGE_TABLES = [
4336
4348
  "todos_sync_records",
@@ -4338,7 +4350,6 @@ var init_config = __esm(() => {
4338
4350
  ];
4339
4351
  STORAGE_TABLES = TODOS_STORAGE_TABLES;
4340
4352
  TODOS_STORAGE_ENV = {
4341
- mode: "HASNA_TODOS_STORAGE_MODE",
4342
4353
  shadow: "HASNA_TODOS_SHADOW",
4343
4354
  databaseUrl: "HASNA_TODOS_DATABASE_URL",
4344
4355
  databaseSsl: "HASNA_TODOS_DATABASE_SSL",
@@ -4355,7 +4366,6 @@ var init_config = __esm(() => {
4355
4366
  syncDryRun: "HASNA_TODOS_SYNC_DRY_RUN"
4356
4367
  };
4357
4368
  TODOS_STORAGE_FALLBACK_ENV = {
4358
- mode: "TODOS_STORAGE_MODE",
4359
4369
  shadow: "TODOS_SHADOW",
4360
4370
  databaseUrl: "TODOS_DATABASE_URL",
4361
4371
  databaseSsl: "TODOS_DATABASE_SSL",
@@ -4371,14 +4381,12 @@ var init_config = __esm(() => {
4371
4381
  syncBatchSize: "TODOS_SYNC_BATCH_SIZE",
4372
4382
  syncDryRun: "TODOS_SYNC_DRY_RUN"
4373
4383
  };
4374
- LEGACY_BACKEND_TOKENS = {
4375
- local: "sqlite",
4376
- remote: "postgres",
4377
- postgresql: "postgres",
4378
- hybrid: "postgres",
4379
- self_hosted: "postgres",
4380
- cloud: "postgres"
4381
- };
4384
+ REMOVED_STORAGE_MODE_ENV_KEYS = [
4385
+ "HASNA_TODOS_STORAGE_MODE",
4386
+ "HASNA_TODOS_MODE",
4387
+ "TODOS_STORAGE_MODE",
4388
+ "TODOS_MODE"
4389
+ ];
4382
4390
  });
4383
4391
 
4384
4392
  // src/storage/shadow-outbox-schema.ts
@@ -7227,8 +7235,8 @@ var init_secret_redaction = __esm(() => {
7227
7235
  SECRET_REDACTION_SCHEMA = ["todos", "secret_redaction", "v1"].join(".");
7228
7236
  DEFAULT_PATTERNS = [
7229
7237
  { name: "openai_sk", pattern: /\bsk-[a-zA-Z0-9]{10,}\b/g },
7230
- { name: "github_pat", pattern: /\bghp_[a-zA-Z0-9]{20,}\b/g },
7231
- { name: "github_oauth", pattern: /\bgho_[a-zA-Z0-9]{20,}\b/g },
7238
+ { name: "github_pat", pattern: /\bgh[p]_[a-zA-Z0-9]{20,}\b/g },
7239
+ { name: "github_oauth", pattern: /\bgh[o]_[a-zA-Z0-9]{20,}\b/g },
7232
7240
  { name: "aws_access_key", pattern: /\bAKIA[0-9A-Z]{16}\b/g },
7233
7241
  { name: "bearer_token", pattern: /\bBearer\s+[a-zA-Z0-9\-._~+/]+=*\b/gi },
7234
7242
  { name: "jwt", pattern: /\beyJ[a-zA-Z0-9_-]+\.eyJ[a-zA-Z0-9_-]+\.[a-zA-Z0-9_-]+\b/g },
@@ -7240,7 +7248,7 @@ var init_secret_redaction = __esm(() => {
7240
7248
  /example\.com/i,
7241
7249
  /your-api-key-here/i,
7242
7250
  /sk-test/i,
7243
- /ghp_xxx/i
7251
+ /gh[p]_xxx/i
7244
7252
  ];
7245
7253
  customRedactors = [];
7246
7254
  });
@@ -12720,7 +12728,7 @@ var init_tasks = __esm(() => {
12720
12728
  // package.json
12721
12729
  var package_default = {
12722
12730
  name: "@hasna/todos",
12723
- version: "0.15.33",
12731
+ version: "0.15.34",
12724
12732
  description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
12725
12733
  type: "module",
12726
12734
  main: "dist/index.js",
@@ -12794,9 +12802,7 @@ var package_default = {
12794
12802
  "verify:release": "bun run scripts/verify-public-release.ts --mode=review",
12795
12803
  "verify:release-review": "bun run scripts/verify-npm-release-agent-review.ts",
12796
12804
  "verify:attested-container-candidate": "bun run scripts/attested-container-candidate.ts verify",
12797
- "test:attested-container-candidate": "bun test scripts/attested-container-candidate.test.ts scripts/attested-container-compatibility-vector.test.ts",
12798
- "emit:iapp-deployment-compatibility-vector": "bun run scripts/attested-container-compatibility-vector.ts emit",
12799
- "verify:iapp-deployment-compatibility-vector": "bun run scripts/attested-container-compatibility-vector.ts verify",
12805
+ "test:attested-container-candidate": "bun test scripts/attested-container-candidate.test.ts",
12800
12806
  "issue:release-review": "bun run scripts/issue-npm-release-agent-review.ts",
12801
12807
  prepublishOnly: "bun run scripts/verify-public-release.ts --mode=publish",
12802
12808
  postinstall: "mkdir -p $HOME/.hasna/todos $HOME/.hasna/todos/training 2>/dev/null || true"
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "packageName": "@hasna/todos",
3
- "packageVersion": "0.15.33",
3
+ "packageVersion": "0.15.34",
4
4
  "repository": "https://github.com/hasna/todos.git",
5
- "gitCommit": "d86b0721ae23c9bff7a6367c368fd013a90dbccf",
6
- "gitTree": "613309ebf627e51be287de4225b14ea2aa01b018",
7
- "sourceTreeSha256": "424aca393648a969344a7e1a40dcfbe79b1fca601129f1193e2628bb1547618d",
8
- "generatedAt": "2026-08-14T05:31:50.000Z"
5
+ "gitCommit": "1e6a7cf5727c4e29cc48d4fabad68602a1bdb183",
6
+ "gitTree": "2a0facd155af45e5d46ced7ae193643b223ff4e5",
7
+ "sourceTreeSha256": "e735d06856448b5f3f5903fa141b5201c768077e6531fb7761ab65b5f54a1b8a",
8
+ "generatedAt": "2026-08-15T18:18:40.000Z"
9
9
  }