@hasna/todos 0.11.35 → 0.11.37

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.
@@ -1,6 +1,7 @@
1
1
  import { Command } from "commander";
2
+ import { getPackageVersion } from "../lib/package-version.js";
2
3
  import type { Project, Task } from "../types/index.js";
3
- export declare function getPackageVersion(): string;
4
+ export { getPackageVersion };
4
5
  export declare function handleError(e: unknown): never;
5
6
  export declare function setProgramRef(p: Command): void;
6
7
  export declare function resolveTaskId(partialId: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/cli/helpers.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEvD,wBAAgB,iBAAiB,IAAI,MAAM,CAO1C;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,KAAK,CAc7C;AAID,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,QAA4B;AAEpE,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAcvD;AAED,wBAAgB,aAAa,IAAI,MAAM,GAAG,IAAI,CAM7C;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,GAAG,SAAS,CAUjF;AAMD,wBAAgB,WAAW,CAAC,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,SAAS,CAE1E;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAUjD;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,GAAG,MAAM,EAAE,CAGlF;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI,CAI7D;AAED,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAM9D,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAKhE,CAAC;AAEF,wBAAgB,cAAc,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAQ9C"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/cli/helpers.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAGpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,EAAE,iBAAiB,EAAE,CAAC;AAE7B,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,KAAK,CAc7C;AAID,wBAAgB,aAAa,CAAC,CAAC,EAAE,OAAO,QAA4B;AAEpE,wBAAgB,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAcvD;AAED,wBAAgB,aAAa,IAAI,MAAM,GAAG,IAAI,CAM7C;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,GAAG,SAAS,CAUjF;AAMD,wBAAgB,WAAW,CAAC,IAAI,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,GAAG,SAAS,CAE1E;AAED,4EAA4E;AAC5E,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAUjD;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,GAAG,MAAM,EAAE,CAGlF;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI,CAI7D;AAED,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAM9D,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAKhE,CAAC;AAEF,wBAAgB,cAAc,CAAC,CAAC,EAAE,IAAI,GAAG,MAAM,CAQ9C"}
package/dist/cli/index.js CHANGED
@@ -4063,6 +4063,30 @@ var init_projects = __esm(() => {
4063
4063
  init_machines();
4064
4064
  });
4065
4065
 
4066
+ // src/lib/package-version.ts
4067
+ import { existsSync as existsSync2, readFileSync } from "fs";
4068
+ import { dirname as dirname2, join as join2 } from "path";
4069
+ import { fileURLToPath } from "url";
4070
+ function getPackageVersion(fromUrl = import.meta.url) {
4071
+ try {
4072
+ let dir = dirname2(fileURLToPath(fromUrl));
4073
+ for (let i = 0;i < 5; i++) {
4074
+ const pkgPath = join2(dir, "package.json");
4075
+ if (existsSync2(pkgPath)) {
4076
+ return JSON.parse(readFileSync(pkgPath, "utf-8")).version || "0.0.0";
4077
+ }
4078
+ const parent = dirname2(dir);
4079
+ if (parent === dir)
4080
+ break;
4081
+ dir = parent;
4082
+ }
4083
+ } catch {
4084
+ return "0.0.0";
4085
+ }
4086
+ return "0.0.0";
4087
+ }
4088
+ var init_package_version = () => {};
4089
+
4066
4090
  // src/cli/helpers.ts
4067
4091
  var exports_helpers = {};
