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