@hasna/todos 0.11.89 → 0.11.92
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/README.md +7 -2
- package/dist/cli/cloud-router.d.ts +57 -25
- package/dist/cli/cloud-router.d.ts.map +1 -1
- package/dist/cli/commands/agent-commands.d.ts.map +1 -1
- package/dist/cli/commands/plan-template-commands.d.ts.map +1 -1
- 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/commands/storage-commands.d.ts.map +1 -1
- package/dist/cli/commands/task-commands.d.ts.map +1 -1
- package/dist/cli/helpers.d.ts +15 -1
- package/dist/cli/helpers.d.ts.map +1 -1
- package/dist/cli/index.js +19594 -30654
- package/dist/cli/output-redaction.d.ts +5 -0
- package/dist/cli/output-redaction.d.ts.map +1 -0
- package/dist/cli/stage-a.d.ts +45 -0
- package/dist/cli/stage-a.d.ts.map +1 -0
- package/dist/contracts.js +196 -97
- package/dist/index.js +517 -313
- package/dist/lib/package-version.d.ts +1 -1
- package/dist/lib/package-version.d.ts.map +1 -1
- package/dist/lib/redaction.d.ts.map +1 -1
- package/dist/lib/tmux.d.ts.map +1 -1
- package/dist/mcp/index.js +837 -6847
- package/dist/mcp.js +118 -20
- package/dist/registry.js +196 -97
- package/dist/release-provenance.json +5 -3
- package/dist/sdk/index.js +37 -2
- package/dist/sdk/v1.generated.d.ts +72 -3
- package/dist/sdk/v1.generated.d.ts.map +1 -1
- package/dist/server/index.js +2213 -8136
- package/dist/server/openapi.d.ts +366 -2
- package/dist/server/openapi.d.ts.map +1 -1
- package/dist/server/v1.d.ts.map +1 -1
- package/dist/storage.js +112 -14
- package/dist/types/index.d.ts +2 -2
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +9 -6
package/dist/index.js
CHANGED
|
@@ -2427,7 +2427,7 @@ var init_schema = __esm(() => {
|
|
|
2427
2427
|
});
|
|
2428
2428
|
|
|
2429
2429
|
// src/db/machines.ts
|
|
2430
|
-
import { existsSync
|
|
2430
|
+
import { existsSync } from "fs";
|
|
2431
2431
|
import { hostname as osHostname, platform as osPlatform, arch as osArch } from "os";
|
|
2432
2432
|
import { resolve } from "path";
|
|
2433
2433
|
import { spawnSync } from "child_process";
|
|
@@ -2637,7 +2637,7 @@ function getMachineTopologyDiagnostics(opts = {}, db, at = new Date) {
|
|
|
2637
2637
|
message: `${project.name} has ${distinctPaths.length} different machine-local paths`
|
|
2638
2638
|
});
|
|
2639
2639
|
}
|
|
2640
|
-
if (localRow && !
|
|
2640
|
+
if (localRow && !existsSync(localRow.path)) {
|
|
2641
2641
|
pathIssues.push({
|
|
2642
2642
|
type: "path_missing",
|
|
2643
2643
|
project_id: project.id,
|
|
@@ -2648,7 +2648,7 @@ function getMachineTopologyDiagnostics(opts = {}, db, at = new Date) {
|
|
|
2648
2648
|
message: `Local path does not exist on this machine: ${localRow.path}`
|
|
2649
2649
|
});
|
|
2650
2650
|
}
|
|
2651
|
-
if (!localRow && project.path && machineById.has(localMachine.id) && !
|
|
2651
|
+
if (!localRow && project.path && machineById.has(localMachine.id) && !existsSync(project.path)) {
|
|
2652
2652
|
pathIssues.push({
|
|
2653
2653
|
type: "path_missing",
|
|
2654
2654
|
project_id: project.id,
|
|
@@ -3036,8 +3036,8 @@ __export(exports_database, {
|
|
|
3036
3036
|
LOCK_EXPIRY_MINUTES: () => LOCK_EXPIRY_MINUTES
|
|
3037
3037
|
});
|
|
3038
3038
|
import { Database } from "bun:sqlite";
|
|
3039
|
-
import { existsSync as
|
|
3040
|
-
import { dirname
|
|
3039
|
+
import { existsSync as existsSync2, mkdirSync } from "fs";
|
|
3040
|
+
import { dirname, join, resolve as resolve2 } from "path";
|
|
3041
3041
|
function isInMemoryDb(path) {
|
|
3042
3042
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
3043
3043
|
}
|
|
@@ -3046,12 +3046,12 @@ function findNearestProjectDb(startDir) {
|
|
|
3046
3046
|
const stopAt = gitRoot ? resolve2(gitRoot) : resolve2(startDir);
|
|
3047
3047
|
let dir = resolve2(startDir);
|
|
3048
3048
|
while (true) {
|
|
3049
|
-
const candidate =
|
|
3050
|
-
if (
|
|
3049
|
+
const candidate = join(dir, ".hasna", "todos", "todos.db");
|
|
3050
|
+
if (existsSync2(candidate))
|
|
3051
3051
|
return candidate;
|
|
3052
3052
|
if (dir === stopAt)
|
|
3053
3053
|
break;
|
|
3054
|
-
const parent =
|
|
3054
|
+
const parent = dirname(dir);
|
|
3055
3055
|
if (parent === dir)
|
|
3056
3056
|
break;
|
|
3057
3057
|
dir = parent;
|
|
@@ -3061,9 +3061,9 @@ function findNearestProjectDb(startDir) {
|
|
|
3061
3061
|
function findGitRoot(startDir) {
|
|
3062
3062
|
let dir = resolve2(startDir);
|
|
3063
3063
|
while (true) {
|
|
3064
|
-
if (
|
|
3064
|
+
if (existsSync2(join(dir, ".git")))
|
|
3065
3065
|
return dir;
|
|
3066
|
-
const parent =
|
|
3066
|
+
const parent = dirname(dir);
|
|
3067
3067
|
if (parent === dir)
|
|
3068
3068
|
break;
|
|
3069
3069
|
dir = parent;
|
|
@@ -3084,11 +3084,11 @@ function getDbPath() {
|
|
|
3084
3084
|
if (process.env["TODOS_DB_SCOPE"] === "project") {
|
|
3085
3085
|
const gitRoot = findGitRoot(cwd);
|
|
3086
3086
|
if (gitRoot) {
|
|
3087
|
-
return
|
|
3087
|
+
return join(gitRoot, ".hasna", "todos", "todos.db");
|
|
3088
3088
|
}
|
|
3089
3089
|
}
|
|
3090
3090
|
const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
3091
|
-
return
|
|
3091
|
+
return join(home, ".hasna", "todos", "todos.db");
|
|
3092
3092
|
}
|
|
3093
3093
|
function getDatabasePath() {
|
|
3094
3094
|
return getDbPath();
|
|
@@ -3096,8 +3096,8 @@ function getDatabasePath() {
|
|
|
3096
3096
|
function ensureDir(filePath) {
|
|
3097
3097
|
if (isInMemoryDb(filePath))
|
|
3098
3098
|
return;
|
|
3099
|
-
const dir =
|
|
3100
|
-
if (!
|
|
3099
|
+
const dir = dirname(resolve2(filePath));
|
|
3100
|
+
if (!existsSync2(dir)) {
|
|
3101
3101
|
mkdirSync(dir, { recursive: true });
|
|
3102
3102
|
}
|
|
3103
3103
|
}
|
|
@@ -3368,26 +3368,26 @@ var init_types = __esm(() => {
|
|
|
3368
3368
|
});
|
|
3369
3369
|
|
|
3370
3370
|
// src/lib/sync-utils.ts
|
|
3371
|
-
import { existsSync as
|
|
3372
|
-
import { join as
|
|
3371
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
3372
|
+
import { join as join2 } from "path";
|
|
3373
3373
|
function getHomeDir() {
|
|
3374
3374
|
return process.env["HOME"] || process.env["USERPROFILE"] || "~";
|
|
3375
3375
|
}
|
|
3376
3376
|
function getTodosGlobalDir() {
|
|
3377
|
-
return
|
|
3377
|
+
return join2(getHomeDir(), ".hasna", "todos");
|
|
3378
3378
|
}
|
|
3379
3379
|
function ensureDir2(dir) {
|
|
3380
|
-
if (!
|
|
3380
|
+
if (!existsSync3(dir))
|
|
3381
3381
|
mkdirSync2(dir, { recursive: true });
|
|
3382
3382
|
}
|
|
3383
3383
|
function listJsonFiles(dir) {
|
|
3384
|
-
if (!
|
|
3384
|
+
if (!existsSync3(dir))
|
|
3385
3385
|
return [];
|
|
3386
3386
|
return readdirSync(dir).filter((f) => f.endsWith(".json"));
|
|
3387
3387
|
}
|
|
3388
3388
|
function readJsonFile(path) {
|
|
3389
3389
|
try {
|
|
3390
|
-
return JSON.parse(
|
|
3390
|
+
return JSON.parse(readFileSync(path, "utf-8"));
|
|
3391
3391
|
} catch {
|
|
3392
3392
|
return null;
|
|
3393
3393
|
}
|
|
@@ -3397,14 +3397,14 @@ function writeJsonFile(path, data) {
|
|
|
3397
3397
|
`);
|
|
3398
3398
|
}
|
|
3399
3399
|
function readHighWaterMark(dir) {
|
|
3400
|
-
const path =
|
|
3401
|
-
if (!
|
|
3400
|
+
const path = join2(dir, ".highwatermark");
|
|
3401
|
+
if (!existsSync3(path))
|
|
3402
3402
|
return 1;
|
|
3403
|
-
const val = parseInt(
|
|
3403
|
+
const val = parseInt(readFileSync(path, "utf-8").trim(), 10);
|
|
3404
3404
|
return isNaN(val) ? 1 : val;
|
|
3405
3405
|
}
|
|
3406
3406
|
function writeHighWaterMark(dir, value) {
|
|
3407
|
-
writeFileSync(
|
|
3407
|
+
writeFileSync(join2(dir, ".highwatermark"), String(value));
|
|
3408
3408
|
}
|
|
3409
3409
|
function getFileMtimeMs(path) {
|
|
3410
3410
|
try {
|
|
@@ -3430,10 +3430,10 @@ var init_sync_utils = __esm(() => {
|
|
|
3430
3430
|
});
|
|
3431
3431
|
|
|
3432
3432
|
// src/lib/config.ts
|
|
3433
|
-
import { existsSync as
|
|
3434
|
-
import { dirname as
|
|
3433
|
+
import { existsSync as existsSync4 } from "fs";
|
|
3434
|
+
import { dirname as dirname2, join as join3 } from "path";
|
|
3435
3435
|
function getConfigPath() {
|
|
3436
|
-
return
|
|
3436
|
+
return join3(getTodosGlobalDir(), "config.json");
|
|
3437
3437
|
}
|
|
3438
3438
|
function normalizeAgent(agent) {
|
|
3439
3439
|
return agent.trim().toLowerCase();
|
|
@@ -3441,7 +3441,7 @@ function normalizeAgent(agent) {
|
|
|
3441
3441
|
function loadConfig() {
|
|
3442
3442
|
if (cached)
|
|
3443
3443
|
return cached;
|
|
3444
|
-
if (!
|
|
3444
|
+
if (!existsSync4(getConfigPath())) {
|
|
3445
3445
|
cached = {};
|
|
3446
3446
|
return cached;
|
|
3447
3447
|
}
|
|
@@ -3454,7 +3454,7 @@ function loadConfig() {
|
|
|
3454
3454
|
}
|
|
3455
3455
|
function saveConfig(config) {
|
|
3456
3456
|
const configPath = getConfigPath();
|
|
3457
|
-
ensureDir2(
|
|
3457
|
+
ensureDir2(dirname2(configPath));
|
|
3458
3458
|
writeJsonFile(configPath, config);
|
|
3459
3459
|
cached = config;
|
|
3460
3460
|
return config;
|
|
@@ -4139,7 +4139,8 @@ var init_redaction = __esm(() => {
|
|
|
4139
4139
|
"input_tokens",
|
|
4140
4140
|
"output_tokens",
|
|
4141
4141
|
"prompt_tokens",
|
|
4142
|
-
"completion_tokens"
|
|
4142
|
+
"completion_tokens",
|
|
4143
|
+
"cost_tokens"
|
|
4143
4144
|
]);
|
|
4144
4145
|
});
|
|
4145
4146
|
|
|
@@ -4523,7 +4524,7 @@ var init_runner_sandbox = __esm(() => {
|
|
|
4523
4524
|
// src/lib/event-hooks.ts
|
|
4524
4525
|
import { createHash, randomUUID } from "crypto";
|
|
4525
4526
|
import { appendFileSync, mkdirSync as mkdirSync3 } from "fs";
|
|
4526
|
-
import { dirname as
|
|
4527
|
+
import { dirname as dirname3, resolve as resolve6 } from "path";
|
|
4527
4528
|
import { createConnection } from "net";
|
|
4528
4529
|
function safeName(name) {
|
|
4529
4530
|
const trimmed = name.trim();
|
|
@@ -4662,7 +4663,7 @@ async function deliverHook(hook, envelope) {
|
|
|
4662
4663
|
output = line.trim();
|
|
4663
4664
|
} else if (hook.target === "file") {
|
|
4664
4665
|
const filePath = resolve6(hook.file_path);
|
|
4665
|
-
mkdirSync3(
|
|
4666
|
+
mkdirSync3(dirname3(filePath), { recursive: true });
|
|
4666
4667
|
appendFileSync(filePath, line);
|
|
4667
4668
|
} else if (hook.target === "socket") {
|
|
4668
4669
|
await writeSocket(hook.socket_path, line);
|
|
@@ -4781,9 +4782,9 @@ var init_event_hooks = __esm(() => {
|
|
|
4781
4782
|
|
|
4782
4783
|
// node_modules/.bun/@hasna+events@0.1.11/node_modules/@hasna/events/dist/index.js
|
|
4783
4784
|
import { chmod, mkdir, readFile, rename, writeFile } from "fs/promises";
|
|
4784
|
-
import { existsSync as
|
|
4785
|
+
import { existsSync as existsSync5 } from "fs";
|
|
4785
4786
|
import { homedir } from "os";
|
|
4786
|
-
import { join as
|
|
4787
|
+
import { join as join4 } from "path";
|
|
4787
4788
|
import { createHmac, timingSafeEqual } from "crypto";
|
|
4788
4789
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4789
4790
|
import { spawn } from "child_process";
|
|
@@ -4884,7 +4885,7 @@ function channelMatchesEvent(channel, event) {
|
|
|
4884
4885
|
return channel.filters.some((filter) => eventMatchesFilter(event, filter));
|
|
4885
4886
|
}
|
|
4886
4887
|
function getEventsDataDir(override) {
|
|
4887
|
-
return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] ||
|
|
4888
|
+
return override || process.env[HASNA_EVENTS_DIR_ENV] || process.env[HASNA_EVENTS_HOME_ENV] || join4(homedir(), ".hasna", "events");
|
|
4888
4889
|
}
|
|
4889
4890
|
|
|
4890
4891
|
class JsonEventsStore {
|
|
@@ -4894,9 +4895,9 @@ class JsonEventsStore {
|
|
|
4894
4895
|
deliveriesPath;
|
|
4895
4896
|
constructor(dataDir = getEventsDataDir()) {
|
|
4896
4897
|
this.dataDir = dataDir;
|
|
4897
|
-
this.channelsPath =
|
|
4898
|
-
this.eventsPath =
|
|
4899
|
-
this.deliveriesPath =
|
|
4898
|
+
this.channelsPath = join4(dataDir, "channels.json");
|
|
4899
|
+
this.eventsPath = join4(dataDir, "events.json");
|
|
4900
|
+
this.deliveriesPath = join4(dataDir, "deliveries.json");
|
|
4900
4901
|
}
|
|
4901
4902
|
async init() {
|
|
4902
4903
|
await mkdir(this.dataDir, { recursive: true, mode: 448 });
|
|
@@ -4968,7 +4969,7 @@ class JsonEventsStore {
|
|
|
4968
4969
|
};
|
|
4969
4970
|
}
|
|
4970
4971
|
async ensureArrayFile(path) {
|
|
4971
|
-
if (!
|
|
4972
|
+
if (!existsSync5(path)) {
|
|
4972
4973
|
await writeFile(path, `[]
|
|
4973
4974
|
`, { encoding: "utf-8", mode: 384 });
|
|
4974
4975
|
}
|
|
@@ -5741,7 +5742,7 @@ var init_shared_events = __esm(() => {
|
|
|
5741
5742
|
});
|
|
5742
5743
|
|
|
5743
5744
|
// src/lib/secret-redaction.ts
|
|
5744
|
-
import { readFileSync as
|
|
5745
|
+
import { readFileSync as readFileSync2, existsSync as existsSync6 } from "fs";
|
|
5745
5746
|
function registerCustomRedactor(fn) {
|
|
5746
5747
|
customRedactors.push(fn);
|
|
5747
5748
|
}
|
|
@@ -5806,9 +5807,9 @@ function scanAndRedactText(text, options = {}) {
|
|
|
5806
5807
|
};
|
|
5807
5808
|
}
|
|
5808
5809
|
function scanFileForSecrets(path, options = {}) {
|
|
5809
|
-
if (!
|
|
5810
|
+
if (!existsSync6(path))
|
|
5810
5811
|
throw new Error(`File not found: ${path}`);
|
|
5811
|
-
const content =
|
|
5812
|
+
const content = readFileSync2(path, "utf8");
|
|
5812
5813
|
return scanAndRedactText(content, options);
|
|
5813
5814
|
}
|
|
5814
5815
|
function safeStringify(value, space) {
|
|
@@ -9334,8 +9335,8 @@ var init_boards = __esm(() => {
|
|
|
9334
9335
|
|
|
9335
9336
|
// src/lib/artifact-store.ts
|
|
9336
9337
|
import { createHash as createHash2 } from "crypto";
|
|
9337
|
-
import { existsSync as
|
|
9338
|
-
import { basename, dirname as
|
|
9338
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync3, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
9339
|
+
import { basename, dirname as dirname4, join as join5, resolve as resolve7 } from "path";
|
|
9339
9340
|
import { tmpdir as tmpdir2 } from "os";
|
|
9340
9341
|
function isInMemoryDb2(path) {
|
|
9341
9342
|
return path === ":memory:" || path.startsWith("file::memory:");
|
|
@@ -9347,15 +9348,15 @@ function artifactStoreRoot() {
|
|
|
9347
9348
|
return resolve7(process.env["TODOS_ARTIFACTS_DIR"]);
|
|
9348
9349
|
const dbPath = getDatabasePath();
|
|
9349
9350
|
if (isInMemoryDb2(dbPath))
|
|
9350
|
-
return
|
|
9351
|
-
return
|
|
9351
|
+
return join5(tmpdir2(), "hasna-todos-artifacts");
|
|
9352
|
+
return join5(dirname4(resolve7(dbPath)), "artifacts");
|
|
9352
9353
|
}
|
|
9353
9354
|
function artifactStorePath(relativePath) {
|
|
9354
9355
|
const normalized = relativePath.replace(/\\/g, "/");
|
|
9355
9356
|
if (normalized.includes("..") || normalized.startsWith("/") || normalized.length === 0) {
|
|
9356
9357
|
throw new Error("Invalid artifact store path");
|
|
9357
9358
|
}
|
|
9358
|
-
return
|
|
9359
|
+
return join5(artifactStoreRoot(), normalized);
|
|
9359
9360
|
}
|
|
9360
9361
|
function sha256(buffer) {
|
|
9361
9362
|
return createHash2("sha256").update(buffer).digest("hex");
|
|
@@ -9396,12 +9397,12 @@ function mediaTypeFor(path, textLike) {
|
|
|
9396
9397
|
}
|
|
9397
9398
|
function storeArtifactContent(input) {
|
|
9398
9399
|
const sourcePath = resolve7(input.path);
|
|
9399
|
-
if (!
|
|
9400
|
+
if (!existsSync7(sourcePath))
|
|
9400
9401
|
return null;
|
|
9401
9402
|
const sourceStat = statSync2(sourcePath);
|
|
9402
9403
|
if (!sourceStat.isFile())
|
|
9403
9404
|
throw new Error(`Artifact path is not a file: ${input.path}`);
|
|
9404
|
-
const sourceBuffer =
|
|
9405
|
+
const sourceBuffer = readFileSync3(sourcePath);
|
|
9405
9406
|
const sourceSha = sha256(sourceBuffer);
|
|
9406
9407
|
const textLike = isTextLike(sourceBuffer, input.path);
|
|
9407
9408
|
let storedBuffer = sourceBuffer;
|
|
@@ -9413,10 +9414,10 @@ function storeArtifactContent(input) {
|
|
|
9413
9414
|
redactionStatus = "redacted";
|
|
9414
9415
|
}
|
|
9415
9416
|
const storedSha = sha256(storedBuffer);
|
|
9416
|
-
const relativePath =
|
|
9417
|
+
const relativePath = join5("sha256", storedSha.slice(0, 2), storedSha).replace(/\\/g, "/");
|
|
9417
9418
|
const destination = artifactStorePath(relativePath);
|
|
9418
|
-
if (!
|
|
9419
|
-
mkdirSync4(
|
|
9419
|
+
if (!existsSync7(destination)) {
|
|
9420
|
+
mkdirSync4(dirname4(destination), { recursive: true });
|
|
9420
9421
|
writeFileSync2(destination, storedBuffer);
|
|
9421
9422
|
}
|
|
9422
9423
|
const createdAt = input.created_at || new Date().toISOString();
|
|
@@ -9475,7 +9476,7 @@ function verifyStoredArtifact(input) {
|
|
|
9475
9476
|
};
|
|
9476
9477
|
}
|
|
9477
9478
|
const storedPath = artifactStorePath(store.relative_path);
|
|
9478
|
-
if (!
|
|
9479
|
+
if (!existsSync7(storedPath)) {
|
|
9479
9480
|
return {
|
|
9480
9481
|
id: input.id,
|
|
9481
9482
|
path: input.path,
|
|
@@ -9488,7 +9489,7 @@ function verifyStoredArtifact(input) {
|
|
|
9488
9489
|
message: "stored artifact content is missing"
|
|
9489
9490
|
};
|
|
9490
9491
|
}
|
|
9491
|
-
const buffer =
|
|
9492
|
+
const buffer = readFileSync3(storedPath);
|
|
9492
9493
|
const actualSha = sha256(buffer);
|
|
9493
9494
|
const actualSize = buffer.length;
|
|
9494
9495
|
const ok = actualSha === store.sha256 && actualSize === store.size_bytes;
|
|
@@ -9508,7 +9509,7 @@ function exportStoredArtifactContent(input) {
|
|
|
9508
9509
|
const report = verifyStoredArtifact(input);
|
|
9509
9510
|
if (report.status !== "ok" || !report.relative_path || !report.actual_sha256 || report.actual_size_bytes === null)
|
|
9510
9511
|
return null;
|
|
9511
|
-
const content =
|
|
9512
|
+
const content = readFileSync3(artifactStorePath(report.relative_path));
|
|
9512
9513
|
return {
|
|
9513
9514
|
artifact_id: input.id,
|
|
9514
9515
|
sha256: report.actual_sha256,
|
|
@@ -9534,7 +9535,7 @@ function importStoredArtifactContent(content) {
|
|
|
9534
9535
|
};
|
|
9535
9536
|
}
|
|
9536
9537
|
const destination = artifactStorePath(content.relative_path);
|
|
9537
|
-
mkdirSync4(
|
|
9538
|
+
mkdirSync4(dirname4(destination), { recursive: true });
|
|
9538
9539
|
writeFileSync2(destination, buffer);
|
|
9539
9540
|
return {
|
|
9540
9541
|
id: content.artifact_id,
|
|
@@ -9555,29 +9556,29 @@ function getArtifactStoreRoot(dbPath) {
|
|
|
9555
9556
|
return resolve7(process.env["TODOS_ARTIFACTS_DIR"]);
|
|
9556
9557
|
const path = dbPath ?? getDatabasePath();
|
|
9557
9558
|
if (isInMemoryDb2(path))
|
|
9558
|
-
return
|
|
9559
|
-
return
|
|
9559
|
+
return join5(tmpdir2(), "hasna-todos-artifacts");
|
|
9560
|
+
return join5(dirname4(resolve7(path)), "artifacts");
|
|
9560
9561
|
}
|
|
9561
9562
|
function computeContentHash(path) {
|
|
9562
|
-
return sha256(
|
|
9563
|
+
return sha256(readFileSync3(resolve7(path)));
|
|
9563
9564
|
}
|
|
9564
9565
|
function storeArtifactFile(input) {
|
|
9565
9566
|
const sourcePath = resolve7(input.sourcePath);
|
|
9566
|
-
if (!
|
|
9567
|
+
if (!existsSync7(sourcePath)) {
|
|
9567
9568
|
throw new Error(`Source file not found: ${input.sourcePath}`);
|
|
9568
9569
|
}
|
|
9569
9570
|
if (!statSync2(sourcePath).isFile()) {
|
|
9570
9571
|
throw new Error(`Source path is not a file: ${input.sourcePath}`);
|
|
9571
9572
|
}
|
|
9572
|
-
const buffer =
|
|
9573
|
+
const buffer = readFileSync3(sourcePath);
|
|
9573
9574
|
const contentHash = sha256(buffer);
|
|
9574
9575
|
const mimeType = mediaTypeFor(sourcePath, isTextLike(buffer, sourcePath));
|
|
9575
9576
|
const storageMode = input.storageMode ?? "copy";
|
|
9576
9577
|
let localPath = sourcePath;
|
|
9577
9578
|
if (storageMode === "copy") {
|
|
9578
9579
|
const fileName = input.name && input.name.trim().length > 0 ? basename(input.name) : basename(sourcePath);
|
|
9579
|
-
const destination =
|
|
9580
|
-
mkdirSync4(
|
|
9580
|
+
const destination = join5(getArtifactStoreRoot(input.dbPath), input.artifactId, fileName);
|
|
9581
|
+
mkdirSync4(dirname4(destination), { recursive: true });
|
|
9581
9582
|
writeFileSync2(destination, buffer);
|
|
9582
9583
|
localPath = destination;
|
|
9583
9584
|
}
|
|
@@ -9586,11 +9587,11 @@ function storeArtifactFile(input) {
|
|
|
9586
9587
|
function deleteStoredArtifactFile(localPath, storageMode, _dbPath2) {
|
|
9587
9588
|
if (storageMode === "reference")
|
|
9588
9589
|
return false;
|
|
9589
|
-
if (!localPath || !
|
|
9590
|
+
if (!localPath || !existsSync7(localPath))
|
|
9590
9591
|
return false;
|
|
9591
9592
|
rmSync(localPath, { force: true });
|
|
9592
9593
|
try {
|
|
9593
|
-
rmSync(
|
|
9594
|
+
rmSync(dirname4(localPath), { recursive: false });
|
|
9594
9595
|
} catch {}
|
|
9595
9596
|
return true;
|
|
9596
9597
|
}
|
|
@@ -9612,7 +9613,7 @@ function buildArtifactExportManifest(artifacts, dbPath) {
|
|
|
9612
9613
|
}
|
|
9613
9614
|
function writeArtifactExportManifest(manifest, outputPath) {
|
|
9614
9615
|
const destination = resolve7(outputPath);
|
|
9615
|
-
mkdirSync4(
|
|
9616
|
+
mkdirSync4(dirname4(destination), { recursive: true });
|
|
9616
9617
|
writeFileSync2(destination, `${JSON.stringify(manifest, null, 2)}
|
|
9617
9618
|
`);
|
|
9618
9619
|
return destination;
|
|
@@ -10857,28 +10858,126 @@ var init_dispatches = __esm(() => {
|
|
|
10857
10858
|
init_database();
|
|
10858
10859
|
init_types();
|
|
10859
10860
|
});
|
|
10860
|
-
|
|
10861
|
-
|
|
10862
|
-
|
|
10863
|
-
|
|
10864
|
-
|
|
10865
|
-
|
|
10866
|
-
|
|
10867
|
-
|
|
10868
|
-
|
|
10869
|
-
|
|
10870
|
-
|
|
10871
|
-
|
|
10872
|
-
|
|
10873
|
-
|
|
10874
|
-
|
|
10875
|
-
|
|
10876
|
-
|
|
10861
|
+
// package.json
|
|
10862
|
+
var package_default = {
|
|
10863
|
+
name: "@hasna/todos",
|
|
10864
|
+
version: "0.11.92",
|
|
10865
|
+
description: "Universal task management for AI coding agents - CLI + MCP server + interactive TUI",
|
|
10866
|
+
type: "module",
|
|
10867
|
+
main: "dist/index.js",
|
|
10868
|
+
types: "dist/index.d.ts",
|
|
10869
|
+
bin: {
|
|
10870
|
+
todos: "dist/cli/index.js",
|
|
10871
|
+
"todos-mcp": "dist/mcp/index.js",
|
|
10872
|
+
"todos-serve": "dist/server/index.js"
|
|
10873
|
+
},
|
|
10874
|
+
exports: {
|
|
10875
|
+
".": {
|
|
10876
|
+
types: "./dist/index.d.ts",
|
|
10877
|
+
import: "./dist/index.js"
|
|
10878
|
+
},
|
|
10879
|
+
"./sdk": {
|
|
10880
|
+
types: "./dist/sdk/index.d.ts",
|
|
10881
|
+
import: "./dist/sdk/index.js"
|
|
10882
|
+
},
|
|
10883
|
+
"./mcp": {
|
|
10884
|
+
types: "./dist/mcp.d.ts",
|
|
10885
|
+
import: "./dist/mcp.js"
|
|
10886
|
+
},
|
|
10887
|
+
"./registry": {
|
|
10888
|
+
types: "./dist/registry.d.ts",
|
|
10889
|
+
import: "./dist/registry.js"
|
|
10890
|
+
},
|
|
10891
|
+
"./contracts": {
|
|
10892
|
+
types: "./dist/contracts.d.ts",
|
|
10893
|
+
import: "./dist/contracts.js"
|
|
10894
|
+
},
|
|
10895
|
+
"./storage": {
|
|
10896
|
+
types: "./dist/storage.d.ts",
|
|
10897
|
+
import: "./dist/storage.js"
|
|
10877
10898
|
}
|
|
10878
|
-
}
|
|
10879
|
-
|
|
10899
|
+
},
|
|
10900
|
+
workspaces: [
|
|
10901
|
+
"dashboard"
|
|
10902
|
+
],
|
|
10903
|
+
files: [
|
|
10904
|
+
"dist",
|
|
10905
|
+
"dashboard/dist",
|
|
10906
|
+
"LICENSE",
|
|
10907
|
+
"README.md"
|
|
10908
|
+
],
|
|
10909
|
+
scripts: {
|
|
10910
|
+
build: "rm -rf dist dashboard/dist && cd dashboard && bun install --frozen-lockfile && bun run build && cd .. && bun build src/cli/index.tsx --outdir dist/cli --target bun --external ink --external react --external chalk --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/server/index.ts --outdir dist/server --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/sdk/index.ts --outdir dist/sdk --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/index.ts src/mcp.ts src/registry.ts src/contracts.ts src/storage.ts --outdir dist --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && tsc --emitDeclarationOnly --outDir dist",
|
|
10911
|
+
"build:server": "rm -rf dist && bun build src/cli/index.tsx --outdir dist/cli --target bun --external ink --external react --external chalk --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/mcp/index.ts --outdir dist/mcp --target bun --external @modelcontextprotocol/sdk --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/server/index.ts --outdir dist/server --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/sdk/index.ts --outdir dist/sdk --target bun --external '@hasna/contracts' --external '@hasna/contracts/*' && bun build src/index.ts src/mcp.ts src/registry.ts src/contracts.ts src/storage.ts --outdir dist --target bun --external '@hasna/contracts' --external '@hasna/contracts/*'",
|
|
10912
|
+
migrate: "bun run src/server/index.ts migrate",
|
|
10913
|
+
"backfill:comment-redaction": "bun run src/server/index.ts redact-comments",
|
|
10914
|
+
"generate:sdk": "bun run scripts/generate-sdk.ts",
|
|
10915
|
+
"build:dashboard": "cd dashboard && bun install --frozen-lockfile && bun run build",
|
|
10916
|
+
typecheck: "tsc --noEmit",
|
|
10917
|
+
test: "bun test",
|
|
10918
|
+
"test:no-cloud": "bun test src/no-cloud-boundary.test.ts src/local-first.test.ts src/lib/public-release-gate.test.ts",
|
|
10919
|
+
"dev:cli": "bun run src/cli/index.tsx",
|
|
10920
|
+
"dev:mcp": "bun run src/mcp/index.ts",
|
|
10921
|
+
"dev:serve": "bun run src/server/index.ts",
|
|
10922
|
+
"verify:release": "bun run scripts/verify-public-release.ts --mode=review",
|
|
10923
|
+
prepublishOnly: "bun run scripts/verify-public-release.ts --mode=publish",
|
|
10924
|
+
postinstall: "mkdir -p $HOME/.hasna/todos $HOME/.hasna/todos/training 2>/dev/null || true"
|
|
10925
|
+
},
|
|
10926
|
+
keywords: [
|
|
10927
|
+
"todos",
|
|
10928
|
+
"tasks",
|
|
10929
|
+
"mcp",
|
|
10930
|
+
"ai",
|
|
10931
|
+
"coding-agent",
|
|
10932
|
+
"claude",
|
|
10933
|
+
"codex",
|
|
10934
|
+
"gemini",
|
|
10935
|
+
"tui",
|
|
10936
|
+
"cli",
|
|
10937
|
+
"dashboard"
|
|
10938
|
+
],
|
|
10939
|
+
publishConfig: {
|
|
10940
|
+
registry: "https://registry.npmjs.org",
|
|
10941
|
+
access: "public"
|
|
10942
|
+
},
|
|
10943
|
+
repository: {
|
|
10944
|
+
type: "git",
|
|
10945
|
+
url: "https://github.com/hasna/todos.git"
|
|
10946
|
+
},
|
|
10947
|
+
homepage: "https://github.com/hasna/todos",
|
|
10948
|
+
bugs: {
|
|
10949
|
+
url: "https://github.com/hasna/todos/issues"
|
|
10950
|
+
},
|
|
10951
|
+
engines: {
|
|
10952
|
+
bun: ">=1.0.0"
|
|
10953
|
+
},
|
|
10954
|
+
packageManager: "bun@1.3.14",
|
|
10955
|
+
author: "Andrei Hasna <andrei@hasna.com>",
|
|
10956
|
+
license: "Apache-2.0",
|
|
10957
|
+
dependencies: {
|
|
10958
|
+
"@hasna/contracts": "0.5.2",
|
|
10959
|
+
"@hasna/events": "^0.1.11",
|
|
10960
|
+
"@modelcontextprotocol/sdk": "^1.12.1",
|
|
10961
|
+
chalk: "^5.4.1",
|
|
10962
|
+
commander: "^13.1.0",
|
|
10963
|
+
ink: "^5.2.0",
|
|
10964
|
+
react: "^18.3.1",
|
|
10965
|
+
zod: "^3.24.2"
|
|
10966
|
+
},
|
|
10967
|
+
overrides: {
|
|
10968
|
+
"fast-uri": "3.1.2"
|
|
10969
|
+
},
|
|
10970
|
+
devDependencies: {
|
|
10971
|
+
"@types/bun": "^1.2.4",
|
|
10972
|
+
"@types/react": "^18.3.18",
|
|
10973
|
+
"bun-types": "1.3.9",
|
|
10974
|
+
typescript: "^5.7.3"
|
|
10880
10975
|
}
|
|
10881
|
-
|
|
10976
|
+
};
|
|
10977
|
+
|
|
10978
|
+
// src/lib/package-version.ts
|
|
10979
|
+
function getPackageVersion(_fromUrl = import.meta.url) {
|
|
10980
|
+
return package_default.version || "0.0.0";
|
|
10882
10981
|
}
|
|
10883
10982
|
|
|
10884
10983
|
// src/mcp/token-utils.ts
|
|
@@ -13465,7 +13564,7 @@ var TODOS_JSON_CONTRACTS_MANIFEST = createJsonContractsManifest({
|
|
|
13465
13564
|
});
|
|
13466
13565
|
// src/lib/onboarding-fixtures.ts
|
|
13467
13566
|
import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync3 } from "fs";
|
|
13468
|
-
import { join as
|
|
13567
|
+
import { join as join6 } from "path";
|
|
13469
13568
|
|
|
13470
13569
|
// src/lib/local-bridge.ts
|
|
13471
13570
|
init_database();
|
|
@@ -14372,7 +14471,7 @@ function writeOnboardingFixtureFiles(directory) {
|
|
|
14372
14471
|
mkdirSync5(directory, { recursive: true });
|
|
14373
14472
|
const files = [];
|
|
14374
14473
|
for (const fixture of allFixtures()) {
|
|
14375
|
-
const path =
|
|
14474
|
+
const path = join6(directory, `${fixture.summary.name}.bridge.json`);
|
|
14376
14475
|
writeFileSync3(path, `${JSON.stringify(fixture.bundle, null, 2)}
|
|
14377
14476
|
`, "utf-8");
|
|
14378
14477
|
files.push(path);
|
|
@@ -14389,8 +14488,8 @@ function importOnboardingFixture(options = {}) {
|
|
|
14389
14488
|
// src/lib/local-backups.ts
|
|
14390
14489
|
init_database();
|
|
14391
14490
|
import { createHash as createHash3 } from "crypto";
|
|
14392
|
-
import { readFileSync as
|
|
14393
|
-
import { dirname as
|
|
14491
|
+
import { readFileSync as readFileSync4, writeFileSync as writeFileSync4 } from "fs";
|
|
14492
|
+
import { dirname as dirname5, resolve as resolve8 } from "path";
|
|
14394
14493
|
import { mkdirSync as mkdirSync6 } from "fs";
|
|
14395
14494
|
var TODOS_LOCAL_BACKUP_KIND = "hasna.todos.local-backup";
|
|
14396
14495
|
var TODOS_LOCAL_BACKUP_SCHEMA_VERSION = 1;
|
|
@@ -14498,13 +14597,13 @@ function createLocalBackup(options = {}, db) {
|
|
|
14498
14597
|
}
|
|
14499
14598
|
function writeLocalBackupFile(backup, outputPath) {
|
|
14500
14599
|
const path = resolve8(outputPath);
|
|
14501
|
-
mkdirSync6(
|
|
14600
|
+
mkdirSync6(dirname5(path), { recursive: true });
|
|
14502
14601
|
writeFileSync4(path, `${JSON.stringify(backup, null, 2)}
|
|
14503
14602
|
`);
|
|
14504
14603
|
return path;
|
|
14505
14604
|
}
|
|
14506
14605
|
function readLocalBackupFile(path) {
|
|
14507
|
-
return JSON.parse(
|
|
14606
|
+
return JSON.parse(readFileSync4(resolve8(path), "utf-8"));
|
|
14508
14607
|
}
|
|
14509
14608
|
function verifyLocalBackup(value, options = {}, db) {
|
|
14510
14609
|
const verifiedAt = options.verified_at ?? now();
|
|
@@ -15198,7 +15297,7 @@ function renderLocalSnapshotMarkdown(snapshot) {
|
|
|
15198
15297
|
}
|
|
15199
15298
|
// src/lib/sdk-integration-fixtures.ts
|
|
15200
15299
|
import { mkdirSync as mkdirSync7, writeFileSync as writeFileSync5 } from "fs";
|
|
15201
|
-
import { join as
|
|
15300
|
+
import { join as join7 } from "path";
|
|
15202
15301
|
|
|
15203
15302
|
// src/cli-mcp-parity.ts
|
|
15204
15303
|
function source4(version) {
|
|
@@ -17154,7 +17253,7 @@ function writeSdkIntegrationFixtures(directory, options = {}) {
|
|
|
17154
17253
|
];
|
|
17155
17254
|
const written = [];
|
|
17156
17255
|
for (const [name, payload] of files) {
|
|
17157
|
-
const file =
|
|
17256
|
+
const file = join7(directory, name);
|
|
17158
17257
|
writeFileSync5(file, `${JSON.stringify(payload, null, 2)}
|
|
17159
17258
|
`, "utf-8");
|
|
17160
17259
|
written.push(file);
|
|
@@ -18436,8 +18535,8 @@ function renderLocalAuditLedgerMarkdown(ledger) {
|
|
|
18436
18535
|
// src/lib/release-compatibility.ts
|
|
18437
18536
|
init_migrations();
|
|
18438
18537
|
init_schema();
|
|
18439
|
-
import { readFileSync as
|
|
18440
|
-
import { join as
|
|
18538
|
+
import { readFileSync as readFileSync5 } from "fs";
|
|
18539
|
+
import { join as join8, resolve as resolve9 } from "path";
|
|
18441
18540
|
import { Database as Database2 } from "bun:sqlite";
|
|
18442
18541
|
var LOCAL_RELEASE_COMPATIBILITY_SCHEMA_VERSION = 1;
|
|
18443
18542
|
var EXPECTED_PACKAGE_NAME = "@hasna/todos";
|
|
@@ -18483,7 +18582,7 @@ function warn(id, message, details) {
|
|
|
18483
18582
|
return { id, status: "warning", message, details };
|
|
18484
18583
|
}
|
|
18485
18584
|
function readPackageJson(root) {
|
|
18486
|
-
return JSON.parse(
|
|
18585
|
+
return JSON.parse(readFileSync5(join8(root, "package.json"), "utf8"));
|
|
18487
18586
|
}
|
|
18488
18587
|
function sortedKeys(value) {
|
|
18489
18588
|
return Object.keys(value ?? {}).sort((left, right) => left.localeCompare(right));
|
|
@@ -23970,7 +24069,7 @@ function createPostgresTodosStorageAdapter(options) {
|
|
|
23970
24069
|
get: (id) => store.get("plans", id),
|
|
23971
24070
|
list: async (projectId) => (await store.list("plans")).filter((plan) => projectId === undefined || plan.project_id === projectId).sort((a, b) => a.name.localeCompare(b.name)),
|
|
23972
24071
|
update: (id, input) => updatePlan2(id, input, store),
|
|
23973
|
-
delete: (id, context) => store.
|
|
24072
|
+
delete: (id, context) => store.deletePlan(id, context)
|
|
23974
24073
|
},
|
|
23975
24074
|
agents: {
|
|
23976
24075
|
register: (input, context) => registerAgent2(input, store, context),
|
|
@@ -24254,6 +24353,62 @@ class PostgresJsonRecordStore {
|
|
|
24254
24353
|
}
|
|
24255
24354
|
return value;
|
|
24256
24355
|
}
|
|
24356
|
+
async completeTask(id, agentId, options) {
|
|
24357
|
+
await this.ensureSchema();
|
|
24358
|
+
const operationTimestamp = new Date().toISOString();
|
|
24359
|
+
const completedAt2 = options?.completed_at ?? operationTimestamp;
|
|
24360
|
+
const evidence = options ? {
|
|
24361
|
+
...options.files_changed !== undefined ? { files_changed: options.files_changed } : {},
|
|
24362
|
+
...options.test_results !== undefined ? { test_results: options.test_results } : {},
|
|
24363
|
+
...options.commit_hash !== undefined ? { commit_hash: options.commit_hash } : {},
|
|
24364
|
+
...options.notes !== undefined ? { notes: options.notes } : {},
|
|
24365
|
+
...options.attachment_ids !== undefined ? { attachment_ids: options.attachment_ids } : {}
|
|
24366
|
+
} : {};
|
|
24367
|
+
const hasEvidence = Object.keys(evidence).length > 0;
|
|
24368
|
+
const hasConfidence = options?.confidence !== undefined;
|
|
24369
|
+
const result = await this.options.client.query(`/* todos:complete-task-atomic */ UPDATE ${this.tableName}
|
|
24370
|
+
SET payload = payload || jsonb_build_object(
|
|
24371
|
+
'status', 'completed',
|
|
24372
|
+
'assigned_to', CASE
|
|
24373
|
+
WHEN jsonb_typeof(payload->'assigned_to') = 'string' THEN payload->'assigned_to'
|
|
24374
|
+
ELSE COALESCE(to_jsonb($3::text), 'null'::jsonb)
|
|
24375
|
+
END,
|
|
24376
|
+
'completed_at', $4::text,
|
|
24377
|
+
'updated_at', $9::text,
|
|
24378
|
+
'version', COALESCE((payload->>'version')::integer, 0) + 1,
|
|
24379
|
+
'metadata',
|
|
24380
|
+
(CASE WHEN jsonb_typeof(payload->'metadata') = 'object'
|
|
24381
|
+
THEN payload->'metadata' ELSE '{}'::jsonb END)
|
|
24382
|
+
|| CASE WHEN $5::boolean THEN jsonb_build_object(
|
|
24383
|
+
'_evidence',
|
|
24384
|
+
(CASE WHEN jsonb_typeof(payload->'metadata'->'_evidence') = 'object'
|
|
24385
|
+
THEN payload->'metadata'->'_evidence' ELSE '{}'::jsonb END) || $6::jsonb
|
|
24386
|
+
) ELSE '{}'::jsonb END
|
|
24387
|
+
|| CASE WHEN $7::boolean THEN jsonb_build_object(
|
|
24388
|
+
'_completion',
|
|
24389
|
+
(CASE WHEN jsonb_typeof(payload->'metadata'->'_completion') = 'object'
|
|
24390
|
+
THEN payload->'metadata'->'_completion' ELSE '{}'::jsonb END)
|
|
24391
|
+
|| jsonb_build_object('confidence', $8::double precision)
|
|
24392
|
+
) ELSE '{}'::jsonb END
|
|
24393
|
+
) || CASE WHEN $7::boolean
|
|
24394
|
+
THEN jsonb_build_object('confidence', $8::double precision)
|
|
24395
|
+
ELSE '{}'::jsonb END,
|
|
24396
|
+
updated_at = $9::timestamptz,
|
|
24397
|
+
version = COALESCE(version, 0) + 1
|
|
24398
|
+
WHERE service = $1 AND object_type = 'tasks' AND object_id = $2 AND deleted_at IS NULL
|
|
24399
|
+
RETURNING payload`, [
|
|
24400
|
+
this.service,
|
|
24401
|
+
id,
|
|
24402
|
+
agentId ?? null,
|
|
24403
|
+
completedAt2,
|
|
24404
|
+
hasEvidence,
|
|
24405
|
+
jsonbParam(evidence),
|
|
24406
|
+
hasConfidence,
|
|
24407
|
+
options?.confidence ?? null,
|
|
24408
|
+
operationTimestamp
|
|
24409
|
+
]);
|
|
24410
|
+
return result.rows[0] ? payloadRecord2(result.rows[0].payload) : null;
|
|
24411
|
+
}
|
|
24257
24412
|
async renameProject(id, newSlug, name, context = {}) {
|
|
24258
24413
|
await this.ensureSchema();
|
|
24259
24414
|
const normalizedSlug = slugifyRaw(newSlug);
|
|
@@ -24370,6 +24525,41 @@ class PostgresJsonRecordStore {
|
|
|
24370
24525
|
version: numberValue3(existing["version"])
|
|
24371
24526
|
}, context);
|
|
24372
24527
|
}
|
|
24528
|
+
async deletePlan(id, context = {}) {
|
|
24529
|
+
await this.ensureSchema();
|
|
24530
|
+
const timestamp2 = new Date().toISOString();
|
|
24531
|
+
const result = await this.options.client.query(`/* todos:delete-plan-atomic */ WITH target AS (
|
|
24532
|
+
SELECT payload FROM ${this.tableName}
|
|
24533
|
+
WHERE service = $1 AND object_type = 'plans' AND object_id = $2 AND deleted_at IS NULL
|
|
24534
|
+
FOR UPDATE
|
|
24535
|
+
), updated_tasks AS (
|
|
24536
|
+
UPDATE ${this.tableName} r SET
|
|
24537
|
+
payload = jsonb_set(
|
|
24538
|
+
jsonb_set(r.payload, '{plan_id}', 'null'::jsonb, true),
|
|
24539
|
+
'{version}',
|
|
24540
|
+
to_jsonb(COALESCE((r.payload->>'version')::int, 0) + 1),
|
|
24541
|
+
true
|
|
24542
|
+
) || jsonb_build_object('updated_at', $3::text),
|
|
24543
|
+
updated_at = $3::timestamptz,
|
|
24544
|
+
source_machine_id = COALESCE($4, r.source_machine_id),
|
|
24545
|
+
version = COALESCE(r.version, 0) + 1
|
|
24546
|
+
WHERE r.service = $1 AND r.object_type = 'tasks' AND r.deleted_at IS NULL
|
|
24547
|
+
AND r.payload->>'plan_id' = $2 AND EXISTS (SELECT 1 FROM target)
|
|
24548
|
+
RETURNING 1
|
|
24549
|
+
), deleted_plan AS (
|
|
24550
|
+
UPDATE ${this.tableName} r SET
|
|
24551
|
+
deleted_at = $3::timestamptz,
|
|
24552
|
+
updated_at = $3::timestamptz,
|
|
24553
|
+
source_machine_id = COALESCE($4, r.source_machine_id),
|
|
24554
|
+
version = COALESCE(r.version, 0) + 1
|
|
24555
|
+
FROM target
|
|
24556
|
+
WHERE r.service = $1 AND r.object_type = 'plans' AND r.object_id = $2 AND r.deleted_at IS NULL
|
|
24557
|
+
RETURNING 1
|
|
24558
|
+
) SELECT
|
|
24559
|
+
EXISTS (SELECT 1 FROM target) AS found,
|
|
24560
|
+
(SELECT count(*) FROM updated_tasks) AS tasks_updated`, [this.service, id, timestamp2, this.machineId(context)]);
|
|
24561
|
+
return Boolean(result.rows[0]?.found);
|
|
24562
|
+
}
|
|
24373
24563
|
async tombstone(tombstone, context = {}) {
|
|
24374
24564
|
await this.ensureSchema();
|
|
24375
24565
|
const deletedAt = stringValue2(tombstone.deleted_at) ?? new Date().toISOString();
|
|
@@ -24523,14 +24713,10 @@ async function startTask2(id, agentId, store) {
|
|
|
24523
24713
|
}, store);
|
|
24524
24714
|
}
|
|
24525
24715
|
async function completeTask2(id, agentId, options, store) {
|
|
24526
|
-
const task2 = await
|
|
24527
|
-
|
|
24528
|
-
|
|
24529
|
-
|
|
24530
|
-
completed_at: options?.completed_at ?? new Date().toISOString(),
|
|
24531
|
-
actual_minutes: task2.actual_minutes,
|
|
24532
|
-
confidence: options?.confidence ?? task2.confidence
|
|
24533
|
-
}, store);
|
|
24716
|
+
const task2 = await store.completeTask(id, agentId, options);
|
|
24717
|
+
if (!task2)
|
|
24718
|
+
throw new Error(`tasks record not found: ${id}`);
|
|
24719
|
+
return task2;
|
|
24534
24720
|
}
|
|
24535
24721
|
async function failTask2(id, agentId, reason, options, store) {
|
|
24536
24722
|
const task2 = await requireRecord("tasks", id, store);
|
|
@@ -25104,8 +25290,9 @@ async function resolvePostgresPlanSlug(options) {
|
|
|
25104
25290
|
const used = new Set(plans.filter((plan) => plan.project_id === options.projectId && plan.id !== options.excludeId && plan.slug).map((plan) => plan.slug));
|
|
25105
25291
|
if (options.slug !== undefined) {
|
|
25106
25292
|
const slug = normalizePlanSlug2(options.slug);
|
|
25107
|
-
if (used.has(slug))
|
|
25108
|
-
throw new
|
|
25293
|
+
if (used.has(slug)) {
|
|
25294
|
+
throw new ResourceConflictError("PLAN_SLUG_CONFLICT", `Plan slug already exists in this scope: ${slug}`);
|
|
25295
|
+
}
|
|
25109
25296
|
return slug;
|
|
25110
25297
|
}
|
|
25111
25298
|
const base = planSlugBase4(options.name);
|
|
@@ -25143,13 +25330,22 @@ function numberValue3(value) {
|
|
|
25143
25330
|
return typeof value === "number" && Number.isSafeInteger(value) ? value : null;
|
|
25144
25331
|
}
|
|
25145
25332
|
function isPostgresUniqueViolation(error) {
|
|
25146
|
-
|
|
25333
|
+
if (typeof error !== "object" || error === null)
|
|
25334
|
+
return false;
|
|
25335
|
+
const candidate = error;
|
|
25336
|
+
const states = [candidate.code, candidate.errno, candidate.sqlState, candidate.sqlstate];
|
|
25337
|
+
if (typeof candidate.cause === "object" && candidate.cause !== null) {
|
|
25338
|
+
const cause = candidate.cause;
|
|
25339
|
+
states.push(cause.code, cause.errno, cause.sqlState, cause.sqlstate);
|
|
25340
|
+
}
|
|
25341
|
+
return states.some((state) => String(state) === "23505");
|
|
25147
25342
|
}
|
|
25148
25343
|
function postgresConstraintName(error) {
|
|
25149
25344
|
if (typeof error !== "object" || error === null)
|
|
25150
25345
|
return "";
|
|
25151
25346
|
const candidate = error;
|
|
25152
|
-
const
|
|
25347
|
+
const cause = typeof candidate.cause === "object" && candidate.cause !== null ? candidate.cause : undefined;
|
|
25348
|
+
const constraint = candidate.constraint ?? candidate.constraint_name ?? cause?.constraint ?? cause?.constraint_name;
|
|
25153
25349
|
return typeof constraint === "string" ? constraint : "";
|
|
25154
25350
|
}
|
|
25155
25351
|
|
|
@@ -27167,8 +27363,8 @@ init_task_crud();
|
|
|
27167
27363
|
init_redaction();
|
|
27168
27364
|
import { Database as Database3 } from "bun:sqlite";
|
|
27169
27365
|
import { createHash as createHash10 } from "crypto";
|
|
27170
|
-
import { existsSync as
|
|
27171
|
-
import { basename as basename2, dirname as
|
|
27366
|
+
import { existsSync as existsSync9, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
|
|
27367
|
+
import { basename as basename2, dirname as dirname6, join as join9, resolve as resolve10 } from "path";
|
|
27172
27368
|
|
|
27173
27369
|
// src/lib/task-routing.ts
|
|
27174
27370
|
init_database();
|
|
@@ -27176,7 +27372,7 @@ init_projects();
|
|
|
27176
27372
|
init_task_crud();
|
|
27177
27373
|
init_task_lifecycle();
|
|
27178
27374
|
init_task_lists();
|
|
27179
|
-
import { existsSync as
|
|
27375
|
+
import { existsSync as existsSync8, statSync as statSync3 } from "fs";
|
|
27180
27376
|
var DEFAULT_STALE_IN_PROGRESS_MS = 3 * 24 * 60 * 60 * 1000;
|
|
27181
27377
|
var TERMINAL_WORKFLOW_STATES = new Set([
|
|
27182
27378
|
"failed",
|
|
@@ -27231,7 +27427,7 @@ function routeConcurrencyKey(task2, project, taskList, projectPath) {
|
|
|
27231
27427
|
}
|
|
27232
27428
|
function directoryExists(path) {
|
|
27233
27429
|
try {
|
|
27234
|
-
return
|
|
27430
|
+
return existsSync8(path) && statSync3(path).isDirectory();
|
|
27235
27431
|
} catch {
|
|
27236
27432
|
return false;
|
|
27237
27433
|
}
|
|
@@ -27432,7 +27628,7 @@ function pointerPatch(previous, input, key) {
|
|
|
27432
27628
|
|
|
27433
27629
|
// src/lib/task-route-sources.ts
|
|
27434
27630
|
var TASK_ROUTE_SOURCE_DISCOVERY_SCHEMA_VERSION = "todos.task_route_sources.v1";
|
|
27435
|
-
var TODO_STORE_RELATIVE_PATH =
|
|
27631
|
+
var TODO_STORE_RELATIVE_PATH = join9(".hasna", "todos", "todos.db");
|
|
27436
27632
|
var ROOT_SCAN_MAX_DEPTH = 5;
|
|
27437
27633
|
var SKIPPED_SCAN_DIRS = new Set([
|
|
27438
27634
|
".git",
|
|
@@ -27455,9 +27651,9 @@ function sourceStoreId(sourceDbPath) {
|
|
|
27455
27651
|
function inferSourceRepoPath(sourceDbPath) {
|
|
27456
27652
|
const normalized = normalizePath3(sourceDbPath);
|
|
27457
27653
|
if (normalized.endsWith(TODO_STORE_RELATIVE_PATH)) {
|
|
27458
|
-
return
|
|
27654
|
+
return dirname6(dirname6(dirname6(normalized)));
|
|
27459
27655
|
}
|
|
27460
|
-
return
|
|
27656
|
+
return dirname6(normalized);
|
|
27461
27657
|
}
|
|
27462
27658
|
function createStoreRef(sourceDbPath) {
|
|
27463
27659
|
const normalized = normalizePath3(sourceDbPath);
|
|
@@ -27508,8 +27704,8 @@ function discoverStoresUnderRoot(sourceRoot) {
|
|
|
27508
27704
|
const rootPath = normalizePath3(sourceRoot);
|
|
27509
27705
|
const errors = [];
|
|
27510
27706
|
const stores = [];
|
|
27511
|
-
if (!
|
|
27512
|
-
const ref = createStoreRef(
|
|
27707
|
+
if (!existsSync9(rootPath)) {
|
|
27708
|
+
const ref = createStoreRef(join9(rootPath, TODO_STORE_RELATIVE_PATH));
|
|
27513
27709
|
errors.push({
|
|
27514
27710
|
...ref,
|
|
27515
27711
|
code: "SOURCE_ROOT_MISSING",
|
|
@@ -27521,7 +27717,7 @@ function discoverStoresUnderRoot(sourceRoot) {
|
|
|
27521
27717
|
try {
|
|
27522
27718
|
rootStat = statSync4(rootPath);
|
|
27523
27719
|
} catch (error) {
|
|
27524
|
-
const ref = createStoreRef(
|
|
27720
|
+
const ref = createStoreRef(join9(rootPath, TODO_STORE_RELATIVE_PATH));
|
|
27525
27721
|
errors.push({
|
|
27526
27722
|
...ref,
|
|
27527
27723
|
code: "SOURCE_ROOT_UNREADABLE",
|
|
@@ -27534,8 +27730,8 @@ function discoverStoresUnderRoot(sourceRoot) {
|
|
|
27534
27730
|
return { stores, errors };
|
|
27535
27731
|
}
|
|
27536
27732
|
function scanDirectory(dir, depth) {
|
|
27537
|
-
const candidate =
|
|
27538
|
-
if (
|
|
27733
|
+
const candidate = join9(dir, TODO_STORE_RELATIVE_PATH);
|
|
27734
|
+
if (existsSync9(candidate)) {
|
|
27539
27735
|
stores.push(createStoreRef(candidate));
|
|
27540
27736
|
}
|
|
27541
27737
|
if (depth >= ROOT_SCAN_MAX_DEPTH)
|
|
@@ -27555,7 +27751,7 @@ function discoverStoresUnderRoot(sourceRoot) {
|
|
|
27555
27751
|
for (const entry2 of entries) {
|
|
27556
27752
|
if (!entry2.isDirectory() || SKIPPED_SCAN_DIRS.has(entry2.name))
|
|
27557
27753
|
continue;
|
|
27558
|
-
scanDirectory(
|
|
27754
|
+
scanDirectory(join9(dir, entry2.name), depth + 1);
|
|
27559
27755
|
}
|
|
27560
27756
|
}
|
|
27561
27757
|
scanDirectory(rootPath, 0);
|
|
@@ -27581,7 +27777,7 @@ function collectStoreRefs(input) {
|
|
|
27581
27777
|
};
|
|
27582
27778
|
}
|
|
27583
27779
|
function openReadonlyStore(ref) {
|
|
27584
|
-
if (!
|
|
27780
|
+
if (!existsSync9(ref.source_db_path)) {
|
|
27585
27781
|
throw Object.assign(new Error(`Store does not exist: ${ref.source_db_path}`), { code: "STORE_MISSING" });
|
|
27586
27782
|
}
|
|
27587
27783
|
return new Database3(ref.source_db_path, { readonly: true, create: false });
|
|
@@ -27892,8 +28088,8 @@ init_plans();
|
|
|
27892
28088
|
init_database();
|
|
27893
28089
|
init_projects();
|
|
27894
28090
|
init_tasks();
|
|
27895
|
-
import { existsSync as
|
|
27896
|
-
import { join as
|
|
28091
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
28092
|
+
import { join as join10, resolve as resolve11 } from "path";
|
|
27897
28093
|
var PLAN_MARKDOWN_SCHEMA = "hasna.todos.plan/v1";
|
|
27898
28094
|
function assertSafePathSegment(value, label) {
|
|
27899
28095
|
const trimmed = value.trim();
|
|
@@ -27955,7 +28151,7 @@ function resolvePlanArtifactPaths(input) {
|
|
|
27955
28151
|
const project = resolvePlanArtifactProject(input);
|
|
27956
28152
|
const projectId = assertSafePathSegment(project.id, "project id");
|
|
27957
28153
|
const projectRoot = resolve11(project.path);
|
|
27958
|
-
const directory =
|
|
28154
|
+
const directory = join10(projectRoot, ".hasna", "todos", "plans", projectId);
|
|
27959
28155
|
const planId = input.plan_id ? assertSafePathSegment(input.plan_id, "plan id") : null;
|
|
27960
28156
|
const planSlug = input.plan_slug ? assertSafePathSegment(slugify(input.plan_slug), "plan slug") : null;
|
|
27961
28157
|
const fileName = planId ? planSlug ? `${planSlug}--${planId.slice(0, 8)}.md` : `${planId}.md` : null;
|
|
@@ -27963,7 +28159,7 @@ function resolvePlanArtifactPaths(input) {
|
|
|
27963
28159
|
project_id: project.id,
|
|
27964
28160
|
project_root: projectRoot,
|
|
27965
28161
|
directory,
|
|
27966
|
-
file_path: fileName ?
|
|
28162
|
+
file_path: fileName ? join10(directory, fileName) : directory
|
|
27967
28163
|
};
|
|
27968
28164
|
}
|
|
27969
28165
|
function resolvePlanArtifactCandidatePaths(plan, db) {
|
|
@@ -28129,10 +28325,10 @@ function readPlanArtifact(plan, db) {
|
|
|
28129
28325
|
return null;
|
|
28130
28326
|
const d = db || getDatabase();
|
|
28131
28327
|
const paths = resolvePlanArtifactCandidatePaths(plan, d);
|
|
28132
|
-
const path =
|
|
28328
|
+
const path = existsSync10(paths.primary.file_path) ? paths.primary.file_path : existsSync10(paths.legacy.file_path) ? paths.legacy.file_path : null;
|
|
28133
28329
|
if (!path)
|
|
28134
28330
|
return null;
|
|
28135
|
-
const markdown =
|
|
28331
|
+
const markdown = readFileSync6(path, "utf8");
|
|
28136
28332
|
return {
|
|
28137
28333
|
path,
|
|
28138
28334
|
markdown,
|
|
@@ -28144,7 +28340,7 @@ function inspectPlanArtifact(plan, db) {
|
|
|
28144
28340
|
return null;
|
|
28145
28341
|
const d = db || getDatabase();
|
|
28146
28342
|
const paths = resolvePlanArtifactCandidatePaths(plan, d);
|
|
28147
|
-
const path =
|
|
28343
|
+
const path = existsSync10(paths.primary.file_path) ? paths.primary.file_path : existsSync10(paths.legacy.file_path) ? paths.legacy.file_path : null;
|
|
28148
28344
|
if (!path) {
|
|
28149
28345
|
return {
|
|
28150
28346
|
path: paths.primary.file_path,
|
|
@@ -28156,7 +28352,7 @@ function inspectPlanArtifact(plan, db) {
|
|
|
28156
28352
|
};
|
|
28157
28353
|
}
|
|
28158
28354
|
try {
|
|
28159
|
-
const artifact = parsePlanArtifactMarkdown(
|
|
28355
|
+
const artifact = parsePlanArtifactMarkdown(readFileSync6(path, "utf8"));
|
|
28160
28356
|
return {
|
|
28161
28357
|
path,
|
|
28162
28358
|
exists: true,
|
|
@@ -29233,8 +29429,8 @@ function renderRetrospectiveMarkdown(record) {
|
|
|
29233
29429
|
init_database();
|
|
29234
29430
|
init_projects();
|
|
29235
29431
|
init_task_lists();
|
|
29236
|
-
import { existsSync as
|
|
29237
|
-
import { basename as basename3, dirname as
|
|
29432
|
+
import { existsSync as existsSync11, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
|
|
29433
|
+
import { basename as basename3, dirname as dirname7, resolve as resolve12 } from "path";
|
|
29238
29434
|
function safeStat(path) {
|
|
29239
29435
|
try {
|
|
29240
29436
|
return statSync5(path);
|
|
@@ -29246,15 +29442,15 @@ function canonicalPath(input) {
|
|
|
29246
29442
|
const resolved = resolve12(input);
|
|
29247
29443
|
const stats2 = safeStat(resolved);
|
|
29248
29444
|
if (stats2?.isFile())
|
|
29249
|
-
return
|
|
29445
|
+
return dirname7(resolved);
|
|
29250
29446
|
return resolved;
|
|
29251
29447
|
}
|
|
29252
29448
|
function findUp(start, marker) {
|
|
29253
29449
|
let current = canonicalPath(start);
|
|
29254
29450
|
while (true) {
|
|
29255
|
-
if (
|
|
29451
|
+
if (existsSync11(resolve12(current, marker)))
|
|
29256
29452
|
return current;
|
|
29257
|
-
const parent =
|
|
29453
|
+
const parent = dirname7(current);
|
|
29258
29454
|
if (parent === current)
|
|
29259
29455
|
return null;
|
|
29260
29456
|
current = parent;
|
|
@@ -29264,10 +29460,10 @@ function readPackageJson2(path) {
|
|
|
29264
29460
|
if (!path)
|
|
29265
29461
|
return null;
|
|
29266
29462
|
const file = resolve12(path, "package.json");
|
|
29267
|
-
if (!
|
|
29463
|
+
if (!existsSync11(file))
|
|
29268
29464
|
return null;
|
|
29269
29465
|
try {
|
|
29270
|
-
const parsed = JSON.parse(
|
|
29466
|
+
const parsed = JSON.parse(readFileSync7(file, "utf-8"));
|
|
29271
29467
|
return parsed && typeof parsed === "object" ? parsed : null;
|
|
29272
29468
|
} catch {
|
|
29273
29469
|
return null;
|
|
@@ -29286,7 +29482,7 @@ function workspaceMarker(root, rootPackage) {
|
|
|
29286
29482
|
if (rootPackage?.workspaces)
|
|
29287
29483
|
markers.push("package.json#workspaces");
|
|
29288
29484
|
for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
|
|
29289
|
-
if (
|
|
29485
|
+
if (existsSync11(resolve12(root, marker)))
|
|
29290
29486
|
markers.push(marker);
|
|
29291
29487
|
}
|
|
29292
29488
|
const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
|
|
@@ -29624,21 +29820,21 @@ var gatherTrainingData = async (options = {}) => {
|
|
|
29624
29820
|
};
|
|
29625
29821
|
// src/lib/model-config.ts
|
|
29626
29822
|
init_sync_utils();
|
|
29627
|
-
import { existsSync as
|
|
29628
|
-
import { join as
|
|
29823
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
29824
|
+
import { join as join11 } from "path";
|
|
29629
29825
|
var DEFAULT_MODEL = "gpt-4o-mini";
|
|
29630
29826
|
function getConfigDir() {
|
|
29631
29827
|
return getTodosGlobalDir();
|
|
29632
29828
|
}
|
|
29633
29829
|
function getConfigPath2() {
|
|
29634
|
-
return
|
|
29830
|
+
return join11(getConfigDir(), "config.json");
|
|
29635
29831
|
}
|
|
29636
29832
|
function readConfig() {
|
|
29637
29833
|
const configPath = getConfigPath2();
|
|
29638
|
-
if (!
|
|
29834
|
+
if (!existsSync12(configPath))
|
|
29639
29835
|
return {};
|
|
29640
29836
|
try {
|
|
29641
|
-
const raw =
|
|
29837
|
+
const raw = readFileSync8(configPath, "utf-8");
|
|
29642
29838
|
return JSON.parse(raw);
|
|
29643
29839
|
} catch {
|
|
29644
29840
|
return {};
|
|
@@ -29646,7 +29842,7 @@ function readConfig() {
|
|
|
29646
29842
|
}
|
|
29647
29843
|
function writeConfig(config) {
|
|
29648
29844
|
const configDir = getConfigDir();
|
|
29649
|
-
if (!
|
|
29845
|
+
if (!existsSync12(configDir)) {
|
|
29650
29846
|
mkdirSync9(configDir, { recursive: true });
|
|
29651
29847
|
}
|
|
29652
29848
|
writeFileSync7(getConfigPath2(), JSON.stringify(config, null, 2) + `
|
|
@@ -30391,7 +30587,7 @@ init_database();
|
|
|
30391
30587
|
init_tasks();
|
|
30392
30588
|
init_config2();
|
|
30393
30589
|
init_redaction();
|
|
30394
|
-
import { existsSync as
|
|
30590
|
+
import { existsSync as existsSync13, readFileSync as readFileSync9 } from "fs";
|
|
30395
30591
|
var DEFAULT_RETRY = {
|
|
30396
30592
|
attempts: 1,
|
|
30397
30593
|
backoff_ms: 0
|
|
@@ -30554,7 +30750,7 @@ Timed out after ${provider.timeout_ms}ms`);
|
|
|
30554
30750
|
};
|
|
30555
30751
|
}
|
|
30556
30752
|
function runCiLogProvider(input) {
|
|
30557
|
-
const text = input.log_text ?? (input.log_path &&
|
|
30753
|
+
const text = input.log_text ?? (input.log_path && existsSync13(input.log_path) ? readFileSync9(input.log_path, "utf-8") : "");
|
|
30558
30754
|
return {
|
|
30559
30755
|
status: classifyLog(text),
|
|
30560
30756
|
attempts: 1,
|
|
@@ -30566,7 +30762,7 @@ function runBrowserProvider(input) {
|
|
|
30566
30762
|
if (!input.artifact_path) {
|
|
30567
30763
|
return { status: "unknown", attempts: 1, exit_code: null, output_summary: "browser provider needs a screenshot or artifact path" };
|
|
30568
30764
|
}
|
|
30569
|
-
if (!
|
|
30765
|
+
if (!existsSync13(input.artifact_path)) {
|
|
30570
30766
|
return { status: "failed", attempts: 1, exit_code: null, output_summary: `artifact not found: ${input.artifact_path}` };
|
|
30571
30767
|
}
|
|
30572
30768
|
return {
|
|
@@ -30731,7 +30927,7 @@ function listVerificationRecords(filter = {}, db) {
|
|
|
30731
30927
|
// src/lib/verification-evidence.ts
|
|
30732
30928
|
init_database();
|
|
30733
30929
|
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync10 } from "fs";
|
|
30734
|
-
import { dirname as
|
|
30930
|
+
import { dirname as dirname8 } from "path";
|
|
30735
30931
|
var VERIFICATION_EVIDENCE_SCHEMA = "todos.verification_evidence.v1";
|
|
30736
30932
|
function getMachineId3() {
|
|
30737
30933
|
return process.env["TODOS_MACHINE_ID"] || __require("os").hostname();
|
|
@@ -30851,7 +31047,7 @@ function exportVerificationEvidence(filter = {}, db) {
|
|
|
30851
31047
|
};
|
|
30852
31048
|
}
|
|
30853
31049
|
function writeVerificationExport(bundle, path) {
|
|
30854
|
-
mkdirSync10(
|
|
31050
|
+
mkdirSync10(dirname8(path), { recursive: true });
|
|
30855
31051
|
writeFileSync8(path, JSON.stringify(bundle, null, 2), "utf8");
|
|
30856
31052
|
}
|
|
30857
31053
|
// src/lib/policy-packs.ts
|
|
@@ -31211,21 +31407,21 @@ function resourceDiagnostics() {
|
|
|
31211
31407
|
};
|
|
31212
31408
|
}
|
|
31213
31409
|
// src/lib/sandbox-profiles.ts
|
|
31214
|
-
import { existsSync as
|
|
31215
|
-
import { join as
|
|
31410
|
+
import { existsSync as existsSync14, readFileSync as readFileSync10, writeFileSync as writeFileSync9, mkdirSync as mkdirSync11 } from "fs";
|
|
31411
|
+
import { join as join12, dirname as dirname9 } from "path";
|
|
31216
31412
|
var SANDBOX_PROFILE_VERSION = "todos.sandbox-profile.v1";
|
|
31217
31413
|
function getProfilesPath() {
|
|
31218
31414
|
if (process.env["TODOS_SANDBOX_PROFILES_PATH"]) {
|
|
31219
31415
|
return process.env["TODOS_SANDBOX_PROFILES_PATH"];
|
|
31220
31416
|
}
|
|
31221
|
-
const localDir =
|
|
31222
|
-
const local =
|
|
31223
|
-
if (
|
|
31417
|
+
const localDir = join12(process.cwd(), ".todos");
|
|
31418
|
+
const local = join12(localDir, "sandbox-profiles.json");
|
|
31419
|
+
if (existsSync14(localDir))
|
|
31224
31420
|
return local;
|
|
31225
|
-
if (
|
|
31421
|
+
if (existsSync14(local))
|
|
31226
31422
|
return local;
|
|
31227
31423
|
const home = process.env["HOME"] || "~";
|
|
31228
|
-
return
|
|
31424
|
+
return join12(home, ".hasna", "todos", "sandbox-profiles.json");
|
|
31229
31425
|
}
|
|
31230
31426
|
var cached2 = null;
|
|
31231
31427
|
function resetSandboxProfileCache() {
|
|
@@ -31257,11 +31453,11 @@ function loadSandboxProfiles() {
|
|
|
31257
31453
|
if (cached2)
|
|
31258
31454
|
return cached2;
|
|
31259
31455
|
const path = getProfilesPath();
|
|
31260
|
-
if (!
|
|
31456
|
+
if (!existsSync14(path)) {
|
|
31261
31457
|
cached2 = getDefaultSandboxProfiles();
|
|
31262
31458
|
return cached2;
|
|
31263
31459
|
}
|
|
31264
|
-
const parsed = JSON.parse(
|
|
31460
|
+
const parsed = JSON.parse(readFileSync10(path, "utf8"));
|
|
31265
31461
|
cached2 = parsed.profiles?.length ? parsed.profiles : getDefaultSandboxProfiles();
|
|
31266
31462
|
return cached2;
|
|
31267
31463
|
}
|
|
@@ -31270,7 +31466,7 @@ function getSandboxProfile(name) {
|
|
|
31270
31466
|
}
|
|
31271
31467
|
function saveSandboxProfiles(profiles) {
|
|
31272
31468
|
const path = getProfilesPath();
|
|
31273
|
-
mkdirSync11(
|
|
31469
|
+
mkdirSync11(dirname9(path), { recursive: true });
|
|
31274
31470
|
writeFileSync9(path, JSON.stringify({ schema_version: SANDBOX_PROFILE_VERSION, profiles }, null, 2));
|
|
31275
31471
|
cached2 = profiles;
|
|
31276
31472
|
}
|
|
@@ -31637,7 +31833,7 @@ init_task_commits();
|
|
|
31637
31833
|
|
|
31638
31834
|
// src/lib/git-traceability.ts
|
|
31639
31835
|
init_task_commits();
|
|
31640
|
-
import { existsSync as
|
|
31836
|
+
import { existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
|
|
31641
31837
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
31642
31838
|
import { resolve as resolve14 } from "path";
|
|
31643
31839
|
var GIT_TRACEABILITY_SCHEMA_VERSION = "todos.git_traceability.v1";
|
|
@@ -31683,10 +31879,10 @@ function inspectGitCommit(sha, cwd) {
|
|
|
31683
31879
|
}
|
|
31684
31880
|
function loadCiSnapshot(path) {
|
|
31685
31881
|
const target = path ? resolve14(path) : resolve14(process.cwd(), ".todos", "ci-snapshot.json");
|
|
31686
|
-
if (!
|
|
31882
|
+
if (!existsSync15(target))
|
|
31687
31883
|
return null;
|
|
31688
31884
|
try {
|
|
31689
|
-
const parsed = JSON.parse(
|
|
31885
|
+
const parsed = JSON.parse(readFileSync11(target, "utf8"));
|
|
31690
31886
|
return { ...parsed, captured_at: parsed.captured_at ?? new Date().toISOString() };
|
|
31691
31887
|
} catch {
|
|
31692
31888
|
return null;
|
|
@@ -31779,8 +31975,8 @@ function formatTraceabilityReport(report) {
|
|
|
31779
31975
|
`);
|
|
31780
31976
|
}
|
|
31781
31977
|
// src/lib/mention-resolver.ts
|
|
31782
|
-
import { existsSync as
|
|
31783
|
-
import { basename as basename4, isAbsolute, join as
|
|
31978
|
+
import { existsSync as existsSync16, readdirSync as readdirSync3, readFileSync as readFileSync12, statSync as statSync6 } from "fs";
|
|
31979
|
+
import { basename as basename4, isAbsolute, join as join13, relative as relative4, resolve as resolve15, sep as sep2 } from "path";
|
|
31784
31980
|
init_database();
|
|
31785
31981
|
init_plans();
|
|
31786
31982
|
init_task_runs();
|
|
@@ -31934,7 +32130,7 @@ function resolveFile(parsed, workspace) {
|
|
|
31934
32130
|
return resolution;
|
|
31935
32131
|
}
|
|
31936
32132
|
resolution.path = relPath;
|
|
31937
|
-
if (!
|
|
32133
|
+
if (!existsSync16(absolutePath)) {
|
|
31938
32134
|
resolution.warnings.push("file does not exist in the local workspace");
|
|
31939
32135
|
return resolution;
|
|
31940
32136
|
}
|
|
@@ -31944,7 +32140,7 @@ function resolveFile(parsed, workspace) {
|
|
|
31944
32140
|
return resolution;
|
|
31945
32141
|
}
|
|
31946
32142
|
if (parsed.line !== undefined) {
|
|
31947
|
-
const lineCount =
|
|
32143
|
+
const lineCount = readFileSync12(absolutePath, "utf-8").split(/\r?\n/).length;
|
|
31948
32144
|
if (parsed.line < 1 || parsed.line > lineCount) {
|
|
31949
32145
|
resolution.warnings.push(`line ${parsed.line} is outside the file range 1-${lineCount}`);
|
|
31950
32146
|
return resolution;
|
|
@@ -31967,7 +32163,7 @@ function walkSourceFiles(root, current = root, files = []) {
|
|
|
31967
32163
|
if (SKIP_DIRS.has(entry2.name))
|
|
31968
32164
|
continue;
|
|
31969
32165
|
}
|
|
31970
|
-
const absolutePath =
|
|
32166
|
+
const absolutePath = join13(current, entry2.name);
|
|
31971
32167
|
if (entry2.isDirectory()) {
|
|
31972
32168
|
if (!SKIP_DIRS.has(entry2.name))
|
|
31973
32169
|
walkSourceFiles(root, absolutePath, files);
|
|
@@ -31997,7 +32193,7 @@ function resolveSymbol(parsed, workspace, maxMatches) {
|
|
|
31997
32193
|
const pattern = symbolPattern(name);
|
|
31998
32194
|
const matches = [];
|
|
31999
32195
|
for (const file of walkSourceFiles(workspace)) {
|
|
32000
|
-
const lines =
|
|
32196
|
+
const lines = readFileSync12(file, "utf-8").split(/\r?\n/);
|
|
32001
32197
|
for (let index = 0;index < lines.length; index += 1) {
|
|
32002
32198
|
const line = lines[index];
|
|
32003
32199
|
const found = pattern.exec(line);
|
|
@@ -33865,7 +34061,7 @@ function getAdapterDocsFingerprint() {
|
|
|
33865
34061
|
// src/lib/inbox-intake.ts
|
|
33866
34062
|
init_database();
|
|
33867
34063
|
init_tasks();
|
|
33868
|
-
import { existsSync as
|
|
34064
|
+
import { existsSync as existsSync17, readFileSync as readFileSync13 } from "fs";
|
|
33869
34065
|
import { basename as basename5 } from "path";
|
|
33870
34066
|
import { createHash as createHash12 } from "crypto";
|
|
33871
34067
|
init_secret_redaction();
|
|
@@ -33911,9 +34107,9 @@ function loadRawContent(input) {
|
|
|
33911
34107
|
}
|
|
33912
34108
|
}
|
|
33913
34109
|
if (input.file_path) {
|
|
33914
|
-
if (!
|
|
34110
|
+
if (!existsSync17(input.file_path))
|
|
33915
34111
|
throw new Error(`File not found: ${input.file_path}`);
|
|
33916
|
-
const raw =
|
|
34112
|
+
const raw = readFileSync13(input.file_path, "utf8");
|
|
33917
34113
|
const name = basename5(input.file_path).toLowerCase();
|
|
33918
34114
|
const source_type2 = input.source_type ?? (name.includes("ci") || name.endsWith(".log") ? "ci_log" : "file");
|
|
33919
34115
|
return {
|
|
@@ -34532,7 +34728,7 @@ function formatNlIntakePreviewText(preview) {
|
|
|
34532
34728
|
// src/lib/issue-importers.ts
|
|
34533
34729
|
init_database();
|
|
34534
34730
|
init_tasks();
|
|
34535
|
-
import { existsSync as
|
|
34731
|
+
import { existsSync as existsSync18, readFileSync as readFileSync14 } from "fs";
|
|
34536
34732
|
var ISSUE_IMPORT_SCHEMA = "todos.issue_import.v1";
|
|
34537
34733
|
var ISSUE_SOURCES = ["github", "linear", "jira", "auto"];
|
|
34538
34734
|
var GITHUB_LABEL_PRIORITY = {
|
|
@@ -34751,9 +34947,9 @@ function parseIssueExport(data, source9 = "auto") {
|
|
|
34751
34947
|
return normalized;
|
|
34752
34948
|
}
|
|
34753
34949
|
function loadIssueExportFromFile(path) {
|
|
34754
|
-
if (!
|
|
34950
|
+
if (!existsSync18(path))
|
|
34755
34951
|
throw new Error(`File not found: ${path}`);
|
|
34756
|
-
return JSON.parse(
|
|
34952
|
+
return JSON.parse(readFileSync14(path, "utf8"));
|
|
34757
34953
|
}
|
|
34758
34954
|
function loadIssueExportInput(input) {
|
|
34759
34955
|
if (input.file_path) {
|
|
@@ -34911,8 +35107,8 @@ todos import issues ./linear.json --source linear --dry-run
|
|
|
34911
35107
|
// src/lib/run-records.ts
|
|
34912
35108
|
init_database();
|
|
34913
35109
|
init_secret_redaction();
|
|
34914
|
-
import { existsSync as
|
|
34915
|
-
import { join as
|
|
35110
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
35111
|
+
import { join as join14, dirname as dirname10 } from "path";
|
|
34916
35112
|
var RUN_RECORD_SCHEMA = "todos.run_record.v1";
|
|
34917
35113
|
var RUN_RECORD_STATUSES = ["active", "completed", "failed", "archived"];
|
|
34918
35114
|
function parseJsonArray3(raw, fallback = []) {
|
|
@@ -35105,8 +35301,8 @@ function buildRunReplayBundle(id, db) {
|
|
|
35105
35301
|
}
|
|
35106
35302
|
function exportRunReplay(id, outputPath, db) {
|
|
35107
35303
|
const bundle = buildRunReplayBundle(id, db);
|
|
35108
|
-
const path = outputPath ??
|
|
35109
|
-
mkdirSync12(
|
|
35304
|
+
const path = outputPath ?? join14(process.cwd(), ".todos", "replays", `${id.slice(0, 8)}.json`);
|
|
35305
|
+
mkdirSync12(dirname10(path), { recursive: true });
|
|
35110
35306
|
writeFileSync10(path, JSON.stringify(bundle, null, 2));
|
|
35111
35307
|
const d = db || getDatabase();
|
|
35112
35308
|
d.run(`UPDATE run_records SET replay_bundle = ?, updated_at = ? WHERE id = ?`, [path, now(), id]);
|
|
@@ -35145,16 +35341,16 @@ function formatRunRecordMarkdown(record) {
|
|
|
35145
35341
|
`;
|
|
35146
35342
|
}
|
|
35147
35343
|
function getDefaultReplayDir() {
|
|
35148
|
-
const local =
|
|
35149
|
-
if (
|
|
35344
|
+
const local = join14(process.cwd(), ".todos", "replays");
|
|
35345
|
+
if (existsSync19(join14(process.cwd(), ".todos")))
|
|
35150
35346
|
return local;
|
|
35151
35347
|
const home = process.env["HOME"] || "~";
|
|
35152
|
-
return
|
|
35348
|
+
return join14(home, ".hasna", "todos", "replays");
|
|
35153
35349
|
}
|
|
35154
35350
|
// src/lib/release-checks.ts
|
|
35155
35351
|
init_secret_redaction();
|
|
35156
|
-
import { existsSync as
|
|
35157
|
-
import { join as
|
|
35352
|
+
import { existsSync as existsSync20, readFileSync as readFileSync15, readdirSync as readdirSync4, statSync as statSync7 } from "fs";
|
|
35353
|
+
import { join as join15, relative as relative5 } from "path";
|
|
35158
35354
|
var RELEASE_CHECK_SCHEMA = "todos.release_check.v1";
|
|
35159
35355
|
var FORBIDDEN_DIST_PATTERNS = [
|
|
35160
35356
|
{
|
|
@@ -35167,16 +35363,16 @@ var FORBIDDEN_DIST_PATTERNS = [
|
|
|
35167
35363
|
];
|
|
35168
35364
|
var REQUIRED_BINS = ["todos", "todos-mcp", "todos-serve"];
|
|
35169
35365
|
function readPackageJson3(root) {
|
|
35170
|
-
const path =
|
|
35171
|
-
if (!
|
|
35366
|
+
const path = join15(root, "package.json");
|
|
35367
|
+
if (!existsSync20(path))
|
|
35172
35368
|
throw new Error(`package.json not found in ${root}`);
|
|
35173
|
-
return JSON.parse(
|
|
35369
|
+
return JSON.parse(readFileSync15(path, "utf8"));
|
|
35174
35370
|
}
|
|
35175
35371
|
function walkFiles(dir, acc = []) {
|
|
35176
|
-
if (!
|
|
35372
|
+
if (!existsSync20(dir))
|
|
35177
35373
|
return acc;
|
|
35178
35374
|
for (const entry2 of readdirSync4(dir)) {
|
|
35179
|
-
const full =
|
|
35375
|
+
const full = join15(dir, entry2);
|
|
35180
35376
|
const st = statSync7(full);
|
|
35181
35377
|
if (st.isDirectory())
|
|
35182
35378
|
walkFiles(full, acc);
|
|
@@ -35194,8 +35390,8 @@ function auditPackageContents(root) {
|
|
|
35194
35390
|
checks.push({ id: "files_dist", severity: "error", message: "package.json files must include dist" });
|
|
35195
35391
|
}
|
|
35196
35392
|
for (const pattern of files) {
|
|
35197
|
-
const target =
|
|
35198
|
-
if (!
|
|
35393
|
+
const target = join15(root, pattern);
|
|
35394
|
+
if (!existsSync20(target)) {
|
|
35199
35395
|
checks.push({ id: `files_missing_${pattern}`, severity: "error", message: `Published file path missing: ${pattern}` });
|
|
35200
35396
|
}
|
|
35201
35397
|
}
|
|
@@ -35209,8 +35405,8 @@ function auditPackageContents(root) {
|
|
|
35209
35405
|
checks.push({ id: `bin_${name}`, severity: "error", message: `Missing bin entry: ${name}` });
|
|
35210
35406
|
continue;
|
|
35211
35407
|
}
|
|
35212
|
-
const binPath =
|
|
35213
|
-
if (!
|
|
35408
|
+
const binPath = join15(root, rel);
|
|
35409
|
+
if (!existsSync20(binPath)) {
|
|
35214
35410
|
checks.push({ id: `bin_path_${name}`, severity: "error", message: `Bin file missing: ${rel}` });
|
|
35215
35411
|
} else {
|
|
35216
35412
|
checks.push({ id: `bin_ok_${name}`, severity: "info", message: `Bin present: ${name} \u2192 ${rel}` });
|
|
@@ -35227,8 +35423,8 @@ function auditPackageContents(root) {
|
|
|
35227
35423
|
}
|
|
35228
35424
|
function scanDistArtifacts(root) {
|
|
35229
35425
|
const checks = [];
|
|
35230
|
-
const distDir =
|
|
35231
|
-
if (!
|
|
35426
|
+
const distDir = join15(root, "dist");
|
|
35427
|
+
if (!existsSync20(distDir)) {
|
|
35232
35428
|
checks.push({ id: "dist_missing", severity: "error", message: "dist/ directory not found \u2014 run bun run build" });
|
|
35233
35429
|
return checks;
|
|
35234
35430
|
}
|
|
@@ -35236,7 +35432,7 @@ function scanDistArtifacts(root) {
|
|
|
35236
35432
|
const rel = relative5(root, file);
|
|
35237
35433
|
let content;
|
|
35238
35434
|
try {
|
|
35239
|
-
content =
|
|
35435
|
+
content = readFileSync15(file, "utf8");
|
|
35240
35436
|
} catch {
|
|
35241
35437
|
continue;
|
|
35242
35438
|
}
|
|
@@ -35538,8 +35734,8 @@ function renderReleaseNotesMarkdown(document) {
|
|
|
35538
35734
|
// src/lib/db-backup.ts
|
|
35539
35735
|
init_database();
|
|
35540
35736
|
init_migrations();
|
|
35541
|
-
import { existsSync as
|
|
35542
|
-
import { dirname as
|
|
35737
|
+
import { existsSync as existsSync21, copyFileSync, mkdirSync as mkdirSync13, readFileSync as readFileSync16, renameSync, statSync as statSync8, writeFileSync as writeFileSync11, unlinkSync } from "fs";
|
|
35738
|
+
import { dirname as dirname11, join as join16, resolve as resolve16 } from "path";
|
|
35543
35739
|
import { Database as Database4 } from "bun:sqlite";
|
|
35544
35740
|
var DB_BACKUP_SCHEMA = "todos.db_backup.v1";
|
|
35545
35741
|
function resolveDbPath(dbPath) {
|
|
@@ -35556,9 +35752,9 @@ function resolveDbPath(dbPath) {
|
|
|
35556
35752
|
}
|
|
35557
35753
|
function backupDatabase(outputPath, sourcePath) {
|
|
35558
35754
|
const source9 = resolveDbPath(sourcePath);
|
|
35559
|
-
if (!
|
|
35755
|
+
if (!existsSync21(source9))
|
|
35560
35756
|
throw new Error(`Database not found: ${source9}`);
|
|
35561
|
-
mkdirSync13(
|
|
35757
|
+
mkdirSync13(dirname11(outputPath), { recursive: true });
|
|
35562
35758
|
closeDatabase();
|
|
35563
35759
|
const src = new Database4(source9);
|
|
35564
35760
|
try {
|
|
@@ -35579,14 +35775,14 @@ function backupDatabase(outputPath, sourcePath) {
|
|
|
35579
35775
|
};
|
|
35580
35776
|
}
|
|
35581
35777
|
function restoreDatabase(backupPath, targetPath) {
|
|
35582
|
-
if (!
|
|
35778
|
+
if (!existsSync21(backupPath))
|
|
35583
35779
|
throw new Error(`Backup not found: ${backupPath}`);
|
|
35584
35780
|
const integrity = checkDatabaseIntegrity(backupPath);
|
|
35585
35781
|
if (!integrity.ok) {
|
|
35586
35782
|
throw new Error(`Backup failed integrity check: ${integrity.errors.join("; ")}`);
|
|
35587
35783
|
}
|
|
35588
35784
|
const target = targetPath ? resolve16(targetPath) : resolveDbPath();
|
|
35589
|
-
mkdirSync13(
|
|
35785
|
+
mkdirSync13(dirname11(target), { recursive: true });
|
|
35590
35786
|
closeDatabase();
|
|
35591
35787
|
const staging = `${target}.restore.tmp`;
|
|
35592
35788
|
try {
|
|
@@ -35595,7 +35791,7 @@ function restoreDatabase(backupPath, targetPath) {
|
|
|
35595
35791
|
copyFileSync(backupPath, staging);
|
|
35596
35792
|
for (const sidecar of [`${target}-wal`, `${target}-shm`]) {
|
|
35597
35793
|
try {
|
|
35598
|
-
if (
|
|
35794
|
+
if (existsSync21(sidecar))
|
|
35599
35795
|
unlinkSync(sidecar);
|
|
35600
35796
|
} catch {}
|
|
35601
35797
|
}
|
|
@@ -35612,7 +35808,7 @@ function restoreDatabase(backupPath, targetPath) {
|
|
|
35612
35808
|
function checkDatabaseIntegrity(dbPath) {
|
|
35613
35809
|
const path = dbPath ? resolve16(dbPath) : resolveDbPath();
|
|
35614
35810
|
const errors = [];
|
|
35615
|
-
if (!
|
|
35811
|
+
if (!existsSync21(path)) {
|
|
35616
35812
|
return {
|
|
35617
35813
|
schema_version: DB_BACKUP_SCHEMA,
|
|
35618
35814
|
path,
|
|
@@ -35709,16 +35905,16 @@ function migrationDryRun(dbPath) {
|
|
|
35709
35905
|
};
|
|
35710
35906
|
}
|
|
35711
35907
|
function defaultBackupPath(dbPath) {
|
|
35712
|
-
const base = dbPath ?
|
|
35908
|
+
const base = dbPath ? dirname11(resolve16(dbPath)) : dirname11(resolveDbPath());
|
|
35713
35909
|
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
35714
|
-
return
|
|
35910
|
+
return join16(base, "backups", `todos-${stamp}.db`);
|
|
35715
35911
|
}
|
|
35716
35912
|
function readBackupManifest(backupPath) {
|
|
35717
35913
|
const manifestPath = `${backupPath}.json`;
|
|
35718
|
-
if (!
|
|
35914
|
+
if (!existsSync21(manifestPath))
|
|
35719
35915
|
return null;
|
|
35720
35916
|
try {
|
|
35721
|
-
return JSON.parse(
|
|
35917
|
+
return JSON.parse(readFileSync16(manifestPath, "utf8"));
|
|
35722
35918
|
} catch {
|
|
35723
35919
|
return null;
|
|
35724
35920
|
}
|
|
@@ -35728,7 +35924,7 @@ function writeBackupManifest(backupPath, result) {
|
|
|
35728
35924
|
writeFileSyncSafe(manifestPath, JSON.stringify(result, null, 2));
|
|
35729
35925
|
}
|
|
35730
35926
|
function writeFileSyncSafe(path, content) {
|
|
35731
|
-
mkdirSync13(
|
|
35927
|
+
mkdirSync13(dirname11(path), { recursive: true });
|
|
35732
35928
|
writeFileSync11(path, content);
|
|
35733
35929
|
}
|
|
35734
35930
|
// src/lib/json-schemas.ts
|
|
@@ -36236,15 +36432,15 @@ ${SCHEMA_ENTITIES.map((e) => `- **${e}**: \`${JSON_SCHEMAS[e].schema_version}\``
|
|
|
36236
36432
|
}
|
|
36237
36433
|
function exportSchemasToDirectory(dir) {
|
|
36238
36434
|
const { mkdirSync: mkdirSync14, writeFileSync: writeFileSync12 } = __require("fs");
|
|
36239
|
-
const { join:
|
|
36435
|
+
const { join: join17 } = __require("path");
|
|
36240
36436
|
mkdirSync14(dir, { recursive: true });
|
|
36241
36437
|
const written = [];
|
|
36242
36438
|
for (const entity of SCHEMA_ENTITIES) {
|
|
36243
|
-
const path =
|
|
36439
|
+
const path = join17(dir, `${entity}.${JSON_SCHEMAS[entity].schema_version.replace(/\./g, "-")}.json`);
|
|
36244
36440
|
writeFileSync12(path, JSON.stringify(JSON_SCHEMAS[entity], null, 2));
|
|
36245
36441
|
written.push(path);
|
|
36246
36442
|
}
|
|
36247
|
-
const catalogPath =
|
|
36443
|
+
const catalogPath = join17(dir, "catalog.json");
|
|
36248
36444
|
writeFileSync12(catalogPath, JSON.stringify({
|
|
36249
36445
|
catalog_version: JSON_SCHEMA_CATALOG_VERSION,
|
|
36250
36446
|
semver: SCHEMA_SEMVER,
|
|
@@ -37159,8 +37355,8 @@ init_tasks();
|
|
|
37159
37355
|
init_plans();
|
|
37160
37356
|
init_comments();
|
|
37161
37357
|
init_templates();
|
|
37162
|
-
import { readFileSync as
|
|
37163
|
-
import { dirname as
|
|
37358
|
+
import { readFileSync as readFileSync17, writeFileSync as writeFileSync12, mkdirSync as mkdirSync14 } from "fs";
|
|
37359
|
+
import { dirname as dirname12 } from "path";
|
|
37164
37360
|
init_secret_redaction();
|
|
37165
37361
|
var BUNDLE_SCHEMA = "todos.bundle.v1";
|
|
37166
37362
|
var BUNDLE_TYPES = ["full_export", "tasks", "partial"];
|
|
@@ -37568,11 +37764,11 @@ function importBundle(bundle, options = {}, db) {
|
|
|
37568
37764
|
return result;
|
|
37569
37765
|
}
|
|
37570
37766
|
function writeBundleFile(bundle, path) {
|
|
37571
|
-
mkdirSync14(
|
|
37767
|
+
mkdirSync14(dirname12(path), { recursive: true });
|
|
37572
37768
|
writeFileSync12(path, JSON.stringify(bundle, null, 2), "utf8");
|
|
37573
37769
|
}
|
|
37574
37770
|
function readBundleFile(path) {
|
|
37575
|
-
const raw = JSON.parse(
|
|
37771
|
+
const raw = JSON.parse(readFileSync17(path, "utf8"));
|
|
37576
37772
|
const validation = validateBundle(raw);
|
|
37577
37773
|
if (!validation.valid)
|
|
37578
37774
|
throw new Error(`Invalid bundle file: ${validation.errors.join("; ")}`);
|
|
@@ -38059,7 +38255,7 @@ init_plans();
|
|
|
38059
38255
|
init_tasks();
|
|
38060
38256
|
init_comments();
|
|
38061
38257
|
import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync15 } from "fs";
|
|
38062
|
-
import { dirname as
|
|
38258
|
+
import { dirname as dirname13 } from "path";
|
|
38063
38259
|
init_task_commits();
|
|
38064
38260
|
var HANDOFF_PACKET_SCHEMA = "todos.handoff_packet.v1";
|
|
38065
38261
|
function summarizeTask2(t) {
|
|
@@ -38234,7 +38430,7 @@ function formatHandoffPacket(packet, format = "json") {
|
|
|
38234
38430
|
function exportHandoffPacket(input = {}, path, db) {
|
|
38235
38431
|
const packet = createHandoffPacket(input, db);
|
|
38236
38432
|
if (path) {
|
|
38237
|
-
mkdirSync15(
|
|
38433
|
+
mkdirSync15(dirname13(path), { recursive: true });
|
|
38238
38434
|
writeFileSync13(path, formatHandoffPacket(packet, "json"), "utf8");
|
|
38239
38435
|
}
|
|
38240
38436
|
return packet;
|
|
@@ -38848,7 +39044,7 @@ init_templates();
|
|
|
38848
39044
|
init_database();
|
|
38849
39045
|
init_templates();
|
|
38850
39046
|
import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
|
|
38851
|
-
import { join as
|
|
39047
|
+
import { join as join17 } from "path";
|
|
38852
39048
|
var BUILTIN_TEMPLATE_LIBRARY_VERSION = "2026-05-21";
|
|
38853
39049
|
var BUILTIN_TEMPLATE_LIBRARY_SOURCE = "bundled-local-template-library";
|
|
38854
39050
|
var TEMPLATE_LIBRARY_SCHEMA = "todos.template_library.v1";
|
|
@@ -39124,7 +39320,7 @@ function writeBuiltinTemplateFiles(directory) {
|
|
|
39124
39320
|
mkdirSync16(directory, { recursive: true });
|
|
39125
39321
|
const files = [];
|
|
39126
39322
|
for (const entry2 of exportBuiltinTemplateFiles()) {
|
|
39127
|
-
const path =
|
|
39323
|
+
const path = join17(directory, entry2.filename);
|
|
39128
39324
|
writeFileSync14(path, `${JSON.stringify(entry2.template, null, 2)}
|
|
39129
39325
|
`, "utf-8");
|
|
39130
39326
|
files.push(path);
|
|
@@ -39168,8 +39364,8 @@ function initBuiltinTemplates(db) {
|
|
|
39168
39364
|
}
|
|
39169
39365
|
// src/lib/template-library.ts
|
|
39170
39366
|
init_database();
|
|
39171
|
-
import { writeFileSync as writeFileSync15, readFileSync as
|
|
39172
|
-
import { dirname as
|
|
39367
|
+
import { writeFileSync as writeFileSync15, readFileSync as readFileSync18, mkdirSync as mkdirSync17 } from "fs";
|
|
39368
|
+
import { dirname as dirname14 } from "path";
|
|
39173
39369
|
init_templates();
|
|
39174
39370
|
function listTemplateLibrary(db) {
|
|
39175
39371
|
const d = db || getDatabase();
|
|
@@ -39212,7 +39408,7 @@ function exportTemplateLibraryCatalog(path, db) {
|
|
|
39212
39408
|
templates: listTemplateLibrary(db)
|
|
39213
39409
|
};
|
|
39214
39410
|
if (path) {
|
|
39215
|
-
mkdirSync17(
|
|
39411
|
+
mkdirSync17(dirname14(path), { recursive: true });
|
|
39216
39412
|
writeFileSync15(path, JSON.stringify(catalog, null, 2), "utf8");
|
|
39217
39413
|
}
|
|
39218
39414
|
return catalog;
|
|
@@ -39230,7 +39426,7 @@ function exportInstalledTemplate(name, db) {
|
|
|
39230
39426
|
}
|
|
39231
39427
|
function importTemplateFromFile(path, db) {
|
|
39232
39428
|
const d = db || getDatabase();
|
|
39233
|
-
const raw = JSON.parse(
|
|
39429
|
+
const raw = JSON.parse(readFileSync18(path, "utf8"));
|
|
39234
39430
|
const payload = raw.template ?? raw;
|
|
39235
39431
|
const created = importTemplate(payload, d);
|
|
39236
39432
|
return { id: created.id, name: created.name };
|
|
@@ -39416,9 +39612,9 @@ init_tasks();
|
|
|
39416
39612
|
init_redaction();
|
|
39417
39613
|
init_sync_utils();
|
|
39418
39614
|
import { createHash as createHash13 } from "crypto";
|
|
39419
|
-
import { existsSync as
|
|
39615
|
+
import { existsSync as existsSync22, readFileSync as readFileSync19, statSync as statSync9 } from "fs";
|
|
39420
39616
|
import { hostname as hostname3, platform, arch } from "os";
|
|
39421
|
-
import { dirname as
|
|
39617
|
+
import { dirname as dirname15, join as join18, resolve as resolve17 } from "path";
|
|
39422
39618
|
import { tmpdir as tmpdir3 } from "os";
|
|
39423
39619
|
var MANIFEST_FILES = ["package.json", "dashboard/package.json", "sdk/package.json"];
|
|
39424
39620
|
var LOCKFILES = ["bun.lock", "bun.lockb", "package-lock.json", "npm-shrinkwrap.json"];
|
|
@@ -39440,20 +39636,20 @@ function sha2565(value) {
|
|
|
39440
39636
|
return createHash13("sha256").update(value).digest("hex");
|
|
39441
39637
|
}
|
|
39442
39638
|
function fileRecord(root, relativePath) {
|
|
39443
|
-
const path =
|
|
39444
|
-
if (!
|
|
39639
|
+
const path = join18(root, relativePath);
|
|
39640
|
+
if (!existsSync22(path))
|
|
39445
39641
|
return null;
|
|
39446
39642
|
const stat = statSync9(path);
|
|
39447
39643
|
if (!stat.isFile())
|
|
39448
39644
|
return null;
|
|
39449
|
-
const content =
|
|
39645
|
+
const content = readFileSync19(path);
|
|
39450
39646
|
return { path: relativePath, sha256: sha2565(content), size_bytes: content.length };
|
|
39451
39647
|
}
|
|
39452
39648
|
function manifestRecord(root, relativePath) {
|
|
39453
39649
|
const base = fileRecord(root, relativePath);
|
|
39454
39650
|
if (!base)
|
|
39455
39651
|
return null;
|
|
39456
|
-
const parsed = readJsonFile(
|
|
39652
|
+
const parsed = readJsonFile(join18(root, relativePath));
|
|
39457
39653
|
if (!parsed)
|
|
39458
39654
|
return { ...base, redacted: {} };
|
|
39459
39655
|
const redacted = redactValue({
|
|
@@ -39548,8 +39744,8 @@ function commandEnv(env, includeValues) {
|
|
|
39548
39744
|
function defaultSnapshotDir() {
|
|
39549
39745
|
const dbPath = getDatabasePath();
|
|
39550
39746
|
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
39551
|
-
return
|
|
39552
|
-
return
|
|
39747
|
+
return join18(tmpdir3(), "hasna-todos", "environment-snapshots");
|
|
39748
|
+
return join18(dirname15(resolve17(dbPath)), "environment-snapshots");
|
|
39553
39749
|
}
|
|
39554
39750
|
function snapshotWithId(snapshot) {
|
|
39555
39751
|
const digest = sha2565(JSON.stringify(snapshot)).slice(0, 24);
|
|
@@ -39596,8 +39792,8 @@ function captureEnvironmentSnapshot(input = {}) {
|
|
|
39596
39792
|
});
|
|
39597
39793
|
}
|
|
39598
39794
|
function writeEnvironmentSnapshot(snapshot, outputPath) {
|
|
39599
|
-
const path = outputPath ? resolve17(outputPath) :
|
|
39600
|
-
ensureDir2(
|
|
39795
|
+
const path = outputPath ? resolve17(outputPath) : join18(defaultSnapshotDir(), `${snapshot.id}.json`);
|
|
39796
|
+
ensureDir2(dirname15(path));
|
|
39601
39797
|
writeJsonFile(path, snapshot);
|
|
39602
39798
|
return path;
|
|
39603
39799
|
}
|
|
@@ -39691,7 +39887,7 @@ init_projects();
|
|
|
39691
39887
|
init_plans();
|
|
39692
39888
|
import { createHash as createHash14 } from "crypto";
|
|
39693
39889
|
import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
|
|
39694
|
-
import { dirname as
|
|
39890
|
+
import { dirname as dirname16, join as join19 } from "path";
|
|
39695
39891
|
var DECISION_RECORD_SCHEMA = "todos.decision_record.v1";
|
|
39696
39892
|
var KNOWLEDGE_SNAPSHOT_SCHEMA = "todos.knowledge_snapshot.v1";
|
|
39697
39893
|
var DECISION_STATUSES = ["proposed", "accepted", "deprecated", "superseded", "rejected"];
|
|
@@ -39921,8 +40117,8 @@ function exportDecisionRecord(id, outputPath, format = "markdown", db) {
|
|
|
39921
40117
|
if (!record)
|
|
39922
40118
|
throw new Error(`Decision record not found: ${id}`);
|
|
39923
40119
|
const content = format === "markdown" ? formatDecisionRecordMarkdown(record) : JSON.stringify(record, null, 2);
|
|
39924
|
-
const path = outputPath ??
|
|
39925
|
-
mkdirSync18(
|
|
40120
|
+
const path = outputPath ?? join19(process.cwd(), ".todos", "decisions", `${record.short_ref}.${format === "markdown" ? "md" : "json"}`);
|
|
40121
|
+
mkdirSync18(dirname16(path), { recursive: true });
|
|
39926
40122
|
writeFileSync16(path, content, "utf8");
|
|
39927
40123
|
return { path, content };
|
|
39928
40124
|
}
|
|
@@ -40069,8 +40265,8 @@ function exportKnowledgeSnapshot(id, outputPath, format = "markdown", db) {
|
|
|
40069
40265
|
throw new Error(`Knowledge snapshot not found: ${id}`);
|
|
40070
40266
|
const content = format === "markdown" ? formatKnowledgeSnapshotMarkdown(record) : JSON.stringify(record, null, 2);
|
|
40071
40267
|
const slug = record.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
40072
|
-
const path = outputPath ??
|
|
40073
|
-
mkdirSync18(
|
|
40268
|
+
const path = outputPath ?? join19(process.cwd(), ".todos", "knowledge", `${slug || record.id.slice(0, 8)}.${format === "markdown" ? "md" : "json"}`);
|
|
40269
|
+
mkdirSync18(dirname16(path), { recursive: true });
|
|
40074
40270
|
writeFileSync16(path, content, "utf8");
|
|
40075
40271
|
return { path, content };
|
|
40076
40272
|
}
|
|
@@ -40103,7 +40299,7 @@ init_tasks();
|
|
|
40103
40299
|
init_task_status();
|
|
40104
40300
|
init_secret_redaction();
|
|
40105
40301
|
import { writeFileSync as writeFileSync17, mkdirSync as mkdirSync19 } from "fs";
|
|
40106
|
-
import { dirname as
|
|
40302
|
+
import { dirname as dirname17 } from "path";
|
|
40107
40303
|
var REPORT_EXPORT_SCHEMA = "todos.report_export.v1";
|
|
40108
40304
|
var REPORT_KINDS = ["project", "plan", "run", "evidence", "roadmap", "retrospective"];
|
|
40109
40305
|
var REPORT_FORMATS = ["markdown", "html"];
|
|
@@ -40334,7 +40530,7 @@ function formatReportExport(data, format) {
|
|
|
40334
40530
|
return format === "html" ? formatReportHtml(data) : formatReportMarkdown(data);
|
|
40335
40531
|
}
|
|
40336
40532
|
function writeReportExport(data, format, path) {
|
|
40337
|
-
mkdirSync19(
|
|
40533
|
+
mkdirSync19(dirname17(path), { recursive: true });
|
|
40338
40534
|
writeFileSync17(path, formatReportExport(data, format), "utf8");
|
|
40339
40535
|
}
|
|
40340
40536
|
function exportReport(input, db) {
|
|
@@ -40368,8 +40564,8 @@ todos report export --kind retrospective --days 14 --format markdown --out retro
|
|
|
40368
40564
|
`;
|
|
40369
40565
|
}
|
|
40370
40566
|
// src/lib/command-aliases.ts
|
|
40371
|
-
import { existsSync as
|
|
40372
|
-
import { join as
|
|
40567
|
+
import { existsSync as existsSync23, readFileSync as readFileSync20, writeFileSync as writeFileSync18, mkdirSync as mkdirSync20 } from "fs";
|
|
40568
|
+
import { join as join20 } from "path";
|
|
40373
40569
|
var COMMAND_ALIASES_SCHEMA = "todos.command_aliases.v1";
|
|
40374
40570
|
var RESERVED = new Set([...listTopLevelCommands(), "help", "version", "alias", "shortcuts"]);
|
|
40375
40571
|
var BUILTIN_SHORTCUTS = [
|
|
@@ -40388,7 +40584,7 @@ var BUILTIN_SHORTCUTS = [
|
|
|
40388
40584
|
{ pattern: /^reports?$/, argv: ["report", "docs"], explain: "Report export documentation" }
|
|
40389
40585
|
];
|
|
40390
40586
|
function aliasesPath(cwd = process.cwd()) {
|
|
40391
|
-
return
|
|
40587
|
+
return join20(cwd, ".todos", "aliases.json");
|
|
40392
40588
|
}
|
|
40393
40589
|
function emptyStore() {
|
|
40394
40590
|
return { schema_version: COMMAND_ALIASES_SCHEMA, aliases: {}, updated_at: new Date(0).toISOString() };
|
|
@@ -40405,9 +40601,9 @@ function validateAliasName(name) {
|
|
|
40405
40601
|
}
|
|
40406
40602
|
function loadAliasStore(cwd) {
|
|
40407
40603
|
const path = aliasesPath(cwd);
|
|
40408
|
-
if (!
|
|
40604
|
+
if (!existsSync23(path))
|
|
40409
40605
|
return emptyStore();
|
|
40410
|
-
const parsed = JSON.parse(
|
|
40606
|
+
const parsed = JSON.parse(readFileSync20(path, "utf8"));
|
|
40411
40607
|
if (parsed.schema_version !== COMMAND_ALIASES_SCHEMA) {
|
|
40412
40608
|
throw new Error(`Unsupported alias store schema: ${parsed.schema_version}`);
|
|
40413
40609
|
}
|
|
@@ -40415,7 +40611,7 @@ function loadAliasStore(cwd) {
|
|
|
40415
40611
|
}
|
|
40416
40612
|
function saveAliasStore(store, cwd) {
|
|
40417
40613
|
const path = aliasesPath(cwd);
|
|
40418
|
-
mkdirSync20(
|
|
40614
|
+
mkdirSync20(join20(path, ".."), { recursive: true });
|
|
40419
40615
|
store.updated_at = new Date().toISOString();
|
|
40420
40616
|
writeFileSync18(path, JSON.stringify(store, null, 2), "utf8");
|
|
40421
40617
|
}
|
|
@@ -41070,18 +41266,18 @@ function createBranchWorkPlan(input, db) {
|
|
|
41070
41266
|
init_database();
|
|
41071
41267
|
init_templates();
|
|
41072
41268
|
init_plans();
|
|
41073
|
-
import { existsSync as
|
|
41074
|
-
import { join as
|
|
41269
|
+
import { existsSync as existsSync24, readFileSync as readFileSync21, writeFileSync as writeFileSync19, mkdirSync as mkdirSync21 } from "fs";
|
|
41270
|
+
import { join as join21 } from "path";
|
|
41075
41271
|
var USER_SCAFFOLD_SCHEMA = "todos.user_scaffold.v1";
|
|
41076
41272
|
var SCAFFOLD_KINDS = ["task", "project", "plan", "checklist", "contract", "verification_policy"];
|
|
41077
41273
|
function storeDir(cwd = process.cwd()) {
|
|
41078
|
-
return
|
|
41274
|
+
return join21(cwd, ".todos", "scaffolds");
|
|
41079
41275
|
}
|
|
41080
41276
|
function storePath(cwd) {
|
|
41081
|
-
return
|
|
41277
|
+
return join21(storeDir(cwd), "store.json");
|
|
41082
41278
|
}
|
|
41083
41279
|
function versionsDir(cwd) {
|
|
41084
|
-
return
|
|
41280
|
+
return join21(storeDir(cwd), "versions");
|
|
41085
41281
|
}
|
|
41086
41282
|
function slugify4(name) {
|
|
41087
41283
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -41091,9 +41287,9 @@ function emptyStore2() {
|
|
|
41091
41287
|
}
|
|
41092
41288
|
function loadUserScaffoldStore(cwd) {
|
|
41093
41289
|
const path = storePath(cwd);
|
|
41094
|
-
if (!
|
|
41290
|
+
if (!existsSync24(path))
|
|
41095
41291
|
return emptyStore2();
|
|
41096
|
-
const parsed = JSON.parse(
|
|
41292
|
+
const parsed = JSON.parse(readFileSync21(path, "utf8"));
|
|
41097
41293
|
if (parsed.schema_version !== USER_SCAFFOLD_SCHEMA) {
|
|
41098
41294
|
throw new Error(`Unsupported scaffold store schema: ${parsed.schema_version}`);
|
|
41099
41295
|
}
|
|
@@ -41106,7 +41302,7 @@ function saveUserScaffoldStore(store, cwd) {
|
|
|
41106
41302
|
}
|
|
41107
41303
|
function snapshotVersion(scaffold, cwd) {
|
|
41108
41304
|
mkdirSync21(versionsDir(cwd), { recursive: true });
|
|
41109
|
-
const path =
|
|
41305
|
+
const path = join21(versionsDir(cwd), `${scaffold.id}-v${scaffold.version}.json`);
|
|
41110
41306
|
writeFileSync19(path, JSON.stringify(scaffold, null, 2), "utf8");
|
|
41111
41307
|
}
|
|
41112
41308
|
function listUserScaffolds(kind, cwd) {
|
|
@@ -41353,7 +41549,7 @@ function listLinkedTemplates(db, cwd) {
|
|
|
41353
41549
|
// src/lib/agent-workflow-demo.ts
|
|
41354
41550
|
init_database();
|
|
41355
41551
|
import { mkdtempSync } from "fs";
|
|
41356
|
-
import { join as
|
|
41552
|
+
import { join as join22 } from "path";
|
|
41357
41553
|
import { tmpdir as tmpdir4 } from "os";
|
|
41358
41554
|
init_projects();
|
|
41359
41555
|
init_task_lists();
|
|
@@ -41374,7 +41570,7 @@ function setupEphemeralDemoDb(options = {}) {
|
|
|
41374
41570
|
if (options.db_path) {
|
|
41375
41571
|
db_path = options.db_path;
|
|
41376
41572
|
} else if (options.persist) {
|
|
41377
|
-
db_path =
|
|
41573
|
+
db_path = join22(mkdtempSync(join22(tmpdir4(), "todos-demo-")), "todos.db");
|
|
41378
41574
|
} else {
|
|
41379
41575
|
db_path = ":memory:";
|
|
41380
41576
|
}
|
|
@@ -43708,16 +43904,16 @@ function runSearchView(idOrName, db) {
|
|
|
43708
43904
|
init_tasks();
|
|
43709
43905
|
init_config2();
|
|
43710
43906
|
init_sync_utils();
|
|
43711
|
-
import { existsSync as
|
|
43712
|
-
import { join as
|
|
43907
|
+
import { existsSync as existsSync25, readFileSync as readFileSync22, readdirSync as readdirSync5, writeFileSync as writeFileSync20 } from "fs";
|
|
43908
|
+
import { join as join23 } from "path";
|
|
43713
43909
|
function getTaskListDir(taskListId) {
|
|
43714
|
-
return
|
|
43910
|
+
return join23(HOME, ".claude", "tasks", taskListId);
|
|
43715
43911
|
}
|
|
43716
43912
|
function readClaudeTask(dir, filename) {
|
|
43717
|
-
return readJsonFile(
|
|
43913
|
+
return readJsonFile(join23(dir, filename));
|
|
43718
43914
|
}
|
|
43719
43915
|
function writeClaudeTask(dir, task2) {
|
|
43720
|
-
writeJsonFile(
|
|
43916
|
+
writeJsonFile(join23(dir, `${task2.id}.json`), task2);
|
|
43721
43917
|
}
|
|
43722
43918
|
function toClaudeStatus(status) {
|
|
43723
43919
|
if (status === "pending" || status === "in_progress" || status === "completed") {
|
|
@@ -43729,14 +43925,14 @@ function toSqliteStatus(status) {
|
|
|
43729
43925
|
return status;
|
|
43730
43926
|
}
|
|
43731
43927
|
function readPrefixCounter(dir) {
|
|
43732
|
-
const path =
|
|
43733
|
-
if (!
|
|
43928
|
+
const path = join23(dir, ".prefix-counter");
|
|
43929
|
+
if (!existsSync25(path))
|
|
43734
43930
|
return 0;
|
|
43735
|
-
const val = parseInt(
|
|
43931
|
+
const val = parseInt(readFileSync22(path, "utf-8").trim(), 10);
|
|
43736
43932
|
return isNaN(val) ? 0 : val;
|
|
43737
43933
|
}
|
|
43738
43934
|
function writePrefixCounter(dir, value) {
|
|
43739
|
-
writeFileSync20(
|
|
43935
|
+
writeFileSync20(join23(dir, ".prefix-counter"), String(value));
|
|
43740
43936
|
}
|
|
43741
43937
|
function formatPrefixedSubject(title, prefix, counter) {
|
|
43742
43938
|
const padded = String(counter).padStart(5, "0");
|
|
@@ -43763,7 +43959,7 @@ function taskToClaudeTask(task2, claudeTaskId, existingMeta) {
|
|
|
43763
43959
|
}
|
|
43764
43960
|
function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
43765
43961
|
const dir = getTaskListDir(taskListId);
|
|
43766
|
-
if (!
|
|
43962
|
+
if (!existsSync25(dir))
|
|
43767
43963
|
ensureDir2(dir);
|
|
43768
43964
|
const filter = {};
|
|
43769
43965
|
if (projectId)
|
|
@@ -43772,7 +43968,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
43772
43968
|
const existingByTodosId = new Map;
|
|
43773
43969
|
const files = listJsonFiles(dir);
|
|
43774
43970
|
for (const f of files) {
|
|
43775
|
-
const path =
|
|
43971
|
+
const path = join23(dir, f);
|
|
43776
43972
|
const ct = readClaudeTask(dir, f);
|
|
43777
43973
|
if (ct?.metadata?.["todos_id"]) {
|
|
43778
43974
|
existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -43859,7 +44055,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
43859
44055
|
}
|
|
43860
44056
|
function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
|
|
43861
44057
|
const dir = getTaskListDir(taskListId);
|
|
43862
|
-
if (!
|
|
44058
|
+
if (!existsSync25(dir)) {
|
|
43863
44059
|
return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
|
|
43864
44060
|
}
|
|
43865
44061
|
const files = readdirSync5(dir).filter((f) => f.endsWith(".json"));
|
|
@@ -43879,7 +44075,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
43879
44075
|
}
|
|
43880
44076
|
for (const f of files) {
|
|
43881
44077
|
try {
|
|
43882
|
-
const filePath =
|
|
44078
|
+
const filePath = join23(dir, f);
|
|
43883
44079
|
const ct = readClaudeTask(dir, f);
|
|
43884
44080
|
if (!ct)
|
|
43885
44081
|
continue;
|
|
@@ -43950,20 +44146,20 @@ function syncClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
43950
44146
|
init_tasks();
|
|
43951
44147
|
init_sync_utils();
|
|
43952
44148
|
init_config2();
|
|
43953
|
-
import { existsSync as
|
|
43954
|
-
import { join as
|
|
44149
|
+
import { existsSync as existsSync26 } from "fs";
|
|
44150
|
+
import { join as join24 } from "path";
|
|
43955
44151
|
function agentBaseDir(agent) {
|
|
43956
44152
|
const key = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
|
|
43957
|
-
return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] ||
|
|
44153
|
+
return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join24(getTodosGlobalDir(), "agents");
|
|
43958
44154
|
}
|
|
43959
44155
|
function getTaskListDir2(agent, taskListId) {
|
|
43960
|
-
return
|
|
44156
|
+
return join24(agentBaseDir(agent), agent, taskListId);
|
|
43961
44157
|
}
|
|
43962
44158
|
function readAgentTask(dir, filename) {
|
|
43963
|
-
return readJsonFile(
|
|
44159
|
+
return readJsonFile(join24(dir, filename));
|
|
43964
44160
|
}
|
|
43965
44161
|
function writeAgentTask(dir, task2) {
|
|
43966
|
-
writeJsonFile(
|
|
44162
|
+
writeJsonFile(join24(dir, `${task2.id}.json`), task2);
|
|
43967
44163
|
}
|
|
43968
44164
|
function taskToAgentTask(task2, externalId, existingMeta) {
|
|
43969
44165
|
return {
|
|
@@ -43988,7 +44184,7 @@ function metadataKey(agent) {
|
|
|
43988
44184
|
}
|
|
43989
44185
|
function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
43990
44186
|
const dir = getTaskListDir2(agent, taskListId);
|
|
43991
|
-
if (!
|
|
44187
|
+
if (!existsSync26(dir))
|
|
43992
44188
|
ensureDir2(dir);
|
|
43993
44189
|
const filter = {};
|
|
43994
44190
|
if (projectId)
|
|
@@ -43997,7 +44193,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
43997
44193
|
const existingByTodosId = new Map;
|
|
43998
44194
|
const files = listJsonFiles(dir);
|
|
43999
44195
|
for (const f of files) {
|
|
44000
|
-
const path =
|
|
44196
|
+
const path = join24(dir, f);
|
|
44001
44197
|
const at = readAgentTask(dir, f);
|
|
44002
44198
|
if (at?.metadata?.["todos_id"]) {
|
|
44003
44199
|
existingByTodosId.set(at.metadata["todos_id"], { task: at, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -44071,7 +44267,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
44071
44267
|
}
|
|
44072
44268
|
function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
44073
44269
|
const dir = getTaskListDir2(agent, taskListId);
|
|
44074
|
-
if (!
|
|
44270
|
+
if (!existsSync26(dir)) {
|
|
44075
44271
|
return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
|
|
44076
44272
|
}
|
|
44077
44273
|
const files = listJsonFiles(dir);
|
|
@@ -44090,7 +44286,7 @@ function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
44090
44286
|
}
|
|
44091
44287
|
for (const f of files) {
|
|
44092
44288
|
try {
|
|
44093
|
-
const filePath =
|
|
44289
|
+
const filePath = join24(dir, f);
|
|
44094
44290
|
const at = readAgentTask(dir, f);
|
|
44095
44291
|
if (!at)
|
|
44096
44292
|
continue;
|
|
@@ -44230,9 +44426,9 @@ function syncWithAgents(agents, taskListIdByAgent, projectId, direction = "both"
|
|
|
44230
44426
|
// src/lib/extract.ts
|
|
44231
44427
|
init_tasks();
|
|
44232
44428
|
init_task_files();
|
|
44233
|
-
import { existsSync as
|
|
44429
|
+
import { existsSync as existsSync27, readFileSync as readFileSync23, statSync as statSync10 } from "fs";
|
|
44234
44430
|
import { createHash as createHash15 } from "crypto";
|
|
44235
|
-
import { relative as relative6, resolve as resolve18, join as
|
|
44431
|
+
import { relative as relative6, resolve as resolve18, join as join25 } from "path";
|
|
44236
44432
|
var EXTRACT_TAGS = ["TODO", "FIXME", "HACK", "XXX", "BUG", "NOTE"];
|
|
44237
44433
|
var DEFAULT_EXTENSIONS = new Set([
|
|
44238
44434
|
".ts",
|
|
@@ -44303,11 +44499,11 @@ function normalizePathForMatch(value) {
|
|
|
44303
44499
|
}
|
|
44304
44500
|
function readGitignorePatterns(basePath) {
|
|
44305
44501
|
const root = statSync10(basePath).isFile() ? resolve18(basePath, "..") : basePath;
|
|
44306
|
-
const gitignorePath =
|
|
44307
|
-
if (!
|
|
44502
|
+
const gitignorePath = join25(root, ".gitignore");
|
|
44503
|
+
if (!existsSync27(gitignorePath))
|
|
44308
44504
|
return [];
|
|
44309
44505
|
try {
|
|
44310
|
-
return
|
|
44506
|
+
return readFileSync23(gitignorePath, "utf-8").split(`
|
|
44311
44507
|
`).map((line) => line.trim()).filter((line) => line && !line.startsWith("#") && !line.startsWith("!"));
|
|
44312
44508
|
} catch {
|
|
44313
44509
|
return [];
|
|
@@ -44446,9 +44642,9 @@ function buildCodebaseIndex(options) {
|
|
|
44446
44642
|
const files = collectFiles(basePath, extensions, excludes, respectGitignore);
|
|
44447
44643
|
const indexed = [];
|
|
44448
44644
|
for (const file of files) {
|
|
44449
|
-
const fullPath = statSync10(basePath).isFile() ? basePath :
|
|
44645
|
+
const fullPath = statSync10(basePath).isFile() ? basePath : join25(basePath, file);
|
|
44450
44646
|
try {
|
|
44451
|
-
const source9 =
|
|
44647
|
+
const source9 = readFileSync23(fullPath, "utf-8");
|
|
44452
44648
|
const relPath = statSync10(basePath).isFile() ? relative6(resolve18(basePath, ".."), fullPath) : file;
|
|
44453
44649
|
indexed.push({
|
|
44454
44650
|
file: relPath,
|
|
@@ -44477,9 +44673,9 @@ function extractTodos(options, db) {
|
|
|
44477
44673
|
const files = collectFiles(basePath, extensions, excludes, respectGitignore);
|
|
44478
44674
|
const allComments = [];
|
|
44479
44675
|
for (const file of files) {
|
|
44480
|
-
const fullPath = statSync10(basePath).isFile() ? basePath :
|
|
44676
|
+
const fullPath = statSync10(basePath).isFile() ? basePath : join25(basePath, file);
|
|
44481
44677
|
try {
|
|
44482
|
-
const source9 =
|
|
44678
|
+
const source9 = readFileSync23(fullPath, "utf-8");
|
|
44483
44679
|
const relPath = statSync10(basePath).isFile() ? relative6(resolve18(basePath, ".."), fullPath) : file;
|
|
44484
44680
|
const comments = extractFromSource(source9, relPath, tags);
|
|
44485
44681
|
allComments.push(...comments);
|
|
@@ -45193,7 +45389,7 @@ function renderWorkflowStatesMarkdown(states = listWorkflowStates()) {
|
|
|
45193
45389
|
// src/lib/agent-replay-simulator.ts
|
|
45194
45390
|
init_redaction();
|
|
45195
45391
|
import { createHash as createHash16 } from "crypto";
|
|
45196
|
-
import { readFileSync as
|
|
45392
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
45197
45393
|
function isObject(value) {
|
|
45198
45394
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
45199
45395
|
}
|
|
@@ -45426,7 +45622,7 @@ function simulateAgentReplay(input, options = {}) {
|
|
|
45426
45622
|
};
|
|
45427
45623
|
}
|
|
45428
45624
|
function simulateAgentReplayFile(path, options = {}) {
|
|
45429
|
-
const parsed = JSON.parse(
|
|
45625
|
+
const parsed = JSON.parse(readFileSync24(path, "utf8"));
|
|
45430
45626
|
return simulateAgentReplay(parsed, options);
|
|
45431
45627
|
}
|
|
45432
45628
|
function renderAgentReplaySimulationMarkdown(simulation) {
|
|
@@ -45454,8 +45650,8 @@ function renderAgentReplaySimulationMarkdown(simulation) {
|
|
|
45454
45650
|
// src/lib/local-extensions.ts
|
|
45455
45651
|
init_config2();
|
|
45456
45652
|
import { createHash as createHash17, createVerify } from "crypto";
|
|
45457
|
-
import { existsSync as
|
|
45458
|
-
import { basename as basename6, join as
|
|
45653
|
+
import { existsSync as existsSync28, readdirSync as readdirSync6, readFileSync as readFileSync25, statSync as statSync11 } from "fs";
|
|
45654
|
+
import { basename as basename6, join as join26, resolve as resolve19 } from "path";
|
|
45459
45655
|
init_redaction();
|
|
45460
45656
|
init_runner_sandbox();
|
|
45461
45657
|
function isObject2(value) {
|
|
@@ -45538,7 +45734,7 @@ function normalizeManifest(input) {
|
|
|
45538
45734
|
};
|
|
45539
45735
|
}
|
|
45540
45736
|
function parseJson(path) {
|
|
45541
|
-
return JSON.parse(
|
|
45737
|
+
return JSON.parse(readFileSync25(path, "utf8"));
|
|
45542
45738
|
}
|
|
45543
45739
|
function sha2566(bytes) {
|
|
45544
45740
|
return `sha256:${createHash17("sha256").update(bytes).digest("hex")}`;
|
|
@@ -45737,13 +45933,13 @@ function verifyExtensionSignature(input) {
|
|
|
45737
45933
|
}
|
|
45738
45934
|
function inspectExtensionSource(source9) {
|
|
45739
45935
|
const resolved = resolve19(source9);
|
|
45740
|
-
if (!
|
|
45936
|
+
if (!existsSync28(resolved))
|
|
45741
45937
|
throw new Error(`extension source not found: ${source9}`);
|
|
45742
45938
|
const stat = statSync11(resolved);
|
|
45743
|
-
const manifestPath = stat.isDirectory() ? [
|
|
45939
|
+
const manifestPath = stat.isDirectory() ? [join26(resolved, "todos.extension.json"), join26(resolved, "extension.json")].find(existsSync28) : resolved;
|
|
45744
45940
|
if (!manifestPath)
|
|
45745
45941
|
throw new Error(`extension directory ${source9} is missing todos.extension.json`);
|
|
45746
|
-
const raw =
|
|
45942
|
+
const raw = readFileSync25(manifestPath);
|
|
45747
45943
|
const parsed = parseJson(manifestPath);
|
|
45748
45944
|
const bundle = isObject2(parsed) && isObject2(parsed["manifest"]);
|
|
45749
45945
|
const manifest = normalizeManifest(bundle ? parsed["manifest"] : parsed);
|
|
@@ -45836,20 +46032,20 @@ function projectExtensionSources(projectPath) {
|
|
|
45836
46032
|
return [];
|
|
45837
46033
|
const root = resolve19(projectPath);
|
|
45838
46034
|
const candidates = [
|
|
45839
|
-
|
|
45840
|
-
|
|
46035
|
+
join26(root, "todos.extension.json"),
|
|
46036
|
+
join26(root, ".todos", "todos.extension.json")
|
|
45841
46037
|
];
|
|
45842
|
-
const extensionDir =
|
|
45843
|
-
if (
|
|
46038
|
+
const extensionDir = join26(root, ".todos", "extensions");
|
|
46039
|
+
if (existsSync28(extensionDir)) {
|
|
45844
46040
|
for (const entry2 of readdirSync6(extensionDir)) {
|
|
45845
46041
|
if (entry2.startsWith("."))
|
|
45846
46042
|
continue;
|
|
45847
|
-
const full =
|
|
46043
|
+
const full = join26(extensionDir, entry2);
|
|
45848
46044
|
if (statSync11(full).isDirectory() || entry2.endsWith(".json"))
|
|
45849
46045
|
candidates.push(full);
|
|
45850
46046
|
}
|
|
45851
46047
|
}
|
|
45852
|
-
return candidates.filter(
|
|
46048
|
+
return candidates.filter(existsSync28);
|
|
45853
46049
|
}
|
|
45854
46050
|
function discoverLocalExtensions(options = {}) {
|
|
45855
46051
|
const config = loadConfig();
|
|
@@ -46151,6 +46347,11 @@ var IDLE_TMUX_COMMANDS = new Set([
|
|
|
46151
46347
|
"tmux",
|
|
46152
46348
|
"zsh"
|
|
46153
46349
|
]);
|
|
46350
|
+
function ensureTmuxAvailable() {
|
|
46351
|
+
if (!Bun.which("tmux", { PATH: process.env.PATH ?? "" })) {
|
|
46352
|
+
throw new Error("tmux is not installed or not in PATH");
|
|
46353
|
+
}
|
|
46354
|
+
}
|
|
46154
46355
|
function parseTmuxTarget(spec) {
|
|
46155
46356
|
if (!spec || spec.trim() === "") {
|
|
46156
46357
|
throw new Error("tmux target spec cannot be empty");
|
|
@@ -46192,6 +46393,7 @@ function formatTmuxTarget(target) {
|
|
|
46192
46393
|
async function validateTmuxTarget(spec) {
|
|
46193
46394
|
const target = parseTmuxTarget(spec);
|
|
46194
46395
|
const targetStr = formatTmuxTarget(target);
|
|
46396
|
+
ensureTmuxAvailable();
|
|
46195
46397
|
let proc;
|
|
46196
46398
|
try {
|
|
46197
46399
|
proc = Bun.spawn(["tmux", "list-panes", "-t", targetStr], {
|
|
@@ -46208,6 +46410,7 @@ async function validateTmuxTarget(spec) {
|
|
|
46208
46410
|
}
|
|
46209
46411
|
}
|
|
46210
46412
|
async function inspectTmuxPane(target) {
|
|
46413
|
+
ensureTmuxAvailable();
|
|
46211
46414
|
const proc = Bun.spawn([
|
|
46212
46415
|
"tmux",
|
|
46213
46416
|
"display-message",
|
|
@@ -46267,6 +46470,7 @@ async function sendToTmux(target, message, delayMs, options = false) {
|
|
|
46267
46470
|
console.log(`[dry-run] message: ${message.slice(0, 200)}`);
|
|
46268
46471
|
return;
|
|
46269
46472
|
}
|
|
46473
|
+
ensureTmuxAvailable();
|
|
46270
46474
|
if (!opts.confirmBusy) {
|
|
46271
46475
|
const pane = await inspectTmuxPane(target);
|
|
46272
46476
|
const status = tmuxPaneBusyStatus(pane);
|
|
@@ -46721,7 +46925,7 @@ init_redaction();
|
|
|
46721
46925
|
// src/lib/retention-cleanup.ts
|
|
46722
46926
|
init_artifact_store();
|
|
46723
46927
|
init_database();
|
|
46724
|
-
import { existsSync as
|
|
46928
|
+
import { existsSync as existsSync29, unlinkSync as unlinkSync2 } from "fs";
|
|
46725
46929
|
var RETENTION_CLEANUP_CONFIRMATION = "delete-local-retention-data";
|
|
46726
46930
|
var ALL_SCOPES = ["comments", "runs", "verifications", "expired_artifacts"];
|
|
46727
46931
|
var EMPTY_COUNTS = {
|
|
@@ -46932,7 +47136,7 @@ function applyRetentionCleanup(input, db) {
|
|
|
46932
47136
|
for (const artifact of report.candidates.artifact_files) {
|
|
46933
47137
|
try {
|
|
46934
47138
|
const path = artifactStorePath(artifact.relative_path);
|
|
46935
|
-
if (!
|
|
47139
|
+
if (!existsSync29(path)) {
|
|
46936
47140
|
report.warnings.push(`stored artifact already missing: ${artifact.relative_path}`);
|
|
46937
47141
|
continue;
|
|
46938
47142
|
}
|
|
@@ -47176,8 +47380,8 @@ init_database();
|
|
|
47176
47380
|
init_migrations();
|
|
47177
47381
|
init_schema();
|
|
47178
47382
|
init_recurrence();
|
|
47179
|
-
import { chmodSync, copyFileSync as copyFileSync2, existsSync as
|
|
47180
|
-
import { basename as basename7, dirname as
|
|
47383
|
+
import { chmodSync, copyFileSync as copyFileSync2, existsSync as existsSync30, mkdirSync as mkdirSync22, statSync as statSync12 } from "fs";
|
|
47384
|
+
import { basename as basename7, dirname as dirname18, join as join27 } from "path";
|
|
47181
47385
|
var REQUIRED_TABLES2 = [
|
|
47182
47386
|
"_migrations",
|
|
47183
47387
|
"projects",
|
|
@@ -47284,7 +47488,7 @@ function findMissingProjectRoots(db) {
|
|
|
47284
47488
|
continue;
|
|
47285
47489
|
if (!row.path.startsWith("/"))
|
|
47286
47490
|
continue;
|
|
47287
|
-
if (!
|
|
47491
|
+
if (!existsSync30(row.path))
|
|
47288
47492
|
missing++;
|
|
47289
47493
|
}
|
|
47290
47494
|
return missing;
|
|
@@ -47344,16 +47548,16 @@ function databasePermissionsAreUnsafe(dbPath) {
|
|
|
47344
47548
|
function createBackup(dbPath) {
|
|
47345
47549
|
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
47346
47550
|
return;
|
|
47347
|
-
if (!
|
|
47551
|
+
if (!existsSync30(dbPath))
|
|
47348
47552
|
return;
|
|
47349
47553
|
const stamp = now().replace(/[:.]/g, "-");
|
|
47350
|
-
const backupDir =
|
|
47554
|
+
const backupDir = join27(dirname18(dbPath), `${basename7(dbPath)}.backup-${stamp}`);
|
|
47351
47555
|
const files = [];
|
|
47352
47556
|
mkdirSync22(backupDir, { recursive: true });
|
|
47353
47557
|
for (const source9 of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
|
|
47354
|
-
if (!
|
|
47558
|
+
if (!existsSync30(source9))
|
|
47355
47559
|
continue;
|
|
47356
|
-
const target =
|
|
47560
|
+
const target = join27(backupDir, basename7(source9));
|
|
47357
47561
|
copyFileSync2(source9, target);
|
|
47358
47562
|
files.push(target);
|
|
47359
47563
|
}
|