4068
4092
  __export(exports_helpers, {
@@ -4082,17 +4106,7 @@ __export(exports_helpers, {
4082
4106
  });
4083
4107
  import chalk from "chalk";
4084
4108
  import { execSync } from "child_process";
4085
- import { readFileSync } from "fs";
4086
- import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
4087
- import { fileURLToPath } from "url";
4088
- function getPackageVersion() {
4089
- try {
4090
- const pkgPath = join2(dirname2(fileURLToPath(import.meta.url)), "..", "..", "package.json");
4091
- return JSON.parse(readFileSync(pkgPath, "utf-8")).version || "0.0.0";
4092
- } catch {
4093
- return "0.0.0";
4094
- }
4095
- }
4109
+ import { resolve as resolve2 } from "path";
4096
4110
  function handleError(e) {
4097
4111
  let jsonMode = false;
4098
4112
  try {
@@ -4189,6 +4203,7 @@ var programForOptions = null, statusColors, priorityColors;
4189
4203
  var init_helpers = __esm(() => {
4190
4204
  init_database();
4191
4205
  init_projects();
4206
+ init_package_version();
4192
4207
  statusColors = {
4193
4208
  pending: chalk.yellow,
4194
4209
  in_progress: chalk.blue,
@@ -4205,14 +4220,14 @@ var init_helpers = __esm(() => {
4205
4220
  });
4206
4221
 
4207
4222
  // src/lib/sync-utils.ts
4208
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync, statSync, writeFileSync } from "fs";
4223
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync, statSync, writeFileSync } from "fs";
4209
4224
  import { join as join3 } from "path";
4210
4225
  function ensureDir2(dir) {
4211
- if (!existsSync2(dir))
4226
+ if (!existsSync3(dir))
4212
4227
  mkdirSync2(dir, { recursive: true });
4213
4228
  }
4214
4229
  function listJsonFiles(dir) {
4215
- if (!existsSync2(dir))
4230
+ if (!existsSync3(dir))
4216
4231
  return [];
4217
4232
  return readdirSync(dir).filter((f) => f.endsWith(".json"));
4218
4233
  }
@@ -4229,7 +4244,7 @@ function writeJsonFile(path, data) {
4229
4244
  }
4230
4245
  function readHighWaterMark(dir) {
4231
4246
  const path = join3(dir, ".highwatermark");
4232
- if (!existsSync2(path))
4247
+ if (!existsSync3(path))
4233
4248
  return 1;
4234
4249
  const val = parseInt(readFileSync2(path, "utf-8").trim(), 10);
4235
4250
  return isNaN(val) ? 1 : val;
@@ -4272,7 +4287,7 @@ __export(exports_config, {
4272
4287
  getAgentTaskListId: () => getAgentTaskListId,
4273
4288
  getAgentPoolForProject: () => getAgentPoolForProject
4274
4289
  });
4275
- import { existsSync as existsSync3 } from "fs";
4290
+ import { existsSync as existsSync4 } from "fs";
4276
4291
  import { join as join4 } from "path";
4277
4292
  function getTodosGlobalDir() {
4278
4293
  const home = process.env["HOME"] || HOME;
@@ -4280,7 +4295,7 @@ function getTodosGlobalDir() {
4280
4295
  const legacyDir = join4(home, ".todos");
4281
4296
  const newConfig = join4(newDir, "config.json");
4282
4297
  const legacyConfig = join4(legacyDir, "config.json");
4283
- if (!existsSync3(newConfig) && existsSync3(legacyConfig))
4298
+ if (!existsSync4(newConfig) && existsSync4(legacyConfig))
4284
4299
  return legacyDir;
4285
4300
  return newDir;
4286
4301
  }
@@ -4296,7 +4311,7 @@ function normalizeAgent(agent) {
4296
4311
  function loadConfig() {
4297
4312
  if (cached)
4298
4313
  return cached;
4299
- if (!existsSync3(getConfigPath())) {
4314
+ if (!existsSync4(getConfigPath())) {
4300
4315
  cached = {};
4301
4316
  return cached;
4302
4317
  }
@@ -7556,9 +7571,31 @@ function isBlockedAgentName(name) {
7556
7571
  const normalized = normalizeAgentNameInput(name);
7557
7572
  return isGenericAgentName(normalized) || hasGeneratedNumericSuffix(normalized) || !ONE_WORD_NAME_RE.test(normalized);
7558
7573
  }
7574
+ function alphabeticSuffix(index) {
7575
+ const letters = "abcdefghijklmnopqrstuvwxyz";
7576
+ let value = index;
7577
+ let suffix = "";
7578
+ do {
7579
+ suffix = letters[value % letters.length] + suffix;
7580
+ value = Math.floor(value / letters.length) - 1;
7581
+ } while (value >= 0);
7582
+ return suffix;
7583
+ }
7559
7584
  function suggestAgentNames(existingNames = []) {
7560
7585
  const existing = new Set([...existingNames].map(normalizeAgentNameInput));
7561
- return PREFERRED_AGENT_NAMES.filter((name) => !existing.has(name));
7586
+ const suggestions = PREFERRED_AGENT_NAMES.filter((name) => !existing.has(name));
7587
+ for (let suffixIndex = 0;suggestions.length < 20 && suffixIndex < 1000; suffixIndex++) {
7588
+ const suffix = alphabeticSuffix(suffixIndex);
7589
+ for (const base of PREFERRED_AGENT_NAMES) {
7590
+ const candidate = `${base}${suffix}`;
7591
+ if (existing.has(candidate) || suggestions.includes(candidate))
7592
+ continue;
7593
+ suggestions.push(candidate);
7594
+ if (suggestions.length >= 20)
7595
+ break;
7596
+ }
7597
+ }
7598
+ return suggestions;
7562
7599
  }
7563
7600
  function validateAgentName(name, existingNames = []) {
7564
7601
  const normalized = normalizeAgentNameInput(name);
@@ -8968,7 +9005,7 @@ __export(exports_serve, {
8968
9005
  SECURITY_HEADERS: () => SECURITY_HEADERS,
8969
9006
  MIME_TYPES: () => MIME_TYPES
8970
9007
  });
8971
- import { existsSync as existsSync6 } from "fs";
9008
+ import { existsSync as existsSync7 } from "fs";
8972
9009
  import { join as join9, dirname as dirname3, extname } from "path";
8973
9010
  import { fileURLToPath as fileURLToPath2 } from "url";
8974
9011
  function resolveDashboardDir() {
@@ -8985,7 +9022,7 @@ function resolveDashboardDir() {
8985
9022
  }
8986
9023
  candidates.push(join9(process.cwd(), "dashboard", "dist"));
8987
9024
  for (const candidate of candidates) {
8988
- if (existsSync6(candidate))
9025
+ if (existsSync7(candidate))
8989
9026
  return candidate;
8990
9027
  }
8991
9028
  return join9(process.cwd(), "dashboard", "dist");
@@ -9038,7 +9075,7 @@ function json(data, status = 200, headers) {
9038
9075
  });
9039
9076
  }
9040
9077
  function serveStaticFile(filePath) {
9041
- if (!existsSync6(filePath))
9078
+ if (!existsSync7(filePath))
9042
9079
  return null;
9043
9080
  const ext = extname(filePath);
9044
9081
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
@@ -9117,7 +9154,7 @@ data: ${data}
9117
9154
  filteredSseClients.delete(client);
9118
9155
  }
9119
9156
  const dashboardDir = resolveDashboardDir();
9120
- const dashboardExists = existsSync6(dashboardDir);
9157
+ const dashboardExists = existsSync7(dashboardDir);
9121
9158
  if (!dashboardExists) {
9122
9159
  console.error(`
9123
9160
  Dashboard not found at: ${dashboardDir}`);
@@ -11060,7 +11097,7 @@ __export(exports_dist, {
11060
11097
  import { createRequire } from "module";
11061
11098
  import { Database as Database2 } from "bun:sqlite";
11062
11099
  import {
11063
- existsSync as existsSync8,
11100
+ existsSync as existsSync9,
11064
11101
  mkdirSync as mkdirSync4,
11065
11102
  readdirSync as readdirSync3,
11066
11103
  copyFileSync
@@ -11974,9 +12011,9 @@ function getDbPath2(serviceName) {
11974
12011
  function migrateDotfile(serviceName) {
11975
12012
  const legacyDir = join11(homedir(), `.${serviceName}`);
11976
12013
  const newDir = join11(HASNA_DIR, serviceName);
11977
- if (!existsSync8(legacyDir))
12014
+ if (!existsSync9(legacyDir))
11978
12015
  return [];
11979
- if (existsSync8(newDir))
12016
+ if (existsSync9(newDir))
11980
12017
  return [];
11981
12018
  mkdirSync4(newDir, { recursive: true });
11982
12019
  const migrated = [];
@@ -11998,7 +12035,7 @@ function copyDirRecursive(src, dest, root, migrated) {
11998
12035
  }
11999
12036
  }
12000
12037
  function hasLegacyDotfile(serviceName) {
12001
- return existsSync8(join11(homedir(), `.${serviceName}`));
12038
+ return existsSync9(join11(homedir(), `.${serviceName}`));
12002
12039
  }
12003
12040
  function getHasnaDir() {
12004
12041
  mkdirSync4(HASNA_DIR, { recursive: true });
@@ -31672,23 +31709,11 @@ __export(exports_mcp, {
31672
31709
  });
31673
31710
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
31674
31711
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
31675
- import { existsSync as existsSync9, readFileSync as readFileSync7 } from "fs";
31676
- import { join as join12, dirname as dirname6 } from "path";
31677
- import { fileURLToPath as fileURLToPath3 } from "url";
31678
31712
  function getMcpVersion() {
31679
- try {
31680
- let dir = dirname6(fileURLToPath3(import.meta.url));
31681
- for (let i = 0;i < 4; i++) {
31682
- const pkgPath = join12(dir, "package.json");
31683
- if (existsSync9(pkgPath)) {
31684
- return JSON.parse(readFileSync7(pkgPath, "utf-8")).version || "0.0.0";
31685
- }
31686
- dir = dirname6(dir);
31687
- }
31688
- } catch {
31689
- return "0.0.0";
31690
- }
31691
- return "0.0.0";
31713
+ return getPackageVersion(import.meta.url);
31714
+ }
31715
+ function hasVersionFlag() {
31716
+ return process.argv.includes("--version") || process.argv.includes("-V");
31692
31717
  }
31693
31718
  function shouldRegisterTool(name) {
31694
31719
  if (TODOS_PROFILE === "minimal")
@@ -31850,6 +31875,11 @@ var init_mcp = __esm(() => {
31850
31875
  init_code_tools();
31851
31876
  init_machines2();
31852
31877
  init_agents2();
31878
+ init_package_version();
31879
+ if (hasVersionFlag()) {
31880
+ console.log(getMcpVersion());
31881
+ process.exit(0);
31882
+ }
31853
31883
  server = new McpServer({
31854
31884
  name: "todos",
31855
31885
  version: getMcpVersion()
@@ -33012,7 +33042,7 @@ import { basename as basename2, resolve as resolve4 } from "path";
33012
33042
  init_tasks();
33013
33043
  init_config();
33014
33044
  init_sync_utils();
33015
- import { existsSync as existsSync4, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "fs";
33045
+ import { existsSync as existsSync5, readFileSync as readFileSync3, readdirSync as readdirSync2, writeFileSync as writeFileSync2 } from "fs";
33016
33046
  import { join as join5 } from "path";
33017
33047
  function getTaskListDir(taskListId) {
33018
33048
  return join5(HOME, ".claude", "tasks", taskListId);
@@ -33034,7 +33064,7 @@ function toSqliteStatus(status) {
33034
33064
  }
33035
33065
  function readPrefixCounter(dir) {
33036
33066
  const path = join5(dir, ".prefix-counter");
33037
- if (!existsSync4(path))
33067
+ if (!existsSync5(path))
33038
33068
  return 0;
33039
33069
  const val = parseInt(readFileSync3(path, "utf-8").trim(), 10);
33040
33070
  return isNaN(val) ? 0 : val;
@@ -33067,7 +33097,7 @@ function taskToClaudeTask(task, claudeTaskId, existingMeta) {
33067
33097
  }
33068
33098
  function pushToClaudeTaskList(taskListId, projectId, options = {}) {
33069
33099
  const dir = getTaskListDir(taskListId);
33070
- if (!existsSync4(dir))
33100
+ if (!existsSync5(dir))
33071
33101
  ensureDir2(dir);
33072
33102
  const filter = {};
33073
33103
  if (projectId)
@@ -33163,7 +33193,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
33163
33193
  }
33164
33194
  function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
33165
33195
  const dir = getTaskListDir(taskListId);
33166
- if (!existsSync4(dir)) {
33196
+ if (!existsSync5(dir)) {
33167
33197
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
33168
33198
  }
33169
33199
  const files = readdirSync2(dir).filter((f) => f.endsWith(".json"));
@@ -33254,12 +33284,12 @@ function syncClaudeTaskList(taskListId, projectId, options = {}) {
33254
33284
  init_tasks();
33255
33285
  init_sync_utils();
33256
33286
  init_config();
33257
- import { existsSync as existsSync5 } from "fs";
33287
+ import { existsSync as existsSync6 } from "fs";
33258
33288
  import { join as join6 } from "path";
33259
33289
  function getTodosGlobalDir2() {
33260
33290
  const newDir = join6(HOME, ".hasna", "todos");
33261
33291
  const legacyDir = join6(HOME, ".todos");
33262
- if (!existsSync5(newDir) && existsSync5(legacyDir))
33292
+ if (!existsSync6(newDir) && existsSync6(legacyDir))
33263
33293
  return legacyDir;
33264
33294
  return newDir;
33265
33295
  }
@@ -33299,7 +33329,7 @@ function metadataKey(agent) {
33299
33329
  }
33300
33330
  function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
33301
33331
  const dir = getTaskListDir2(agent, taskListId);
33302
- if (!existsSync5(dir))
33332
+ if (!existsSync6(dir))
33303
33333
  ensureDir2(dir);
33304
33334
  const filter = {};
33305
33335
  if (projectId)
@@ -33382,7 +33412,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
33382
33412
  }
33383
33413
  function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
33384
33414
  const dir = getTaskListDir2(agent, taskListId);
33385
- if (!existsSync5(dir)) {
33415
+ if (!existsSync6(dir)) {
33386
33416
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
33387
33417
  }
33388
33418
  const files = listJsonFiles(dir);
@@ -34276,7 +34306,7 @@ init_tasks();
34276
34306
  init_config();
34277
34307
  init_helpers();
34278
34308
  import chalk6 from "chalk";
34279
- import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
34309
+ import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
34280
34310
  import { dirname as dirname4, join as join10 } from "path";
34281
34311
  function registerConfigServeCommands(program2) {
34282
34312
  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) => {
@@ -34284,7 +34314,7 @@ function registerConfigServeCommands(program2) {
34284
34314
  const home = process.env["HOME"] || "~";
34285
34315
  const newPath = join10(home, ".hasna", "todos", "config.json");
34286
34316
  const legacyPath = join10(home, ".todos", "config.json");
34287
- const configPath = !existsSync7(newPath) && existsSync7(legacyPath) ? legacyPath : newPath;
34317
+ const configPath = !existsSync8(newPath) && existsSync8(legacyPath) ? legacyPath : newPath;
34288
34318
  if (opts.get) {
34289
34319
  const config2 = loadConfig();
34290
34320
  const keys = opts.get.split(".");
@@ -34321,7 +34351,7 @@ function registerConfigServeCommands(program2) {
34321
34351
  }
34322
34352
  obj[keys[keys.length - 1]] = parsedValue;
34323
34353
  const dir = dirname4(configPath);
34324
- if (!existsSync7(dir))
34354
+ if (!existsSync8(dir))
34325
34355
  mkdirSync3(dir, { recursive: true });
34326
34356
  writeFileSync3(configPath, JSON.stringify(config2, null, 2));
34327
34357
  if (globalOpts.json) {
@@ -36034,8 +36064,8 @@ init_tasks();
36034
36064
  init_helpers();
36035
36065
  import chalk9 from "chalk";
36036
36066
  import { execSync as execSync3 } from "child_process";
36037
- import { existsSync as existsSync10, readFileSync as readFileSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, chmodSync } from "fs";
36038
- import { dirname as dirname7, join as join13 } from "path";
36067
+ import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync5, mkdirSync as mkdirSync5, chmodSync } from "fs";
36068
+ import { dirname as dirname6, join as join12 } from "path";
36039
36069
  var HOME2 = process.env["HOME"] || process.env["USERPROFILE"] || "~";
36040
36070
  function getMcpBinaryPath() {
36041
36071
  try {
@@ -36043,7 +36073,7 @@ function getMcpBinaryPath() {
36043
36073
  if (p)
36044
36074
  return p;
36045
36075
  } catch {}
36046
- const bunBin = join13(HOME2, ".bun", "bin", "todos-mcp");
36076
+ const bunBin = join12(HOME2, ".bun", "bin", "todos-mcp");
36047
36077
  if (existsSync10(bunBin))
36048
36078
  return bunBin;
36049
36079
  return "todos-mcp";
@@ -36052,13 +36082,13 @@ function readJsonFile2(path) {
36052
36082
  if (!existsSync10(path))
36053
36083
  return {};
36054
36084
  try {
36055
- return JSON.parse(readFileSync8(path, "utf-8"));
36085
+ return JSON.parse(readFileSync7(path, "utf-8"));
36056
36086
  } catch {
36057
36087
  return {};
36058
36088
  }
36059
36089
  }
36060
36090
  function writeJsonFile2(path, data) {
36061
- const dir = dirname7(path);
36091
+ const dir = dirname6(path);
36062
36092
  if (!existsSync10(dir))
36063
36093
  mkdirSync5(dir, { recursive: true });
36064
36094
  writeFileSync5(path, JSON.stringify(data, null, 2) + `
@@ -36067,10 +36097,10 @@ function writeJsonFile2(path, data) {
36067
36097
  function readTomlFile(path) {
36068
36098
  if (!existsSync10(path))
36069
36099
  return "";
36070
- return readFileSync8(path, "utf-8");
36100
+ return readFileSync7(path, "utf-8");
36071
36101
  }
36072
36102
  function writeTomlFile(path, content) {
36073
- const dir = dirname7(path);
36103
+ const dir = dirname6(path);
36074
36104
  if (!existsSync10(dir))
36075
36105
  mkdirSync5(dir, { recursive: true });
36076
36106
  writeFileSync5(path, content);
@@ -36117,7 +36147,7 @@ function unregisterClaude(_global) {
36117
36147
  }
36118
36148
  }
36119
36149
  function registerCodex(binPath) {
36120
- const configPath = join13(HOME2, ".codex", "config.toml");
36150
+ const configPath = join12(HOME2, ".codex", "config.toml");
36121
36151
  let content = readTomlFile(configPath);
36122
36152
  content = removeTomlBlock(content, "mcp_servers.todos");
36123
36153
  const block = `
@@ -36131,7 +36161,7 @@ args = []
36131
36161
  console.log(chalk9.green(`Codex CLI: registered in ${configPath}`));
36132
36162
  }
36133
36163
  function unregisterCodex() {
36134
- const configPath = join13(HOME2, ".codex", "config.toml");
36164
+ const configPath = join12(HOME2, ".codex", "config.toml");
36135
36165
  let content = readTomlFile(configPath);
36136
36166
  if (!content.includes("[mcp_servers.todos]")) {
36137
36167
  console.log(chalk9.dim(`Codex CLI: todos not found in ${configPath}`));
@@ -36143,7 +36173,7 @@ function unregisterCodex() {
36143
36173
  console.log(chalk9.green(`Codex CLI: unregistered from ${configPath}`));
36144
36174
  }
36145
36175
  function registerGemini(binPath) {
36146
- const configPath = join13(HOME2, ".gemini", "settings.json");
36176
+ const configPath = join12(HOME2, ".gemini", "settings.json");
36147
36177
  const config = readJsonFile2(configPath);
36148
36178
  if (!config["mcpServers"]) {
36149
36179
  config["mcpServers"] = {};
@@ -36157,7 +36187,7 @@ function registerGemini(binPath) {
36157
36187
  console.log(chalk9.green(`Gemini CLI: registered in ${configPath}`));
36158
36188
  }
36159
36189
  function unregisterGemini() {
36160
- const configPath = join13(HOME2, ".gemini", "settings.json");
36190
+ const configPath = join12(HOME2, ".gemini", "settings.json");
36161
36191
  const config = readJsonFile2(configPath);
36162
36192
  const servers = config["mcpServers"];
36163
36193
  if (!servers || !("todos" in servers)) {
@@ -36214,7 +36244,7 @@ function registerMcpHooksCommands(program2) {
36214
36244
  if (p)
36215
36245
  todosBin = p;
36216
36246
  } catch {}
36217
- const hooksDir = join13(process.cwd(), ".claude", "hooks");
36247
+ const hooksDir = join12(process.cwd(), ".claude", "hooks");
36218
36248
  if (!existsSync10(hooksDir))
36219
36249
  mkdirSync5(hooksDir, { recursive: true });
36220
36250
  const hookScript = `#!/usr/bin/env bash
@@ -36240,11 +36270,11 @@ esac
36240
36270
 
36241
36271
  exit 0
36242
36272
  `;
36243
- const hookPath = join13(hooksDir, "todos-sync.sh");
36273
+ const hookPath = join12(hooksDir, "todos-sync.sh");
36244
36274
  writeFileSync5(hookPath, hookScript);
36245
36275
  execSync3(`chmod +x "${hookPath}"`);
36246
36276
  console.log(chalk9.green(`Hook script created: ${hookPath}`));
36247
- const settingsPath = join13(process.cwd(), ".claude", "settings.json");
36277
+ const settingsPath = join12(process.cwd(), ".claude", "settings.json");
36248
36278
  const settings = readJsonFile2(settingsPath);
36249
36279
  if (!settings["hooks"]) {
36250
36280
  settings["hooks"] = {};
@@ -36338,7 +36368,7 @@ exit 0
36338
36368
  const hookPath = `${gitDir}/hooks/post-commit`;
36339
36369
  const marker = "# todos-auto-link";
36340
36370
  if (existsSync10(hookPath)) {
36341
- const existing = readFileSync8(hookPath, "utf-8");
36371
+ const existing = readFileSync7(hookPath, "utf-8");
36342
36372
  if (existing.includes(marker)) {
36343
36373
  console.log(chalk9.yellow("Hook already installed."));
36344
36374
  return;
@@ -36369,7 +36399,7 @@ $(dirname "$0")/../../scripts/post-commit-hook.sh
36369
36399
  console.log(chalk9.dim("No post-commit hook found."));
36370
36400
  return;
36371
36401
  }
36372
- const content = readFileSync8(hookPath, "utf-8");
36402
+ const content = readFileSync7(hookPath, "utf-8");
36373
36403
  if (!content.includes(marker)) {
36374
36404
  console.log(chalk9.dim("Hook not managed by todos."));
36375
36405
  return;
@@ -36533,7 +36563,7 @@ import chalk11 from "chalk";
36533
36563
  import { execSync as execSync4 } from "child_process";
36534
36564
  import { writeFileSync as writeFileSync6 } from "fs";
36535
36565
  import { tmpdir } from "os";
36536
- import { join as join14 } from "path";
36566
+ import { join as join13 } from "path";
36537
36567
  function getOrCreateLocalMachineName() {
36538
36568
  return process.env["TODOS_MACHINE_NAME"] || __require("os").hostname() || "unknown";
36539
36569
  }
@@ -36727,7 +36757,7 @@ Warning: No primary machine set.`));
36727
36757
  if (opts.push) {
36728
36758
  try {
36729
36759
  const localTasks = listTasks3();
36730
- const tmpFile = join14(tmpdir(), `todos-export-${uuid()}.json`);
36760
+ const tmpFile = join13(tmpdir(), `todos-export-${uuid()}.json`);
36731
36761
  writeFileSync6(tmpFile, JSON.stringify(localTasks, null, 2));
36732
36762
  execSync4(`scp ${tmpFile} ${ssh}:/tmp/todos-import.json`, { timeout: 15000 });
36733
36763
  const importCmd = `ssh ${ssh} 'node -e "const fs=require(\\'fs\\');const tasks=JSON.parse(fs.readFileSync(\\'/tmp/todos-import.json\\',\\'utf-8\\'));console.log(JSON.stringify(tasks.length))"'`;
@@ -1 +1 @@
1
- {"version":3,"file":"agent-names.d.ts","sourceRoot":"","sources":["../../src/db/agent-names.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;gBAEnB,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAE,MAAM,EAAO;CAKrE;AAED,eAAO,MAAM,iBAAiB,yRA0BpB,CAAC;AAEX,eAAO,MAAM,iBAAiB,8NAqBpB,CAAC;AAEX,eAAO,MAAM,gBAAgB,wGAWnB,CAAC;AAEX,eAAO,MAAM,qBAAqB,ykBAIxB,CAAC;AAoBX,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CASxD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGxD;AAED,wBAAgB,iBAAiB,CAAC,aAAa,GAAE,QAAQ,CAAC,MAAM,CAAM,GAAG,MAAM,EAAE,CAGhF;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,GAAE,QAAQ,CAAC,MAAM,CAAM,GAAG,MAAM,CAwB5F;AAyCD,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,QAAQ,GAAG,sBAAsB,EAAE,CA6BnF"}
1
+ {"version":3,"file":"agent-names.d.ts","sourceRoot":"","sources":["../../src/db/agent-names.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,WAAW,EAAE,MAAM,EAAE,CAAC;gBAEnB,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,WAAW,GAAE,MAAM,EAAO;CAKrE;AAED,eAAO,MAAM,iBAAiB,yRA0BpB,CAAC;AAEX,eAAO,MAAM,iBAAiB,8NAqBpB,CAAC;AAEX,eAAO,MAAM,gBAAgB,wGAWnB,CAAC;AAEX,eAAO,MAAM,qBAAqB,ykBAIxB,CAAC;AAoBX,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE/D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CASxD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGxD;AAaD,wBAAgB,iBAAiB,CAAC,aAAa,GAAE,QAAQ,CAAC,MAAM,CAAM,GAAG,MAAM,EAAE,CAahF;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,GAAE,QAAQ,CAAC,MAAM,CAAM,GAAG,MAAM,CAwB5F;AAyCD,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,QAAQ,GAAG,sBAAsB,EAAE,CA6BnF"}
package/dist/index.js CHANGED
@@ -5184,9 +5184,31 @@ function isBlockedAgentName(name) {
5184
5184
  const normalized = normalizeAgentNameInput(name);
5185
5185
  return isGenericAgentName(normalized) || hasGeneratedNumericSuffix(normalized) || !ONE_WORD_NAME_RE.test(normalized);
5186
5186
  }
5187
+ function alphabeticSuffix(index) {
5188
+ const letters = "abcdefghijklmnopqrstuvwxyz";
5189
+ let value = index;
5190
+ let suffix = "";
5191
+ do {
5192
+ suffix = letters[value % letters.length] + suffix;
5193
+ value = Math.floor(value / letters.length) - 1;
5194
+ } while (value >= 0);
5195
+ return suffix;
5196
+ }
5187
5197
  function suggestAgentNames(existingNames = []) {
5188
5198
  const existing = new Set([...existingNames].map(normalizeAgentNameInput));
5189
- return PREFERRED_AGENT_NAMES.filter((name) => !existing.has(name));
5199
+ const suggestions = PREFERRED_AGENT_NAMES.filter((name) => !existing.has(name));
5200
+ for (let suffixIndex = 0;suggestions.length < 20 && suffixIndex < 1000; suffixIndex++) {
5201
+ const suffix = alphabeticSuffix(suffixIndex);
5202
+ for (const base of PREFERRED_AGENT_NAMES) {
5203
+ const candidate = `${base}${suffix}`;
5204
+ if (existing.has(candidate) || suggestions.includes(candidate))
5205
+ continue;
5206
+ suggestions.push(candidate);
5207
+ if (suggestions.length >= 20)
5208
+ break;
5209
+ }
5210
+ }
5211
+ return suggestions;
5190
5212
  }
5191
5213
  function validateAgentName(name, existingNames = []) {
5192
5214
  const normalized = normalizeAgentNameInput(name);
@@ -0,0 +1,2 @@
1
+ export declare function getPackageVersion(fromUrl?: string): string;
2
+ //# sourceMappingURL=package-version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"package-version.d.ts","sourceRoot":"","sources":["../../src/lib/package-version.ts"],"names":[],"mappings":"AAIA,wBAAgB,iBAAiB,CAAC,OAAO,SAAkB,GAAG,MAAM,CAgBnE"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";AAuGA,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAO9E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";AAoGA,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAO9E"}
package/dist/mcp/index.js CHANGED
@@ -12710,9 +12710,31 @@ function isBlockedAgentName(name) {
12710
12710
  const normalized = normalizeAgentNameInput(name);
12711
12711
  return isGenericAgentName(normalized) || hasGeneratedNumericSuffix(normalized) || !ONE_WORD_NAME_RE.test(normalized);
12712
12712
  }
12713
+ function alphabeticSuffix(index) {
12714
+ const letters = "abcdefghijklmnopqrstuvwxyz";
12715
+ let value = index;
12716
+ let suffix = "";
12717
+ do {
12718
+ suffix = letters[value % letters.length] + suffix;
12719
+ value = Math.floor(value / letters.length) - 1;
12720
+ } while (value >= 0);
12721
+ return suffix;
12722
+ }
12713
12723
  function suggestAgentNames(existingNames = []) {
12714
12724
  const existing = new Set([...existingNames].map(normalizeAgentNameInput));
12715
- return PREFERRED_AGENT_NAMES.filter((name) => !existing.has(name));
12725
+ const suggestions = PREFERRED_AGENT_NAMES.filter((name) => !existing.has(name));
12726
+ for (let suffixIndex = 0;suggestions.length < 20 && suffixIndex < 1000; suffixIndex++) {
12727
+ const suffix = alphabeticSuffix(suffixIndex);
12728
+ for (const base of PREFERRED_AGENT_NAMES) {
12729
+ const candidate = `${base}${suffix}`;
12730
+ if (existing.has(candidate) || suggestions.includes(candidate))
12731
+ continue;
12732
+ suggestions.push(candidate);
12733
+ if (suggestions.length >= 20)
12734
+ break;
12735
+ }
12736
+ }
12737
+ return suggestions;
12716
12738
  }
12717
12739
  function validateAgentName(name, existingNames = []) {
12718
12740
  const normalized = normalizeAgentNameInput(name);
@@ -22361,9 +22383,6 @@ async function logError(message, opts) {
22361
22383
 
22362
22384
  // src/mcp/index.ts
22363
22385
  init_types2();
22364
- import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
22365
- import { join as join10, dirname as dirname3 } from "path";
22366
- import { fileURLToPath } from "url";
22367
22386
 
22368
22387
  // src/mcp/tools/dispatch.ts
22369
22388
  init_dispatches();
@@ -26040,22 +26059,40 @@ ID: ${updated.id}${taskNote}`
26040
26059
  }
26041
26060
  }
26042
26061
 
26043
- // src/mcp/index.ts
26044
- function getMcpVersion() {
26062
+ // src/lib/package-version.ts
26063
+ import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
26064
+ import { dirname as dirname3, join as join10 } from "path";
26065
+ import { fileURLToPath } from "url";
26066
+ function getPackageVersion(fromUrl = import.meta.url) {
26045
26067
  try {
26046
- let dir = dirname3(fileURLToPath(import.meta.url));
26047
- for (let i = 0;i < 4; i++) {
26068
+ let dir = dirname3(fileURLToPath(fromUrl));
26069
+ for (let i = 0;i < 5; i++) {
26048
26070
  const pkgPath = join10(dir, "package.json");
26049
26071
  if (existsSync10(pkgPath)) {
26050
26072
  return JSON.parse(readFileSync6(pkgPath, "utf-8")).version || "0.0.0";
26051
26073
  }
26052
- dir = dirname3(dir);
26074
+ const parent = dirname3(dir);
26075
+ if (parent === dir)
26076
+ break;
26077
+ dir = parent;
26053
26078
  }
26054
26079
  } catch {
26055
26080
  return "0.0.0";
26056
26081
  }
26057
26082
  return "0.0.0";
26058
26083
  }
26084
+
26085
+ // src/mcp/index.ts
26086
+ function getMcpVersion() {
26087
+ return getPackageVersion(import.meta.url);
26088
+ }
26089
+ function hasVersionFlag() {
26090
+ return process.argv.includes("--version") || process.argv.includes("-V");
26091
+ }
26092
+ if (hasVersionFlag()) {
26093
+ console.log(getMcpVersion());
26094
+ process.exit(0);
26095
+ }
26059
26096
  var server = new McpServer({
26060
26097
  name: "todos",
26061
26098
  version: getMcpVersion()
@@ -1369,18 +1369,18 @@ __export(exports_database, {
1369
1369
  LOCK_EXPIRY_MINUTES: () => LOCK_EXPIRY_MINUTES
1370
1370
  });
1371
1371
  import { Database } from "bun:sqlite";
1372
- import { existsSync, mkdirSync } from "fs";
1373
- import { dirname, join, resolve } from "path";
1372
+ import { existsSync as existsSync2, mkdirSync } from "fs";
1373
+ import { dirname as dirname2, join as join2, resolve } from "path";
1374
1374
  function isInMemoryDb(path) {
1375
1375
  return path === ":memory:" || path.startsWith("file::memory:");
1376
1376
  }
1377
1377
  function findNearestTodosDb(startDir) {
1378
1378
  let dir = resolve(startDir);
1379
1379
  while (true) {
1380
- const candidate = join(dir, ".todos", "todos.db");
1381
- if (existsSync(candidate))
1380
+ const candidate = join2(dir, ".todos", "todos.db");
1381
+ if (existsSync2(candidate))
1382
1382
  return candidate;
1383
- const parent = dirname(dir);
1383
+ const parent = dirname2(dir);
1384
1384
  if (parent === dir)
1385
1385
  break;
1386
1386
  dir = parent;
@@ -1390,9 +1390,9 @@ function findNearestTodosDb(startDir) {
1390
1390
  function findGitRoot(startDir) {
1391
1391
  let dir = resolve(startDir);
1392
1392
  while (true) {
1393
- if (existsSync(join(dir, ".git")))
1393
+ if (existsSync2(join2(dir, ".git")))
1394
1394
  return dir;
1395
- const parent = dirname(dir);
1395
+ const parent = dirname2(dir);
1396
1396
  if (parent === dir)
1397
1397
  break;
1398
1398
  dir = parent;
@@ -1413,13 +1413,13 @@ function getDbPath() {
1413
1413
  if (process.env["TODOS_DB_SCOPE"] === "project") {
1414
1414
  const gitRoot = findGitRoot(cwd);
1415
1415
  if (gitRoot) {
1416
- return join(gitRoot, ".todos", "todos.db");
1416
+ return join2(gitRoot, ".todos", "todos.db");
1417
1417
  }
1418
1418
  }
1419
1419
  const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
1420
- const newPath = join(home, ".hasna", "todos", "todos.db");
1421
- const legacyPath = join(home, ".todos", "todos.db");
1422
- if (!existsSync(newPath) && existsSync(legacyPath)) {
1420
+ const newPath = join2(home, ".hasna", "todos", "todos.db");
1421
+ const legacyPath = join2(home, ".todos", "todos.db");
1422
+ if (!existsSync2(newPath) && existsSync2(legacyPath)) {
1423
1423
  return legacyPath;
1424
1424
  }
1425
1425
  return newPath;
@@ -1427,8 +1427,8 @@ function getDbPath() {
1427
1427
  function ensureDir(filePath) {
1428
1428
  if (isInMemoryDb(filePath))
1429
1429
  return;
1430
- const dir = dirname(resolve(filePath));
1431
- if (!existsSync(dir)) {
1430
+ const dir = dirname2(resolve(filePath));
1431
+ if (!existsSync2(dir)) {
1432
1432
  mkdirSync(dir, { recursive: true });
1433
1433
  }
1434
1434
  }
@@ -1516,11 +1516,34 @@ var init_database = __esm(() => {
1516
1516
  ALLOWED_TABLES = new Set(["tasks", "projects", "agents", "plans", "task_lists", "task_templates"]);
1517
1517
  });
1518
1518
 
1519
+ // src/lib/package-version.ts
1520
+ import { existsSync, readFileSync } from "fs";
1521
+ import { dirname, join } from "path";
1522
+ import { fileURLToPath } from "url";
1523
+ function getPackageVersion(fromUrl = import.meta.url) {
1524
+ try {
1525
+ let dir = dirname(fileURLToPath(fromUrl));
1526
+ for (let i = 0;i < 5; i++) {
1527
+ const pkgPath = join(dir, "package.json");
1528
+ if (existsSync(pkgPath)) {
1529
+ return JSON.parse(readFileSync(pkgPath, "utf-8")).version || "0.0.0";
1530
+ }
1531
+ const parent = dirname(dir);
1532
+ if (parent === dir)
1533
+ break;
1534
+ dir = parent;
1535
+ }
1536
+ } catch {
1537
+ return "0.0.0";
1538
+ }
1539
+ return "0.0.0";
1540
+ }
1541
+
1519
1542
  // src/server/serve.ts
1520
1543
  init_database();
1521
- import { existsSync as existsSync4 } from "fs";
1522
- import { join as join4, dirname as dirname2, extname } from "path";
1523
- import { fileURLToPath } from "url";
1544
+ import { existsSync as existsSync5 } from "fs";
1545
+ import { join as join5, dirname as dirname3, extname } from "path";
1546
+ import { fileURLToPath as fileURLToPath2 } from "url";
1524
1547
 
1525
1548
  // src/db/api-keys.ts
1526
1549
  init_database();
@@ -1629,15 +1652,15 @@ class CompletionGuardError extends Error {
1629
1652
  init_database();
1630
1653
 
1631
1654
  // src/lib/config.ts
1632
- import { existsSync as existsSync3 } from "fs";
1633
- import { join as join2 } from "path";
1655
+ import { existsSync as existsSync4 } from "fs";
1656
+ import { join as join3 } from "path";
1634
1657
 
1635
1658
  // src/lib/sync-utils.ts
1636
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
1659
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, readdirSync, statSync, writeFileSync } from "fs";
1637
1660
  var HOME = process.env["HOME"] || process.env["USERPROFILE"] || "~";
1638
1661
  function readJsonFile(path) {
1639
1662
  try {
1640
- return JSON.parse(readFileSync(path, "utf-8"));
1663
+ return JSON.parse(readFileSync2(path, "utf-8"));
1641
1664
  } catch {
1642
1665
  return null;
1643
1666
  }
@@ -1646,22 +1669,22 @@ function readJsonFile(path) {
1646
1669
  // src/lib/config.ts
1647
1670
  function getTodosGlobalDir() {
1648
1671
  const home = process.env["HOME"] || HOME;
1649
- const newDir = join2(home, ".hasna", "todos");
1650
- const legacyDir = join2(home, ".todos");
1651
- const newConfig = join2(newDir, "config.json");
1652
- const legacyConfig = join2(legacyDir, "config.json");
1653
- if (!existsSync3(newConfig) && existsSync3(legacyConfig))
1672
+ const newDir = join3(home, ".hasna", "todos");
1673
+ const legacyDir = join3(home, ".todos");
1674
+ const newConfig = join3(newDir, "config.json");
1675
+ const legacyConfig = join3(legacyDir, "config.json");
1676
+ if (!existsSync4(newConfig) && existsSync4(legacyConfig))
1654
1677
  return legacyDir;
1655
1678
  return newDir;
1656
1679
  }
1657
1680
  function getConfigPath() {
1658
- return join2(getTodosGlobalDir(), "config.json");
1681
+ return join3(getTodosGlobalDir(), "config.json");
1659
1682
  }
1660
1683
  var cached = null;
1661
1684
  function loadConfig() {
1662
1685
  if (cached)
1663
1686
  return cached;
1664
- if (!existsSync3(getConfigPath())) {
1687
+ if (!existsSync4(getConfigPath())) {
1665
1688
  cached = {};
1666
1689
  return cached;
1667
1690
  }
@@ -3171,9 +3194,31 @@ function isGenericAgentName(name) {
3171
3194
  }
3172
3195
  return false;
3173
3196
  }
3197
+ function alphabeticSuffix(index) {
3198
+ const letters = "abcdefghijklmnopqrstuvwxyz";
3199
+ let value = index;
3200
+ let suffix = "";
3201
+ do {
3202
+ suffix = letters[value % letters.length] + suffix;
3203
+ value = Math.floor(value / letters.length) - 1;
3204
+ } while (value >= 0);
3205
+ return suffix;
3206
+ }
3174
3207
  function suggestAgentNames(existingNames = []) {
3175
3208
  const existing = new Set([...existingNames].map(normalizeAgentNameInput));
3176
- return PREFERRED_AGENT_NAMES.filter((name) => !existing.has(name));
3209
+ const suggestions = PREFERRED_AGENT_NAMES.filter((name) => !existing.has(name));
3210
+ for (let suffixIndex = 0;suggestions.length < 20 && suffixIndex < 1000; suffixIndex++) {
3211
+ const suffix = alphabeticSuffix(suffixIndex);
3212
+ for (const base of PREFERRED_AGENT_NAMES) {
3213
+ const candidate = `${base}${suffix}`;
3214
+ if (existing.has(candidate) || suggestions.includes(candidate))
3215
+ continue;
3216
+ suggestions.push(candidate);
3217
+ if (suggestions.length >= 20)
3218
+ break;
3219
+ }
3220
+ }
3221
+ return suggestions;
3177
3222
  }
3178
3223
  function validateAgentName(name, existingNames = []) {
3179
3224
  const normalized = normalizeAgentNameInput(name);
@@ -3590,7 +3635,7 @@ function listComments(taskId, db) {
3590
3635
  }
3591
3636
 
3592
3637
  // src/server/routes.ts
3593
- import { join as join3, resolve as resolve2, sep } from "path";
3638
+ import { join as join4, resolve as resolve2, sep } from "path";
3594
3639
  function handleSseEvents(_req, url, ctx) {
3595
3640
  const agentId = url.searchParams.get("agent_id") || undefined;
3596
3641
  const projectId = url.searchParams.get("project_id") || undefined;
@@ -4266,7 +4311,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
4266
4311
  if (!ctx.dashboardExists || method !== "GET" && method !== "HEAD")
4267
4312
  return null;
4268
4313
  if (path !== "/") {
4269
- const filePath = join3(ctx.dashboardDir, path);
4314
+ const filePath = join4(ctx.dashboardDir, path);
4270
4315
  const resolvedFile = resolve2(filePath);
4271
4316
  const resolvedBase = resolve2(ctx.dashboardDir);
4272
4317
  if (!resolvedFile.startsWith(resolvedBase + sep) && resolvedFile !== resolvedBase) {
@@ -4276,7 +4321,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
4276
4321
  if (res2)
4277
4322
  return res2;
4278
4323
  }
4279
- const indexPath = join3(ctx.dashboardDir, "index.html");
4324
+ const indexPath = join4(ctx.dashboardDir, "index.html");
4280
4325
  const res = serveStaticFile2(indexPath);
4281
4326
  if (res)
4282
4327
  return res;
@@ -4287,21 +4332,21 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
4287
4332
  function resolveDashboardDir() {
4288
4333
  const candidates = [];
4289
4334
  try {
4290
- const scriptDir = dirname2(fileURLToPath(import.meta.url));
4291
- candidates.push(join4(scriptDir, "..", "dashboard", "dist"));
4292
- candidates.push(join4(scriptDir, "..", "..", "dashboard", "dist"));
4335
+ const scriptDir = dirname3(fileURLToPath2(import.meta.url));
4336
+ candidates.push(join5(scriptDir, "..", "dashboard", "dist"));
4337
+ candidates.push(join5(scriptDir, "..", "..", "dashboard", "dist"));
4293
4338
  } catch {}
4294
4339
  if (process.argv[1]) {
4295
- const mainDir = dirname2(process.argv[1]);
4296
- candidates.push(join4(mainDir, "..", "dashboard", "dist"));
4297
- candidates.push(join4(mainDir, "..", "..", "dashboard", "dist"));
4340
+ const mainDir = dirname3(process.argv[1]);
4341
+ candidates.push(join5(mainDir, "..", "dashboard", "dist"));
4342
+ candidates.push(join5(mainDir, "..", "..", "dashboard", "dist"));
4298
4343
  }
4299
- candidates.push(join4(process.cwd(), "dashboard", "dist"));
4344
+ candidates.push(join5(process.cwd(), "dashboard", "dist"));
4300
4345
  for (const candidate of candidates) {
4301
- if (existsSync4(candidate))
4346
+ if (existsSync5(candidate))
4302
4347
  return candidate;
4303
4348
  }
4304
- return join4(process.cwd(), "dashboard", "dist");
4349
+ return join5(process.cwd(), "dashboard", "dist");
4305
4350
  }
4306
4351
  var MIME_TYPES = {
4307
4352
  ".html": "text/html; charset=utf-8",
@@ -4373,7 +4418,7 @@ function json(data, status = 200, headers) {
4373
4418
  });
4374
4419
  }
4375
4420
  function serveStaticFile(filePath) {
4376
- if (!existsSync4(filePath))
4421
+ if (!existsSync5(filePath))
4377
4422
  return null;
4378
4423
  const ext = extname(filePath);
4379
4424
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
@@ -4452,7 +4497,7 @@ data: ${data}
4452
4497
  filteredSseClients.delete(client);
4453
4498
  }
4454
4499
  const dashboardDir = resolveDashboardDir();
4455
- const dashboardExists = existsSync4(dashboardDir);
4500
+ const dashboardExists = existsSync5(dashboardDir);
4456
4501
  if (!dashboardExists) {
4457
4502
  console.error(`
4458
4503
  Dashboard not found at: ${dashboardDir}`);
@@ -4726,6 +4771,9 @@ Dashboard not found at: ${dashboardDir}`);
4726
4771
 
4727
4772
  // src/server/index.ts
4728
4773
  var DEFAULT_PORT = 19427;
4774
+ function hasVersionFlag() {
4775
+ return process.argv.includes("--version") || process.argv.includes("-V");
4776
+ }
4729
4777
  function parsePort() {
4730
4778
  const portArg = process.argv.find((a) => a === "--port" || a.startsWith("--port="));
4731
4779
  if (portArg) {
@@ -4757,6 +4805,10 @@ async function findFreePort(start) {
4757
4805
  return start;
4758
4806
  }
4759
4807
  async function main() {
4808
+ if (hasVersionFlag()) {
4809
+ console.log(getPackageVersion());
4810
+ return;
4811
+ }
4760
4812
  const requestedPort = parsePort();
4761
4813
  const port = await findFreePort(requestedPort);
4762
4814
  if (port !== requestedPort) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/todos",
3
- "version": "0.11.35",
3
+ "version": "0.11.37",
4
4
  "description": "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",