@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.
- package/dist/cli/helpers.d.ts +2 -1
- package/dist/cli/helpers.d.ts.map +1 -1
- package/dist/cli/index.js +102 -72
- package/dist/db/agent-names.d.ts.map +1 -1
- package/dist/index.js +23 -1
- package/dist/lib/package-version.d.ts +2 -0
- package/dist/lib/package-version.d.ts.map +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +46 -9
- package/dist/server/index.js +94 -42
- package/package.json +1 -1
package/dist/cli/helpers.d.ts
CHANGED
|
@@ -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
|
|
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":"
|
|
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 {
|
|
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
|
|
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 (!
|
|
4226
|
+
if (!existsSync3(dir))
|
|
4212
4227
|
mkdirSync2(dir, { recursive: true });
|
|
4213
4228
|
}
|
|
4214
4229
|
function listJsonFiles(dir) {
|
|
4215
|
-
if (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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 (!
|
|
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 =
|
|
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
|
|
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 (!
|
|
12014
|
+
if (!existsSync9(legacyDir))
|
|
11978
12015
|
return [];
|
|
11979
|
-
if (
|
|
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
|
|
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
|
-
|
|
31680
|
-
|
|
31681
|
-
|
|
31682
|
-
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 (!
|
|
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 (!
|
|
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 (!
|
|
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
|
|
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 = !
|
|
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 (!
|
|
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
|
|
36038
|
-
import { dirname as
|
|
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 =
|
|
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(
|
|
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 =
|
|
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
|
|
36100
|
+
return readFileSync7(path, "utf-8");
|
|
36071
36101
|
}
|
|
36072
36102
|
function writeTomlFile(path, content) {
|
|
36073
|
-
const dir =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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;
|
|
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
|
-
|
|
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 @@
|
|
|
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"}
|
package/dist/mcp/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/mcp/index.ts"],"names":[],"mappings":";
|
|
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
|
-
|
|
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/
|
|
26044
|
-
|
|
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(
|
|
26047
|
-
for (let i = 0;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
|
-
|
|
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()
|
package/dist/server/index.js
CHANGED
|
@@ -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 =
|
|
1381
|
-
if (
|
|
1380
|
+
const candidate = join2(dir, ".todos", "todos.db");
|
|
1381
|
+
if (existsSync2(candidate))
|
|
1382
1382
|
return candidate;
|
|
1383
|
-
const parent =
|
|
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 (
|
|
1393
|
+
if (existsSync2(join2(dir, ".git")))
|
|
1394
1394
|
return dir;
|
|
1395
|
-
const parent =
|
|
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
|
|
1416
|
+
return join2(gitRoot, ".todos", "todos.db");
|
|
1417
1417
|
}
|
|
1418
1418
|
}
|
|
1419
1419
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
1420
|
-
const newPath =
|
|
1421
|
-
const legacyPath =
|
|
1422
|
-
if (!
|
|
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 =
|
|
1431
|
-
if (!
|
|
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
|
|
1522
|
-
import { join as
|
|
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
|
|
1633
|
-
import { join as
|
|
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
|
|
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(
|
|
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 =
|
|
1650
|
-
const legacyDir =
|
|
1651
|
-
const newConfig =
|
|
1652
|
-
const legacyConfig =
|
|
1653
|
-
if (!
|
|
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
|
|
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 (!
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
4291
|
-
candidates.push(
|
|
4292
|
-
candidates.push(
|
|
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 =
|
|
4296
|
-
candidates.push(
|
|
4297
|
-
candidates.push(
|
|
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(
|
|
4344
|
+
candidates.push(join5(process.cwd(), "dashboard", "dist"));
|
|
4300
4345
|
for (const candidate of candidates) {
|
|
4301
|
-
if (
|
|
4346
|
+
if (existsSync5(candidate))
|
|
4302
4347
|
return candidate;
|
|
4303
4348
|
}
|
|
4304
|
-
return
|
|
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 (!
|
|
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 =
|
|
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) {
|