@hasna/todos 0.11.90 → 0.11.93
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 +67 -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 +19499 -30656
- 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 +571 -314
- 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 +922 -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/cloud.d.ts +10 -0
- package/dist/server/cloud.d.ts.map +1 -1
- package/dist/server/index.js +2305 -8137
- 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/interfaces.d.ts +10 -0
- package/dist/storage/interfaces.d.ts.map +1 -1
- package/dist/storage/local-sqlite.d.ts.map +1 -1
- package/dist/storage/postgres-adapter.d.ts.map +1 -1
- package/dist/storage/postgres-sync.d.ts +22 -0
- package/dist/storage/postgres-sync.d.ts.map +1 -1
- package/dist/storage.js +166 -15
- 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.93",
|
|
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));
|
|
@@ -23389,6 +23488,27 @@ function clockColumnsForTable(table) {
|
|
|
23389
23488
|
}
|
|
23390
23489
|
|
|
23391
23490
|
// src/storage/local-sqlite.ts
|
|
23491
|
+
var TASK_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
23492
|
+
function resolveTaskRefLocal(db, ref) {
|
|
23493
|
+
const raw = ref.trim().toLowerCase();
|
|
23494
|
+
if (!raw)
|
|
23495
|
+
return null;
|
|
23496
|
+
if (TASK_UUID_RE.test(raw))
|
|
23497
|
+
return getTask(raw, db);
|
|
23498
|
+
const prefixRows = db.query("SELECT id FROM tasks WHERE LOWER(id) LIKE ? ESCAPE '\\' LIMIT 2").all(`${raw.replace(/[\\%_]/g, (c) => `\\${c}`)}%`);
|
|
23499
|
+
if (prefixRows.length > 1) {
|
|
23500
|
+
throw new Error(`Task reference is ambiguous: "${ref}"`);
|
|
23501
|
+
}
|
|
23502
|
+
if (prefixRows.length === 1)
|
|
23503
|
+
return getTask(prefixRows[0].id, db);
|
|
23504
|
+
const shortIdRows = db.query("SELECT id FROM tasks WHERE LOWER(short_id) = ? LIMIT 2").all(raw);
|
|
23505
|
+
if (shortIdRows.length > 1) {
|
|
23506
|
+
throw new Error(`Task reference is ambiguous: "${ref}"`);
|
|
23507
|
+
}
|
|
23508
|
+
if (shortIdRows.length === 1)
|
|
23509
|
+
return getTask(shortIdRows[0].id, db);
|
|
23510
|
+
return null;
|
|
23511
|
+
}
|
|
23392
23512
|
function createLocalSqliteTodosStorageAdapter(options = {}) {
|
|
23393
23513
|
const database = () => options.db ?? getDatabase();
|
|
23394
23514
|
let adapter;
|
|
@@ -23404,6 +23524,7 @@ function createLocalSqliteTodosStorageAdapter(options = {}) {
|
|
|
23404
23524
|
tasks: {
|
|
23405
23525
|
create: (input) => createTask(input, database()),
|
|
23406
23526
|
get: (id) => getTask(id, database()),
|
|
23527
|
+
resolveRef: (ref) => resolveTaskRefLocal(database(), ref),
|
|
23407
23528
|
list: (filter = {}) => listTasks(filter, database()),
|
|
23408
23529
|
count: (filter = {}) => countTasks(filter, database()),
|
|
23409
23530
|
update: (id, input) => updateTask(id, input, database()),
|
|
@@ -23634,7 +23755,6 @@ function postgresTodosCommentCursorIndexSql(tableName = DEFAULT_TODOS_POSTGRES_S
|
|
|
23634
23755
|
ON ${tableName} (service, (payload->>'task_id'), (payload->>'created_at'), object_id)
|
|
23635
23756
|
WHERE object_type = 'comments' AND deleted_at IS NULL`;
|
|
23636
23757
|
}
|
|
23637
|
-
|
|
23638
23758
|
class PostgresTodosSyncStore {
|
|
23639
23759
|
client;
|
|
23640
23760
|
service;
|
|
@@ -23921,6 +24041,7 @@ function createPostgresTodosStorageAdapter(options) {
|
|
|
23921
24041
|
tasks: {
|
|
23922
24042
|
create: (input, context) => createTask2(input, store, context),
|
|
23923
24043
|
get: (id) => store.get("tasks", id),
|
|
24044
|
+
resolveRef: (ref) => store.resolveTaskRef(ref),
|
|
23924
24045
|
list: (filter = {}) => store.listTasks(filter),
|
|
23925
24046
|
count: (filter = {}) => store.countTasks(filter),
|
|
23926
24047
|
update: (id, input) => updateTask2(id, input, store),
|
|
@@ -23970,7 +24091,7 @@ function createPostgresTodosStorageAdapter(options) {
|
|
|
23970
24091
|
get: (id) => store.get("plans", id),
|
|
23971
24092
|
list: async (projectId) => (await store.list("plans")).filter((plan) => projectId === undefined || plan.project_id === projectId).sort((a, b) => a.name.localeCompare(b.name)),
|
|
23972
24093
|
update: (id, input) => updatePlan2(id, input, store),
|
|
23973
|
-
delete: (id, context) => store.
|
|
24094
|
+
delete: (id, context) => store.deletePlan(id, context)
|
|
23974
24095
|
},
|
|
23975
24096
|
agents: {
|
|
23976
24097
|
register: (input, context) => registerAgent2(input, store, context),
|
|
@@ -24173,6 +24294,37 @@ class PostgresJsonRecordStore {
|
|
|
24173
24294
|
const row = result.rows[0];
|
|
24174
24295
|
return row ? payloadRecord2(row.payload) : null;
|
|
24175
24296
|
}
|
|
24297
|
+
async resolveTaskRef(ref) {
|
|
24298
|
+
await this.ensureSchema();
|
|
24299
|
+
const raw = ref.trim().toLowerCase();
|
|
24300
|
+
if (!raw)
|
|
24301
|
+
return null;
|
|
24302
|
+
const lastCode = raw.charCodeAt(raw.length - 1);
|
|
24303
|
+
if (lastCode < 65535) {
|
|
24304
|
+
const upper = raw.slice(0, -1) + String.fromCharCode(lastCode + 1);
|
|
24305
|
+
const prefixResult = await this.options.client.query(`/* todos:resolve-task-ref-prefix */ SELECT payload FROM ${this.tableName}
|
|
24306
|
+
WHERE service = $1 AND object_type = 'tasks' AND deleted_at IS NULL
|
|
24307
|
+
AND object_id COLLATE "C" >= $2 AND object_id COLLATE "C" < $3
|
|
24308
|
+
LIMIT 2`, [this.service, raw, upper]);
|
|
24309
|
+
if (prefixResult.rows.length > 1) {
|
|
24310
|
+
throw new Error(`Task reference is ambiguous: "${ref}"`);
|
|
24311
|
+
}
|
|
24312
|
+
if (prefixResult.rows.length === 1) {
|
|
24313
|
+
return payloadRecord2(prefixResult.rows[0].payload);
|
|
24314
|
+
}
|
|
24315
|
+
}
|
|
24316
|
+
const shortIdResult = await this.options.client.query(`/* todos:resolve-task-ref-short-id */ SELECT payload FROM ${this.tableName}
|
|
24317
|
+
WHERE service = $1 AND object_type = 'tasks' AND deleted_at IS NULL
|
|
24318
|
+
AND LOWER(payload->>'short_id') = $2
|
|
24319
|
+
LIMIT 2`, [this.service, raw]);
|
|
24320
|
+
if (shortIdResult.rows.length > 1) {
|
|
24321
|
+
throw new Error(`Task reference is ambiguous: "${ref}"`);
|
|
24322
|
+
}
|
|
24323
|
+
if (shortIdResult.rows.length === 1) {
|
|
24324
|
+
return payloadRecord2(shortIdResult.rows[0].payload);
|
|
24325
|
+
}
|
|
24326
|
+
return null;
|
|
24327
|
+
}
|
|
24176
24328
|
async countTasks(filter) {
|
|
24177
24329
|
await this.ensureSchema();
|
|
24178
24330
|
const { where, params } = this.buildTaskFilterSql(filter);
|
|
@@ -24254,6 +24406,62 @@ class PostgresJsonRecordStore {
|
|
|
24254
24406
|
}
|
|
24255
24407
|
return value;
|
|
24256
24408
|
}
|
|
24409
|
+
async completeTask(id, agentId, options) {
|
|
24410
|
+
await this.ensureSchema();
|
|
24411
|
+
const operationTimestamp = new Date().toISOString();
|
|
24412
|
+
const completedAt2 = options?.completed_at ?? operationTimestamp;
|
|
24413
|
+
const evidence = options ? {
|
|
24414
|
+
...options.files_changed !== undefined ? { files_changed: options.files_changed } : {},
|
|
24415
|
+
...options.test_results !== undefined ? { test_results: options.test_results } : {},
|
|
24416
|
+
...options.commit_hash !== undefined ? { commit_hash: options.commit_hash } : {},
|
|
24417
|
+
...options.notes !== undefined ? { notes: options.notes } : {},
|
|
24418
|
+
...options.attachment_ids !== undefined ? { attachment_ids: options.attachment_ids } : {}
|
|
24419
|
+
} : {};
|
|
24420
|
+
const hasEvidence = Object.keys(evidence).length > 0;
|
|
24421
|
+
const hasConfidence = options?.confidence !== undefined;
|
|
24422
|
+
const result = await this.options.client.query(`/* todos:complete-task-atomic */ UPDATE ${this.tableName}
|
|
24423
|
+
SET payload = payload || jsonb_build_object(
|
|
24424
|
+
'status', 'completed',
|
|
24425
|
+
'assigned_to', CASE
|
|
24426
|
+
WHEN jsonb_typeof(payload->'assigned_to') = 'string' THEN payload->'assigned_to'
|
|
24427
|
+
ELSE COALESCE(to_jsonb($3::text), 'null'::jsonb)
|
|
24428
|
+
END,
|
|
24429
|
+
'completed_at', $4::text,
|
|
24430
|
+
'updated_at', $9::text,
|
|
24431
|
+
'version', COALESCE((payload->>'version')::integer, 0) + 1,
|
|
24432
|
+
'metadata',
|
|
24433
|
+
(CASE WHEN jsonb_typeof(payload->'metadata') = 'object'
|
|
24434
|
+
THEN payload->'metadata' ELSE '{}'::jsonb END)
|
|
24435
|
+
|| CASE WHEN $5::boolean THEN jsonb_build_object(
|
|
24436
|
+
'_evidence',
|
|
24437
|
+
(CASE WHEN jsonb_typeof(payload->'metadata'->'_evidence') = 'object'
|
|
24438
|
+
THEN payload->'metadata'->'_evidence' ELSE '{}'::jsonb END) || $6::jsonb
|
|
24439
|
+
) ELSE '{}'::jsonb END
|
|
24440
|
+
|| CASE WHEN $7::boolean THEN jsonb_build_object(
|
|
24441
|
+
'_completion',
|
|
24442
|
+
(CASE WHEN jsonb_typeof(payload->'metadata'->'_completion') = 'object'
|
|
24443
|
+
THEN payload->'metadata'->'_completion' ELSE '{}'::jsonb END)
|
|
24444
|
+
|| jsonb_build_object('confidence', $8::double precision)
|
|
24445
|
+
) ELSE '{}'::jsonb END
|
|
24446
|
+
) || CASE WHEN $7::boolean
|
|
24447
|
+
THEN jsonb_build_object('confidence', $8::double precision)
|
|
24448
|
+
ELSE '{}'::jsonb END,
|
|
24449
|
+
updated_at = $9::timestamptz,
|
|
24450
|
+
version = COALESCE(version, 0) + 1
|
|
24451
|
+
WHERE service = $1 AND object_type = 'tasks' AND object_id = $2 AND deleted_at IS NULL
|
|
24452
|
+
RETURNING payload`, [
|
|
24453
|
+
this.service,
|
|
24454
|
+
id,
|
|
24455
|
+
agentId ?? null,
|
|
24456
|
+
completedAt2,
|
|
24457
|
+
hasEvidence,
|
|
24458
|
+
jsonbParam(evidence),
|
|
24459
|
+
hasConfidence,
|
|
24460
|
+
options?.confidence ?? null,
|
|
24461
|
+
operationTimestamp
|
|
24462
|
+
]);
|
|
24463
|
+
return result.rows[0] ? payloadRecord2(result.rows[0].payload) : null;
|
|
24464
|
+
}
|
|
24257
24465
|
async renameProject(id, newSlug, name, context = {}) {
|
|
24258
24466
|
await this.ensureSchema();
|
|
24259
24467
|
const normalizedSlug = slugifyRaw(newSlug);
|
|
@@ -24370,6 +24578,41 @@ class PostgresJsonRecordStore {
|
|
|
24370
24578
|
version: numberValue3(existing["version"])
|
|
24371
24579
|
}, context);
|
|
24372
24580
|
}
|
|
24581
|
+
async deletePlan(id, context = {}) {
|
|
24582
|
+
await this.ensureSchema();
|
|
24583
|
+
const timestamp2 = new Date().toISOString();
|
|
24584
|
+
const result = await this.options.client.query(`/* todos:delete-plan-atomic */ WITH target AS (
|
|
24585
|
+
SELECT payload FROM ${this.tableName}
|
|
24586
|
+
WHERE service = $1 AND object_type = 'plans' AND object_id = $2 AND deleted_at IS NULL
|
|
24587
|
+
FOR UPDATE
|
|
24588
|
+
), updated_tasks AS (
|
|
24589
|
+
UPDATE ${this.tableName} r SET
|
|
24590
|
+
payload = jsonb_set(
|
|
24591
|
+
jsonb_set(r.payload, '{plan_id}', 'null'::jsonb, true),
|
|
24592
|
+
'{version}',
|
|
24593
|
+
to_jsonb(COALESCE((r.payload->>'version')::int, 0) + 1),
|
|
24594
|
+
true
|
|
24595
|
+
) || jsonb_build_object('updated_at', $3::text),
|
|
24596
|
+
updated_at = $3::timestamptz,
|
|
24597
|
+
source_machine_id = COALESCE($4, r.source_machine_id),
|
|
24598
|
+
version = COALESCE(r.version, 0) + 1
|
|
24599
|
+
WHERE r.service = $1 AND r.object_type = 'tasks' AND r.deleted_at IS NULL
|
|
24600
|
+
AND r.payload->>'plan_id' = $2 AND EXISTS (SELECT 1 FROM target)
|
|
24601
|
+
RETURNING 1
|
|
24602
|
+
), deleted_plan AS (
|
|
24603
|
+
UPDATE ${this.tableName} r SET
|
|
24604
|
+
deleted_at = $3::timestamptz,
|
|
24605
|
+
updated_at = $3::timestamptz,
|
|
24606
|
+
source_machine_id = COALESCE($4, r.source_machine_id),
|
|
24607
|
+
version = COALESCE(r.version, 0) + 1
|
|
24608
|
+
FROM target
|
|
24609
|
+
WHERE r.service = $1 AND r.object_type = 'plans' AND r.object_id = $2 AND r.deleted_at IS NULL
|
|
24610
|
+
RETURNING 1
|
|
24611
|
+
) SELECT
|
|
24612
|
+
EXISTS (SELECT 1 FROM target) AS found,
|
|
24613
|
+
(SELECT count(*) FROM updated_tasks) AS tasks_updated`, [this.service, id, timestamp2, this.machineId(context)]);
|
|
24614
|
+
return Boolean(result.rows[0]?.found);
|
|
24615
|
+
}
|
|
24373
24616
|
async tombstone(tombstone, context = {}) {
|
|
24374
24617
|
await this.ensureSchema();
|
|
24375
24618
|
const deletedAt = stringValue2(tombstone.deleted_at) ?? new Date().toISOString();
|
|
@@ -24523,14 +24766,10 @@ async function startTask2(id, agentId, store) {
|
|
|
24523
24766
|
}, store);
|
|
24524
24767
|
}
|
|
24525
24768
|
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);
|
|
24769
|
+
const task2 = await store.completeTask(id, agentId, options);
|
|
24770
|
+
if (!task2)
|
|
24771
|
+
throw new Error(`tasks record not found: ${id}`);
|
|
24772
|
+
return task2;
|
|
24534
24773
|
}
|
|
24535
24774
|
async function failTask2(id, agentId, reason, options, store) {
|
|
24536
24775
|
const task2 = await requireRecord("tasks", id, store);
|
|
@@ -25104,8 +25343,9 @@ async function resolvePostgresPlanSlug(options) {
|
|
|
25104
25343
|
const used = new Set(plans.filter((plan) => plan.project_id === options.projectId && plan.id !== options.excludeId && plan.slug).map((plan) => plan.slug));
|
|
25105
25344
|
if (options.slug !== undefined) {
|
|
25106
25345
|
const slug = normalizePlanSlug2(options.slug);
|
|
25107
|
-
if (used.has(slug))
|
|
25108
|
-
throw new
|
|
25346
|
+
if (used.has(slug)) {
|
|
25347
|
+
throw new ResourceConflictError("PLAN_SLUG_CONFLICT", `Plan slug already exists in this scope: ${slug}`);
|
|
25348
|
+
}
|
|
25109
25349
|
return slug;
|
|
25110
25350
|
}
|
|
25111
25351
|
const base = planSlugBase4(options.name);
|
|
@@ -25143,13 +25383,22 @@ function numberValue3(value) {
|
|
|
25143
25383
|
return typeof value === "number" && Number.isSafeInteger(value) ? value : null;
|
|
25144
25384
|
}
|
|
25145
25385
|
function isPostgresUniqueViolation(error) {
|
|
25146
|
-
|
|
25386
|
+
if (typeof error !== "object" || error === null)
|
|
25387
|
+
return false;
|
|
25388
|
+
const candidate = error;
|
|
25389
|
+
const states = [candidate.code, candidate.errno, candidate.sqlState, candidate.sqlstate];
|
|
25390
|
+
if (typeof candidate.cause === "object" && candidate.cause !== null) {
|
|
25391
|
+
const cause = candidate.cause;
|
|
25392
|
+
states.push(cause.code, cause.errno, cause.sqlState, cause.sqlstate);
|
|
25393
|
+
}
|
|
25394
|
+
return states.some((state) => String(state) === "23505");
|
|
25147
25395
|
}
|
|
25148
25396
|
function postgresConstraintName(error) {
|
|
25149
25397
|
if (typeof error !== "object" || error === null)
|
|
25150
25398
|
return "";
|
|
25151
25399
|
const candidate = error;
|
|
25152
|
-
const
|
|
25400
|
+
const cause = typeof candidate.cause === "object" && candidate.cause !== null ? candidate.cause : undefined;
|
|
25401
|
+
const constraint = candidate.constraint ?? candidate.constraint_name ?? cause?.constraint ?? cause?.constraint_name;
|
|
25153
25402
|
return typeof constraint === "string" ? constraint : "";
|
|
25154
25403
|
}
|
|
25155
25404
|
|
|
@@ -27167,8 +27416,8 @@ init_task_crud();
|
|
|
27167
27416
|
init_redaction();
|
|
27168
27417
|
import { Database as Database3 } from "bun:sqlite";
|
|
27169
27418
|
import { createHash as createHash10 } from "crypto";
|
|
27170
|
-
import { existsSync as
|
|
27171
|
-
import { basename as basename2, dirname as
|
|
27419
|
+
import { existsSync as existsSync9, readdirSync as readdirSync2, statSync as statSync4 } from "fs";
|
|
27420
|
+
import { basename as basename2, dirname as dirname6, join as join9, resolve as resolve10 } from "path";
|
|
27172
27421
|
|
|
27173
27422
|
// src/lib/task-routing.ts
|
|
27174
27423
|
init_database();
|
|
@@ -27176,7 +27425,7 @@ init_projects();
|
|
|
27176
27425
|
init_task_crud();
|
|
27177
27426
|
init_task_lifecycle();
|
|
27178
27427
|
init_task_lists();
|
|
27179
|
-
import { existsSync as
|
|
27428
|
+
import { existsSync as existsSync8, statSync as statSync3 } from "fs";
|
|
27180
27429
|
var DEFAULT_STALE_IN_PROGRESS_MS = 3 * 24 * 60 * 60 * 1000;
|
|
27181
27430
|
var TERMINAL_WORKFLOW_STATES = new Set([
|
|
27182
27431
|
"failed",
|
|
@@ -27231,7 +27480,7 @@ function routeConcurrencyKey(task2, project, taskList, projectPath) {
|
|
|
27231
27480
|
}
|
|
27232
27481
|
function directoryExists(path) {
|
|
27233
27482
|
try {
|
|
27234
|
-
return
|
|
27483
|
+
return existsSync8(path) && statSync3(path).isDirectory();
|
|
27235
27484
|
} catch {
|
|
27236
27485
|
return false;
|
|
27237
27486
|
}
|
|
@@ -27432,7 +27681,7 @@ function pointerPatch(previous, input, key) {
|
|
|
27432
27681
|
|
|
27433
27682
|
// src/lib/task-route-sources.ts
|
|
27434
27683
|
var TASK_ROUTE_SOURCE_DISCOVERY_SCHEMA_VERSION = "todos.task_route_sources.v1";
|
|
27435
|
-
var TODO_STORE_RELATIVE_PATH =
|
|
27684
|
+
var TODO_STORE_RELATIVE_PATH = join9(".hasna", "todos", "todos.db");
|
|
27436
27685
|
var ROOT_SCAN_MAX_DEPTH = 5;
|
|
27437
27686
|
var SKIPPED_SCAN_DIRS = new Set([
|
|
27438
27687
|
".git",
|
|
@@ -27455,9 +27704,9 @@ function sourceStoreId(sourceDbPath) {
|
|
|
27455
27704
|
function inferSourceRepoPath(sourceDbPath) {
|
|
27456
27705
|
const normalized = normalizePath3(sourceDbPath);
|
|
27457
27706
|
if (normalized.endsWith(TODO_STORE_RELATIVE_PATH)) {
|
|
27458
|
-
return
|
|
27707
|
+
return dirname6(dirname6(dirname6(normalized)));
|
|
27459
27708
|
}
|
|
27460
|
-
return
|
|
27709
|
+
return dirname6(normalized);
|
|
27461
27710
|
}
|
|
27462
27711
|
function createStoreRef(sourceDbPath) {
|
|
27463
27712
|
const normalized = normalizePath3(sourceDbPath);
|
|
@@ -27508,8 +27757,8 @@ function discoverStoresUnderRoot(sourceRoot) {
|
|
|
27508
27757
|
const rootPath = normalizePath3(sourceRoot);
|
|
27509
27758
|
const errors = [];
|
|
27510
27759
|
const stores = [];
|
|
27511
|
-
if (!
|
|
27512
|
-
const ref = createStoreRef(
|
|
27760
|
+
if (!existsSync9(rootPath)) {
|
|
27761
|
+
const ref = createStoreRef(join9(rootPath, TODO_STORE_RELATIVE_PATH));
|
|
27513
27762
|
errors.push({
|
|
27514
27763
|
...ref,
|
|
27515
27764
|
code: "SOURCE_ROOT_MISSING",
|
|
@@ -27521,7 +27770,7 @@ function discoverStoresUnderRoot(sourceRoot) {
|
|
|
27521
27770
|
try {
|
|
27522
27771
|
rootStat = statSync4(rootPath);
|
|
27523
27772
|
} catch (error) {
|
|
27524
|
-
const ref = createStoreRef(
|
|
27773
|
+
const ref = createStoreRef(join9(rootPath, TODO_STORE_RELATIVE_PATH));
|
|
27525
27774
|
errors.push({
|
|
27526
27775
|
...ref,
|
|
27527
27776
|
code: "SOURCE_ROOT_UNREADABLE",
|
|
@@ -27534,8 +27783,8 @@ function discoverStoresUnderRoot(sourceRoot) {
|
|
|
27534
27783
|
return { stores, errors };
|
|
27535
27784
|
}
|
|
27536
27785
|
function scanDirectory(dir, depth) {
|
|
27537
|
-
const candidate =
|
|
27538
|
-
if (
|
|
27786
|
+
const candidate = join9(dir, TODO_STORE_RELATIVE_PATH);
|
|
27787
|
+
if (existsSync9(candidate)) {
|
|
27539
27788
|
stores.push(createStoreRef(candidate));
|
|
27540
27789
|
}
|
|
27541
27790
|
if (depth >= ROOT_SCAN_MAX_DEPTH)
|
|
@@ -27555,7 +27804,7 @@ function discoverStoresUnderRoot(sourceRoot) {
|
|
|
27555
27804
|
for (const entry2 of entries) {
|
|
27556
27805
|
if (!entry2.isDirectory() || SKIPPED_SCAN_DIRS.has(entry2.name))
|
|
27557
27806
|
continue;
|
|
27558
|
-
scanDirectory(
|
|
27807
|
+
scanDirectory(join9(dir, entry2.name), depth + 1);
|
|
27559
27808
|
}
|
|
27560
27809
|
}
|
|
27561
27810
|
scanDirectory(rootPath, 0);
|
|
@@ -27581,7 +27830,7 @@ function collectStoreRefs(input) {
|
|
|
27581
27830
|
};
|
|
27582
27831
|
}
|
|
27583
27832
|
function openReadonlyStore(ref) {
|
|
27584
|
-
if (!
|
|
27833
|
+
if (!existsSync9(ref.source_db_path)) {
|
|
27585
27834
|
throw Object.assign(new Error(`Store does not exist: ${ref.source_db_path}`), { code: "STORE_MISSING" });
|
|
27586
27835
|
}
|
|
27587
27836
|
return new Database3(ref.source_db_path, { readonly: true, create: false });
|
|
@@ -27892,8 +28141,8 @@ init_plans();
|
|
|
27892
28141
|
init_database();
|
|
27893
28142
|
init_projects();
|
|
27894
28143
|
init_tasks();
|
|
27895
|
-
import { existsSync as
|
|
27896
|
-
import { join as
|
|
28144
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync6 } from "fs";
|
|
28145
|
+
import { join as join10, resolve as resolve11 } from "path";
|
|
27897
28146
|
var PLAN_MARKDOWN_SCHEMA = "hasna.todos.plan/v1";
|
|
27898
28147
|
function assertSafePathSegment(value, label) {
|
|
27899
28148
|
const trimmed = value.trim();
|
|
@@ -27955,7 +28204,7 @@ function resolvePlanArtifactPaths(input) {
|
|
|
27955
28204
|
const project = resolvePlanArtifactProject(input);
|
|
27956
28205
|
const projectId = assertSafePathSegment(project.id, "project id");
|
|
27957
28206
|
const projectRoot = resolve11(project.path);
|
|
27958
|
-
const directory =
|
|
28207
|
+
const directory = join10(projectRoot, ".hasna", "todos", "plans", projectId);
|
|
27959
28208
|
const planId = input.plan_id ? assertSafePathSegment(input.plan_id, "plan id") : null;
|
|
27960
28209
|
const planSlug = input.plan_slug ? assertSafePathSegment(slugify(input.plan_slug), "plan slug") : null;
|
|
27961
28210
|
const fileName = planId ? planSlug ? `${planSlug}--${planId.slice(0, 8)}.md` : `${planId}.md` : null;
|
|
@@ -27963,7 +28212,7 @@ function resolvePlanArtifactPaths(input) {
|
|
|
27963
28212
|
project_id: project.id,
|
|
27964
28213
|
project_root: projectRoot,
|
|
27965
28214
|
directory,
|
|
27966
|
-
file_path: fileName ?
|
|
28215
|
+
file_path: fileName ? join10(directory, fileName) : directory
|
|
27967
28216
|
};
|
|
27968
28217
|
}
|
|
27969
28218
|
function resolvePlanArtifactCandidatePaths(plan, db) {
|
|
@@ -28129,10 +28378,10 @@ function readPlanArtifact(plan, db) {
|
|
|
28129
28378
|
return null;
|
|
28130
28379
|
const d = db || getDatabase();
|
|
28131
28380
|
const paths = resolvePlanArtifactCandidatePaths(plan, d);
|
|
28132
|
-
const path =
|
|
28381
|
+
const path = existsSync10(paths.primary.file_path) ? paths.primary.file_path : existsSync10(paths.legacy.file_path) ? paths.legacy.file_path : null;
|
|
28133
28382
|
if (!path)
|
|
28134
28383
|
return null;
|
|
28135
|
-
const markdown =
|
|
28384
|
+
const markdown = readFileSync6(path, "utf8");
|
|
28136
28385
|
return {
|
|
28137
28386
|
path,
|
|
28138
28387
|
markdown,
|
|
@@ -28144,7 +28393,7 @@ function inspectPlanArtifact(plan, db) {
|
|
|
28144
28393
|
return null;
|
|
28145
28394
|
const d = db || getDatabase();
|
|
28146
28395
|
const paths = resolvePlanArtifactCandidatePaths(plan, d);
|
|
28147
|
-
const path =
|
|
28396
|
+
const path = existsSync10(paths.primary.file_path) ? paths.primary.file_path : existsSync10(paths.legacy.file_path) ? paths.legacy.file_path : null;
|
|
28148
28397
|
if (!path) {
|
|
28149
28398
|
return {
|
|
28150
28399
|
path: paths.primary.file_path,
|
|
@@ -28156,7 +28405,7 @@ function inspectPlanArtifact(plan, db) {
|
|
|
28156
28405
|
};
|
|
28157
28406
|
}
|
|
28158
28407
|
try {
|
|
28159
|
-
const artifact = parsePlanArtifactMarkdown(
|
|
28408
|
+
const artifact = parsePlanArtifactMarkdown(readFileSync6(path, "utf8"));
|
|
28160
28409
|
return {
|
|
28161
28410
|
path,
|
|
28162
28411
|
exists: true,
|
|
@@ -29233,8 +29482,8 @@ function renderRetrospectiveMarkdown(record) {
|
|
|
29233
29482
|
init_database();
|
|
29234
29483
|
init_projects();
|
|
29235
29484
|
init_task_lists();
|
|
29236
|
-
import { existsSync as
|
|
29237
|
-
import { basename as basename3, dirname as
|
|
29485
|
+
import { existsSync as existsSync11, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
|
|
29486
|
+
import { basename as basename3, dirname as dirname7, resolve as resolve12 } from "path";
|
|
29238
29487
|
function safeStat(path) {
|
|
29239
29488
|
try {
|
|
29240
29489
|
return statSync5(path);
|
|
@@ -29246,15 +29495,15 @@ function canonicalPath(input) {
|
|
|
29246
29495
|
const resolved = resolve12(input);
|
|
29247
29496
|
const stats2 = safeStat(resolved);
|
|
29248
29497
|
if (stats2?.isFile())
|
|
29249
|
-
return
|
|
29498
|
+
return dirname7(resolved);
|
|
29250
29499
|
return resolved;
|
|
29251
29500
|
}
|
|
29252
29501
|
function findUp(start, marker) {
|
|
29253
29502
|
let current = canonicalPath(start);
|
|
29254
29503
|
while (true) {
|
|
29255
|
-
if (
|
|
29504
|
+
if (existsSync11(resolve12(current, marker)))
|
|
29256
29505
|
return current;
|
|
29257
|
-
const parent =
|
|
29506
|
+
const parent = dirname7(current);
|
|
29258
29507
|
if (parent === current)
|
|
29259
29508
|
return null;
|
|
29260
29509
|
current = parent;
|
|
@@ -29264,10 +29513,10 @@ function readPackageJson2(path) {
|
|
|
29264
29513
|
if (!path)
|
|
29265
29514
|
return null;
|
|
29266
29515
|
const file = resolve12(path, "package.json");
|
|
29267
|
-
if (!
|
|
29516
|
+
if (!existsSync11(file))
|
|
29268
29517
|
return null;
|
|
29269
29518
|
try {
|
|
29270
|
-
const parsed = JSON.parse(
|
|
29519
|
+
const parsed = JSON.parse(readFileSync7(file, "utf-8"));
|
|
29271
29520
|
return parsed && typeof parsed === "object" ? parsed : null;
|
|
29272
29521
|
} catch {
|
|
29273
29522
|
return null;
|
|
@@ -29286,7 +29535,7 @@ function workspaceMarker(root, rootPackage) {
|
|
|
29286
29535
|
if (rootPackage?.workspaces)
|
|
29287
29536
|
markers.push("package.json#workspaces");
|
|
29288
29537
|
for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
|
|
29289
|
-
if (
|
|
29538
|
+
if (existsSync11(resolve12(root, marker)))
|
|
29290
29539
|
markers.push(marker);
|
|
29291
29540
|
}
|
|
29292
29541
|
const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
|
|
@@ -29624,21 +29873,21 @@ var gatherTrainingData = async (options = {}) => {
|
|
|
29624
29873
|
};
|
|
29625
29874
|
// src/lib/model-config.ts
|
|
29626
29875
|
init_sync_utils();
|
|
29627
|
-
import { existsSync as
|
|
29628
|
-
import { join as
|
|
29876
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "fs";
|
|
29877
|
+
import { join as join11 } from "path";
|
|
29629
29878
|
var DEFAULT_MODEL = "gpt-4o-mini";
|
|
29630
29879
|
function getConfigDir() {
|
|
29631
29880
|
return getTodosGlobalDir();
|
|
29632
29881
|
}
|
|
29633
29882
|
function getConfigPath2() {
|
|
29634
|
-
return
|
|
29883
|
+
return join11(getConfigDir(), "config.json");
|
|
29635
29884
|
}
|
|
29636
29885
|
function readConfig() {
|
|
29637
29886
|
const configPath = getConfigPath2();
|
|
29638
|
-
if (!
|
|
29887
|
+
if (!existsSync12(configPath))
|
|
29639
29888
|
return {};
|
|
29640
29889
|
try {
|
|
29641
|
-
const raw =
|
|
29890
|
+
const raw = readFileSync8(configPath, "utf-8");
|
|
29642
29891
|
return JSON.parse(raw);
|
|
29643
29892
|
} catch {
|
|
29644
29893
|
return {};
|
|
@@ -29646,7 +29895,7 @@ function readConfig() {
|
|
|
29646
29895
|
}
|
|
29647
29896
|
function writeConfig(config) {
|
|
29648
29897
|
const configDir = getConfigDir();
|
|
29649
|
-
if (!
|
|
29898
|
+
if (!existsSync12(configDir)) {
|
|
29650
29899
|
mkdirSync9(configDir, { recursive: true });
|
|
29651
29900
|
}
|
|
29652
29901
|
writeFileSync7(getConfigPath2(), JSON.stringify(config, null, 2) + `
|
|
@@ -30391,7 +30640,7 @@ init_database();
|
|
|
30391
30640
|
init_tasks();
|
|
30392
30641
|
init_config2();
|
|
30393
30642
|
init_redaction();
|
|
30394
|
-
import { existsSync as
|
|
30643
|
+
import { existsSync as existsSync13, readFileSync as readFileSync9 } from "fs";
|
|
30395
30644
|
var DEFAULT_RETRY = {
|
|
30396
30645
|
attempts: 1,
|
|
30397
30646
|
backoff_ms: 0
|
|
@@ -30554,7 +30803,7 @@ Timed out after ${provider.timeout_ms}ms`);
|
|
|
30554
30803
|
};
|
|
30555
30804
|
}
|
|
30556
30805
|
function runCiLogProvider(input) {
|
|
30557
|
-
const text = input.log_text ?? (input.log_path &&
|
|
30806
|
+
const text = input.log_text ?? (input.log_path && existsSync13(input.log_path) ? readFileSync9(input.log_path, "utf-8") : "");
|
|
30558
30807
|
return {
|
|
30559
30808
|
status: classifyLog(text),
|
|
30560
30809
|
attempts: 1,
|
|
@@ -30566,7 +30815,7 @@ function runBrowserProvider(input) {
|
|
|
30566
30815
|
if (!input.artifact_path) {
|
|
30567
30816
|
return { status: "unknown", attempts: 1, exit_code: null, output_summary: "browser provider needs a screenshot or artifact path" };
|
|
30568
30817
|
}
|
|
30569
|
-
if (!
|
|
30818
|
+
if (!existsSync13(input.artifact_path)) {
|
|
30570
30819
|
return { status: "failed", attempts: 1, exit_code: null, output_summary: `artifact not found: ${input.artifact_path}` };
|
|
30571
30820
|
}
|
|
30572
30821
|
return {
|
|
@@ -30731,7 +30980,7 @@ function listVerificationRecords(filter = {}, db) {
|
|
|
30731
30980
|
// src/lib/verification-evidence.ts
|
|
30732
30981
|
init_database();
|
|
30733
30982
|
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync10 } from "fs";
|
|
30734
|
-
import { dirname as
|
|
30983
|
+
import { dirname as dirname8 } from "path";
|
|
30735
30984
|
var VERIFICATION_EVIDENCE_SCHEMA = "todos.verification_evidence.v1";
|
|
30736
30985
|
function getMachineId3() {
|
|
30737
30986
|
return process.env["TODOS_MACHINE_ID"] || __require("os").hostname();
|
|
@@ -30851,7 +31100,7 @@ function exportVerificationEvidence(filter = {}, db) {
|
|
|
30851
31100
|
};
|
|
30852
31101
|
}
|
|
30853
31102
|
function writeVerificationExport(bundle, path) {
|
|
30854
|
-
mkdirSync10(
|
|
31103
|
+
mkdirSync10(dirname8(path), { recursive: true });
|
|
30855
31104
|
writeFileSync8(path, JSON.stringify(bundle, null, 2), "utf8");
|
|
30856
31105
|
}
|
|
30857
31106
|
// src/lib/policy-packs.ts
|
|
@@ -31211,21 +31460,21 @@ function resourceDiagnostics() {
|
|
|
31211
31460
|
};
|
|
31212
31461
|
}
|
|
31213
31462
|
// src/lib/sandbox-profiles.ts
|
|
31214
|
-
import { existsSync as
|
|
31215
|
-
import { join as
|
|
31463
|
+
import { existsSync as existsSync14, readFileSync as readFileSync10, writeFileSync as writeFileSync9, mkdirSync as mkdirSync11 } from "fs";
|
|
31464
|
+
import { join as join12, dirname as dirname9 } from "path";
|
|
31216
31465
|
var SANDBOX_PROFILE_VERSION = "todos.sandbox-profile.v1";
|
|
31217
31466
|
function getProfilesPath() {
|
|
31218
31467
|
if (process.env["TODOS_SANDBOX_PROFILES_PATH"]) {
|
|
31219
31468
|
return process.env["TODOS_SANDBOX_PROFILES_PATH"];
|
|
31220
31469
|
}
|
|
31221
|
-
const localDir =
|
|
31222
|
-
const local =
|
|
31223
|
-
if (
|
|
31470
|
+
const localDir = join12(process.cwd(), ".todos");
|
|
31471
|
+
const local = join12(localDir, "sandbox-profiles.json");
|
|
31472
|
+
if (existsSync14(localDir))
|
|
31224
31473
|
return local;
|
|
31225
|
-
if (
|
|
31474
|
+
if (existsSync14(local))
|
|
31226
31475
|
return local;
|
|
31227
31476
|
const home = process.env["HOME"] || "~";
|
|
31228
|
-
return
|
|
31477
|
+
return join12(home, ".hasna", "todos", "sandbox-profiles.json");
|
|
31229
31478
|
}
|
|
31230
31479
|
var cached2 = null;
|
|
31231
31480
|
function resetSandboxProfileCache() {
|
|
@@ -31257,11 +31506,11 @@ function loadSandboxProfiles() {
|
|
|
31257
31506
|
if (cached2)
|
|
31258
31507
|
return cached2;
|
|
31259
31508
|
const path = getProfilesPath();
|
|
31260
|
-
if (!
|
|
31509
|
+
if (!existsSync14(path)) {
|
|
31261
31510
|
cached2 = getDefaultSandboxProfiles();
|
|
31262
31511
|
return cached2;
|
|
31263
31512
|
}
|
|
31264
|
-
const parsed = JSON.parse(
|
|
31513
|
+
const parsed = JSON.parse(readFileSync10(path, "utf8"));
|
|
31265
31514
|
cached2 = parsed.profiles?.length ? parsed.profiles : getDefaultSandboxProfiles();
|
|
31266
31515
|
return cached2;
|
|
31267
31516
|
}
|
|
@@ -31270,7 +31519,7 @@ function getSandboxProfile(name) {
|
|
|
31270
31519
|
}
|
|
31271
31520
|
function saveSandboxProfiles(profiles) {
|
|
31272
31521
|
const path = getProfilesPath();
|
|
31273
|
-
mkdirSync11(
|
|
31522
|
+
mkdirSync11(dirname9(path), { recursive: true });
|
|
31274
31523
|
writeFileSync9(path, JSON.stringify({ schema_version: SANDBOX_PROFILE_VERSION, profiles }, null, 2));
|
|
31275
31524
|
cached2 = profiles;
|
|
31276
31525
|
}
|
|
@@ -31637,7 +31886,7 @@ init_task_commits();
|
|
|
31637
31886
|
|
|
31638
31887
|
// src/lib/git-traceability.ts
|
|
31639
31888
|
init_task_commits();
|
|
31640
|
-
import { existsSync as
|
|
31889
|
+
import { existsSync as existsSync15, readFileSync as readFileSync11 } from "fs";
|
|
31641
31890
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
31642
31891
|
import { resolve as resolve14 } from "path";
|
|
31643
31892
|
var GIT_TRACEABILITY_SCHEMA_VERSION = "todos.git_traceability.v1";
|
|
@@ -31683,10 +31932,10 @@ function inspectGitCommit(sha, cwd) {
|
|
|
31683
31932
|
}
|
|
31684
31933
|
function loadCiSnapshot(path) {
|
|
31685
31934
|
const target = path ? resolve14(path) : resolve14(process.cwd(), ".todos", "ci-snapshot.json");
|
|
31686
|
-
if (!
|
|
31935
|
+
if (!existsSync15(target))
|
|
31687
31936
|
return null;
|
|
31688
31937
|
try {
|
|
31689
|
-
const parsed = JSON.parse(
|
|
31938
|
+
const parsed = JSON.parse(readFileSync11(target, "utf8"));
|
|
31690
31939
|
return { ...parsed, captured_at: parsed.captured_at ?? new Date().toISOString() };
|
|
31691
31940
|
} catch {
|
|
31692
31941
|
return null;
|
|
@@ -31779,8 +32028,8 @@ function formatTraceabilityReport(report) {
|
|
|
31779
32028
|
`);
|
|
31780
32029
|
}
|
|
31781
32030
|
// src/lib/mention-resolver.ts
|
|
31782
|
-
import { existsSync as
|
|
31783
|
-
import { basename as basename4, isAbsolute, join as
|
|
32031
|
+
import { existsSync as existsSync16, readdirSync as readdirSync3, readFileSync as readFileSync12, statSync as statSync6 } from "fs";
|
|
32032
|
+
import { basename as basename4, isAbsolute, join as join13, relative as relative4, resolve as resolve15, sep as sep2 } from "path";
|
|
31784
32033
|
init_database();
|
|
31785
32034
|
init_plans();
|
|
31786
32035
|
init_task_runs();
|
|
@@ -31934,7 +32183,7 @@ function resolveFile(parsed, workspace) {
|
|
|
31934
32183
|
return resolution;
|
|
31935
32184
|
}
|
|
31936
32185
|
resolution.path = relPath;
|
|
31937
|
-
if (!
|
|
32186
|
+
if (!existsSync16(absolutePath)) {
|
|
31938
32187
|
resolution.warnings.push("file does not exist in the local workspace");
|
|
31939
32188
|
return resolution;
|
|
31940
32189
|
}
|
|
@@ -31944,7 +32193,7 @@ function resolveFile(parsed, workspace) {
|
|
|
31944
32193
|
return resolution;
|
|
31945
32194
|
}
|
|
31946
32195
|
if (parsed.line !== undefined) {
|
|
31947
|
-
const lineCount =
|
|
32196
|
+
const lineCount = readFileSync12(absolutePath, "utf-8").split(/\r?\n/).length;
|
|
31948
32197
|
if (parsed.line < 1 || parsed.line > lineCount) {
|
|
31949
32198
|
resolution.warnings.push(`line ${parsed.line} is outside the file range 1-${lineCount}`);
|
|
31950
32199
|
return resolution;
|
|
@@ -31967,7 +32216,7 @@ function walkSourceFiles(root, current = root, files = []) {
|
|
|
31967
32216
|
if (SKIP_DIRS.has(entry2.name))
|
|
31968
32217
|
continue;
|
|
31969
32218
|
}
|
|
31970
|
-
const absolutePath =
|
|
32219
|
+
const absolutePath = join13(current, entry2.name);
|
|
31971
32220
|
if (entry2.isDirectory()) {
|
|
31972
32221
|
if (!SKIP_DIRS.has(entry2.name))
|
|
31973
32222
|
walkSourceFiles(root, absolutePath, files);
|
|
@@ -31997,7 +32246,7 @@ function resolveSymbol(parsed, workspace, maxMatches) {
|
|
|
31997
32246
|
const pattern = symbolPattern(name);
|
|
31998
32247
|
const matches = [];
|
|
31999
32248
|
for (const file of walkSourceFiles(workspace)) {
|
|
32000
|
-
const lines =
|
|
32249
|
+
const lines = readFileSync12(file, "utf-8").split(/\r?\n/);
|
|
32001
32250
|
for (let index = 0;index < lines.length; index += 1) {
|
|
32002
32251
|
const line = lines[index];
|
|
32003
32252
|
const found = pattern.exec(line);
|
|
@@ -33865,7 +34114,7 @@ function getAdapterDocsFingerprint() {
|
|
|
33865
34114
|
// src/lib/inbox-intake.ts
|
|
33866
34115
|
init_database();
|
|
33867
34116
|
init_tasks();
|
|
33868
|
-
import { existsSync as
|
|
34117
|
+
import { existsSync as existsSync17, readFileSync as readFileSync13 } from "fs";
|
|
33869
34118
|
import { basename as basename5 } from "path";
|
|
33870
34119
|
import { createHash as createHash12 } from "crypto";
|
|
33871
34120
|
init_secret_redaction();
|
|
@@ -33911,9 +34160,9 @@ function loadRawContent(input) {
|
|
|
33911
34160
|
}
|
|
33912
34161
|
}
|
|
33913
34162
|
if (input.file_path) {
|
|
33914
|
-
if (!
|
|
34163
|
+
if (!existsSync17(input.file_path))
|
|
33915
34164
|
throw new Error(`File not found: ${input.file_path}`);
|
|
33916
|
-
const raw =
|
|
34165
|
+
const raw = readFileSync13(input.file_path, "utf8");
|
|
33917
34166
|
const name = basename5(input.file_path).toLowerCase();
|
|
33918
34167
|
const source_type2 = input.source_type ?? (name.includes("ci") || name.endsWith(".log") ? "ci_log" : "file");
|
|
33919
34168
|
return {
|
|
@@ -34532,7 +34781,7 @@ function formatNlIntakePreviewText(preview) {
|
|
|
34532
34781
|
// src/lib/issue-importers.ts
|
|
34533
34782
|
init_database();
|
|
34534
34783
|
init_tasks();
|
|
34535
|
-
import { existsSync as
|
|
34784
|
+
import { existsSync as existsSync18, readFileSync as readFileSync14 } from "fs";
|
|
34536
34785
|
var ISSUE_IMPORT_SCHEMA = "todos.issue_import.v1";
|
|
34537
34786
|
var ISSUE_SOURCES = ["github", "linear", "jira", "auto"];
|
|
34538
34787
|
var GITHUB_LABEL_PRIORITY = {
|
|
@@ -34751,9 +35000,9 @@ function parseIssueExport(data, source9 = "auto") {
|
|
|
34751
35000
|
return normalized;
|
|
34752
35001
|
}
|
|
34753
35002
|
function loadIssueExportFromFile(path) {
|
|
34754
|
-
if (!
|
|
35003
|
+
if (!existsSync18(path))
|
|
34755
35004
|
throw new Error(`File not found: ${path}`);
|
|
34756
|
-
return JSON.parse(
|
|
35005
|
+
return JSON.parse(readFileSync14(path, "utf8"));
|
|
34757
35006
|
}
|
|
34758
35007
|
function loadIssueExportInput(input) {
|
|
34759
35008
|
if (input.file_path) {
|
|
@@ -34911,8 +35160,8 @@ todos import issues ./linear.json --source linear --dry-run
|
|
|
34911
35160
|
// src/lib/run-records.ts
|
|
34912
35161
|
init_database();
|
|
34913
35162
|
init_secret_redaction();
|
|
34914
|
-
import { existsSync as
|
|
34915
|
-
import { join as
|
|
35163
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
35164
|
+
import { join as join14, dirname as dirname10 } from "path";
|
|
34916
35165
|
var RUN_RECORD_SCHEMA = "todos.run_record.v1";
|
|
34917
35166
|
var RUN_RECORD_STATUSES = ["active", "completed", "failed", "archived"];
|
|
34918
35167
|
function parseJsonArray3(raw, fallback = []) {
|
|
@@ -35105,8 +35354,8 @@ function buildRunReplayBundle(id, db) {
|
|
|
35105
35354
|
}
|
|
35106
35355
|
function exportRunReplay(id, outputPath, db) {
|
|
35107
35356
|
const bundle = buildRunReplayBundle(id, db);
|
|
35108
|
-
const path = outputPath ??
|
|
35109
|
-
mkdirSync12(
|
|
35357
|
+
const path = outputPath ?? join14(process.cwd(), ".todos", "replays", `${id.slice(0, 8)}.json`);
|
|
35358
|
+
mkdirSync12(dirname10(path), { recursive: true });
|
|
35110
35359
|
writeFileSync10(path, JSON.stringify(bundle, null, 2));
|
|
35111
35360
|
const d = db || getDatabase();
|
|
35112
35361
|
d.run(`UPDATE run_records SET replay_bundle = ?, updated_at = ? WHERE id = ?`, [path, now(), id]);
|
|
@@ -35145,16 +35394,16 @@ function formatRunRecordMarkdown(record) {
|
|
|
35145
35394
|
`;
|
|
35146
35395
|
}
|
|
35147
35396
|
function getDefaultReplayDir() {
|
|
35148
|
-
const local =
|
|
35149
|
-
if (
|
|
35397
|
+
const local = join14(process.cwd(), ".todos", "replays");
|
|
35398
|
+
if (existsSync19(join14(process.cwd(), ".todos")))
|
|
35150
35399
|
return local;
|
|
35151
35400
|
const home = process.env["HOME"] || "~";
|
|
35152
|
-
return
|
|
35401
|
+
return join14(home, ".hasna", "todos", "replays");
|
|
35153
35402
|
}
|
|
35154
35403
|
// src/lib/release-checks.ts
|
|
35155
35404
|
init_secret_redaction();
|
|
35156
|
-
import { existsSync as
|
|
35157
|
-
import { join as
|
|
35405
|
+
import { existsSync as existsSync20, readFileSync as readFileSync15, readdirSync as readdirSync4, statSync as statSync7 } from "fs";
|
|
35406
|
+
import { join as join15, relative as relative5 } from "path";
|
|
35158
35407
|
var RELEASE_CHECK_SCHEMA = "todos.release_check.v1";
|
|
35159
35408
|
var FORBIDDEN_DIST_PATTERNS = [
|
|
35160
35409
|
{
|
|
@@ -35167,16 +35416,16 @@ var FORBIDDEN_DIST_PATTERNS = [
|
|
|
35167
35416
|
];
|
|
35168
35417
|
var REQUIRED_BINS = ["todos", "todos-mcp", "todos-serve"];
|
|
35169
35418
|
function readPackageJson3(root) {
|
|
35170
|
-
const path =
|
|
35171
|
-
if (!
|
|
35419
|
+
const path = join15(root, "package.json");
|
|
35420
|
+
if (!existsSync20(path))
|
|
35172
35421
|
throw new Error(`package.json not found in ${root}`);
|
|
35173
|
-
return JSON.parse(
|
|
35422
|
+
return JSON.parse(readFileSync15(path, "utf8"));
|
|
35174
35423
|
}
|
|
35175
35424
|
function walkFiles(dir, acc = []) {
|
|
35176
|
-
if (!
|
|
35425
|
+
if (!existsSync20(dir))
|
|
35177
35426
|
return acc;
|
|
35178
35427
|
for (const entry2 of readdirSync4(dir)) {
|
|
35179
|
-
const full =
|
|
35428
|
+
const full = join15(dir, entry2);
|
|
35180
35429
|
const st = statSync7(full);
|
|
35181
35430
|
if (st.isDirectory())
|
|
35182
35431
|
walkFiles(full, acc);
|
|
@@ -35194,8 +35443,8 @@ function auditPackageContents(root) {
|
|
|
35194
35443
|
checks.push({ id: "files_dist", severity: "error", message: "package.json files must include dist" });
|
|
35195
35444
|
}
|
|
35196
35445
|
for (const pattern of files) {
|
|
35197
|
-
const target =
|
|
35198
|
-
if (!
|
|
35446
|
+
const target = join15(root, pattern);
|
|
35447
|
+
if (!existsSync20(target)) {
|
|
35199
35448
|
checks.push({ id: `files_missing_${pattern}`, severity: "error", message: `Published file path missing: ${pattern}` });
|
|
35200
35449
|
}
|
|
35201
35450
|
}
|
|
@@ -35209,8 +35458,8 @@ function auditPackageContents(root) {
|
|
|
35209
35458
|
checks.push({ id: `bin_${name}`, severity: "error", message: `Missing bin entry: ${name}` });
|
|
35210
35459
|
continue;
|
|
35211
35460
|
}
|
|
35212
|
-
const binPath =
|
|
35213
|
-
if (!
|
|
35461
|
+
const binPath = join15(root, rel);
|
|
35462
|
+
if (!existsSync20(binPath)) {
|
|
35214
35463
|
checks.push({ id: `bin_path_${name}`, severity: "error", message: `Bin file missing: ${rel}` });
|
|
35215
35464
|
} else {
|
|
35216
35465
|
checks.push({ id: `bin_ok_${name}`, severity: "info", message: `Bin present: ${name} \u2192 ${rel}` });
|
|
@@ -35227,8 +35476,8 @@ function auditPackageContents(root) {
|
|
|
35227
35476
|
}
|
|
35228
35477
|
function scanDistArtifacts(root) {
|
|
35229
35478
|
const checks = [];
|
|
35230
|
-
const distDir =
|
|
35231
|
-
if (!
|
|
35479
|
+
const distDir = join15(root, "dist");
|
|
35480
|
+
if (!existsSync20(distDir)) {
|
|
35232
35481
|
checks.push({ id: "dist_missing", severity: "error", message: "dist/ directory not found \u2014 run bun run build" });
|
|
35233
35482
|
return checks;
|
|
35234
35483
|
}
|
|
@@ -35236,7 +35485,7 @@ function scanDistArtifacts(root) {
|
|
|
35236
35485
|
const rel = relative5(root, file);
|
|
35237
35486
|
let content;
|
|
35238
35487
|
try {
|
|
35239
|
-
content =
|
|
35488
|
+
content = readFileSync15(file, "utf8");
|
|
35240
35489
|
} catch {
|
|
35241
35490
|
continue;
|
|
35242
35491
|
}
|
|
@@ -35538,8 +35787,8 @@ function renderReleaseNotesMarkdown(document) {
|
|
|
35538
35787
|
// src/lib/db-backup.ts
|
|
35539
35788
|
init_database();
|
|
35540
35789
|
init_migrations();
|
|
35541
|
-
import { existsSync as
|
|
35542
|
-
import { dirname as
|
|
35790
|
+
import { existsSync as existsSync21, copyFileSync, mkdirSync as mkdirSync13, readFileSync as readFileSync16, renameSync, statSync as statSync8, writeFileSync as writeFileSync11, unlinkSync } from "fs";
|
|
35791
|
+
import { dirname as dirname11, join as join16, resolve as resolve16 } from "path";
|
|
35543
35792
|
import { Database as Database4 } from "bun:sqlite";
|
|
35544
35793
|
var DB_BACKUP_SCHEMA = "todos.db_backup.v1";
|
|
35545
35794
|
function resolveDbPath(dbPath) {
|
|
@@ -35556,9 +35805,9 @@ function resolveDbPath(dbPath) {
|
|
|
35556
35805
|
}
|
|
35557
35806
|
function backupDatabase(outputPath, sourcePath) {
|
|
35558
35807
|
const source9 = resolveDbPath(sourcePath);
|
|
35559
|
-
if (!
|
|
35808
|
+
if (!existsSync21(source9))
|
|
35560
35809
|
throw new Error(`Database not found: ${source9}`);
|
|
35561
|
-
mkdirSync13(
|
|
35810
|
+
mkdirSync13(dirname11(outputPath), { recursive: true });
|
|
35562
35811
|
closeDatabase();
|
|
35563
35812
|
const src = new Database4(source9);
|
|
35564
35813
|
try {
|
|
@@ -35579,14 +35828,14 @@ function backupDatabase(outputPath, sourcePath) {
|
|
|
35579
35828
|
};
|
|
35580
35829
|
}
|
|
35581
35830
|
function restoreDatabase(backupPath, targetPath) {
|
|
35582
|
-
if (!
|
|
35831
|
+
if (!existsSync21(backupPath))
|
|
35583
35832
|
throw new Error(`Backup not found: ${backupPath}`);
|
|
35584
35833
|
const integrity = checkDatabaseIntegrity(backupPath);
|
|
35585
35834
|
if (!integrity.ok) {
|
|
35586
35835
|
throw new Error(`Backup failed integrity check: ${integrity.errors.join("; ")}`);
|
|
35587
35836
|
}
|
|
35588
35837
|
const target = targetPath ? resolve16(targetPath) : resolveDbPath();
|
|
35589
|
-
mkdirSync13(
|
|
35838
|
+
mkdirSync13(dirname11(target), { recursive: true });
|
|
35590
35839
|
closeDatabase();
|
|
35591
35840
|
const staging = `${target}.restore.tmp`;
|
|
35592
35841
|
try {
|
|
@@ -35595,7 +35844,7 @@ function restoreDatabase(backupPath, targetPath) {
|
|
|
35595
35844
|
copyFileSync(backupPath, staging);
|
|
35596
35845
|
for (const sidecar of [`${target}-wal`, `${target}-shm`]) {
|
|
35597
35846
|
try {
|
|
35598
|
-
if (
|
|
35847
|
+
if (existsSync21(sidecar))
|
|
35599
35848
|
unlinkSync(sidecar);
|
|
35600
35849
|
} catch {}
|
|
35601
35850
|
}
|
|
@@ -35612,7 +35861,7 @@ function restoreDatabase(backupPath, targetPath) {
|
|
|
35612
35861
|
function checkDatabaseIntegrity(dbPath) {
|
|
35613
35862
|
const path = dbPath ? resolve16(dbPath) : resolveDbPath();
|
|
35614
35863
|
const errors = [];
|
|
35615
|
-
if (!
|
|
35864
|
+
if (!existsSync21(path)) {
|
|
35616
35865
|
return {
|
|
35617
35866
|
schema_version: DB_BACKUP_SCHEMA,
|
|
35618
35867
|
path,
|
|
@@ -35709,16 +35958,16 @@ function migrationDryRun(dbPath) {
|
|
|
35709
35958
|
};
|
|
35710
35959
|
}
|
|
35711
35960
|
function defaultBackupPath(dbPath) {
|
|
35712
|
-
const base = dbPath ?
|
|
35961
|
+
const base = dbPath ? dirname11(resolve16(dbPath)) : dirname11(resolveDbPath());
|
|
35713
35962
|
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
35714
|
-
return
|
|
35963
|
+
return join16(base, "backups", `todos-${stamp}.db`);
|
|
35715
35964
|
}
|
|
35716
35965
|
function readBackupManifest(backupPath) {
|
|
35717
35966
|
const manifestPath = `${backupPath}.json`;
|
|
35718
|
-
if (!
|
|
35967
|
+
if (!existsSync21(manifestPath))
|
|
35719
35968
|
return null;
|
|
35720
35969
|
try {
|
|
35721
|
-
return JSON.parse(
|
|
35970
|
+
return JSON.parse(readFileSync16(manifestPath, "utf8"));
|
|
35722
35971
|
} catch {
|
|
35723
35972
|
return null;
|
|
35724
35973
|
}
|
|
@@ -35728,7 +35977,7 @@ function writeBackupManifest(backupPath, result) {
|
|
|
35728
35977
|
writeFileSyncSafe(manifestPath, JSON.stringify(result, null, 2));
|
|
35729
35978
|
}
|
|
35730
35979
|
function writeFileSyncSafe(path, content) {
|
|
35731
|
-
mkdirSync13(
|
|
35980
|
+
mkdirSync13(dirname11(path), { recursive: true });
|
|
35732
35981
|
writeFileSync11(path, content);
|
|
35733
35982
|
}
|
|
35734
35983
|
// src/lib/json-schemas.ts
|
|
@@ -36236,15 +36485,15 @@ ${SCHEMA_ENTITIES.map((e) => `- **${e}**: \`${JSON_SCHEMAS[e].schema_version}\``
|
|
|
36236
36485
|
}
|
|
36237
36486
|
function exportSchemasToDirectory(dir) {
|
|
36238
36487
|
const { mkdirSync: mkdirSync14, writeFileSync: writeFileSync12 } = __require("fs");
|
|
36239
|
-
const { join:
|
|
36488
|
+
const { join: join17 } = __require("path");
|
|
36240
36489
|
mkdirSync14(dir, { recursive: true });
|
|
36241
36490
|
const written = [];
|
|
36242
36491
|
for (const entity of SCHEMA_ENTITIES) {
|
|
36243
|
-
const path =
|
|
36492
|
+
const path = join17(dir, `${entity}.${JSON_SCHEMAS[entity].schema_version.replace(/\./g, "-")}.json`);
|
|
36244
36493
|
writeFileSync12(path, JSON.stringify(JSON_SCHEMAS[entity], null, 2));
|
|
36245
36494
|
written.push(path);
|
|
36246
36495
|
}
|
|
36247
|
-
const catalogPath =
|
|
36496
|
+
const catalogPath = join17(dir, "catalog.json");
|
|
36248
36497
|
writeFileSync12(catalogPath, JSON.stringify({
|
|
36249
36498
|
catalog_version: JSON_SCHEMA_CATALOG_VERSION,
|
|
36250
36499
|
semver: SCHEMA_SEMVER,
|
|
@@ -37159,8 +37408,8 @@ init_tasks();
|
|
|
37159
37408
|
init_plans();
|
|
37160
37409
|
init_comments();
|
|
37161
37410
|
init_templates();
|
|
37162
|
-
import { readFileSync as
|
|
37163
|
-
import { dirname as
|
|
37411
|
+
import { readFileSync as readFileSync17, writeFileSync as writeFileSync12, mkdirSync as mkdirSync14 } from "fs";
|
|
37412
|
+
import { dirname as dirname12 } from "path";
|
|
37164
37413
|
init_secret_redaction();
|
|
37165
37414
|
var BUNDLE_SCHEMA = "todos.bundle.v1";
|
|
37166
37415
|
var BUNDLE_TYPES = ["full_export", "tasks", "partial"];
|
|
@@ -37568,11 +37817,11 @@ function importBundle(bundle, options = {}, db) {
|
|
|
37568
37817
|
return result;
|
|
37569
37818
|
}
|
|
37570
37819
|
function writeBundleFile(bundle, path) {
|
|
37571
|
-
mkdirSync14(
|
|
37820
|
+
mkdirSync14(dirname12(path), { recursive: true });
|
|
37572
37821
|
writeFileSync12(path, JSON.stringify(bundle, null, 2), "utf8");
|
|
37573
37822
|
}
|
|
37574
37823
|
function readBundleFile(path) {
|
|
37575
|
-
const raw = JSON.parse(
|
|
37824
|
+
const raw = JSON.parse(readFileSync17(path, "utf8"));
|
|
37576
37825
|
const validation = validateBundle(raw);
|
|
37577
37826
|
if (!validation.valid)
|
|
37578
37827
|
throw new Error(`Invalid bundle file: ${validation.errors.join("; ")}`);
|
|
@@ -38059,7 +38308,7 @@ init_plans();
|
|
|
38059
38308
|
init_tasks();
|
|
38060
38309
|
init_comments();
|
|
38061
38310
|
import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync15 } from "fs";
|
|
38062
|
-
import { dirname as
|
|
38311
|
+
import { dirname as dirname13 } from "path";
|
|
38063
38312
|
init_task_commits();
|
|
38064
38313
|
var HANDOFF_PACKET_SCHEMA = "todos.handoff_packet.v1";
|
|
38065
38314
|
function summarizeTask2(t) {
|
|
@@ -38234,7 +38483,7 @@ function formatHandoffPacket(packet, format = "json") {
|
|
|
38234
38483
|
function exportHandoffPacket(input = {}, path, db) {
|
|
38235
38484
|
const packet = createHandoffPacket(input, db);
|
|
38236
38485
|
if (path) {
|
|
38237
|
-
mkdirSync15(
|
|
38486
|
+
mkdirSync15(dirname13(path), { recursive: true });
|
|
38238
38487
|
writeFileSync13(path, formatHandoffPacket(packet, "json"), "utf8");
|
|
38239
38488
|
}
|
|
38240
38489
|
return packet;
|
|
@@ -38848,7 +39097,7 @@ init_templates();
|
|
|
38848
39097
|
init_database();
|
|
38849
39098
|
init_templates();
|
|
38850
39099
|
import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
|
|
38851
|
-
import { join as
|
|
39100
|
+
import { join as join17 } from "path";
|
|
38852
39101
|
var BUILTIN_TEMPLATE_LIBRARY_VERSION = "2026-05-21";
|
|
38853
39102
|
var BUILTIN_TEMPLATE_LIBRARY_SOURCE = "bundled-local-template-library";
|
|
38854
39103
|
var TEMPLATE_LIBRARY_SCHEMA = "todos.template_library.v1";
|
|
@@ -39124,7 +39373,7 @@ function writeBuiltinTemplateFiles(directory) {
|
|
|
39124
39373
|
mkdirSync16(directory, { recursive: true });
|
|
39125
39374
|
const files = [];
|
|
39126
39375
|
for (const entry2 of exportBuiltinTemplateFiles()) {
|
|
39127
|
-
const path =
|
|
39376
|
+
const path = join17(directory, entry2.filename);
|
|
39128
39377
|
writeFileSync14(path, `${JSON.stringify(entry2.template, null, 2)}
|
|
39129
39378
|
`, "utf-8");
|
|
39130
39379
|
files.push(path);
|
|
@@ -39168,8 +39417,8 @@ function initBuiltinTemplates(db) {
|
|
|
39168
39417
|
}
|
|
39169
39418
|
// src/lib/template-library.ts
|
|
39170
39419
|
init_database();
|
|
39171
|
-
import { writeFileSync as writeFileSync15, readFileSync as
|
|
39172
|
-
import { dirname as
|
|
39420
|
+
import { writeFileSync as writeFileSync15, readFileSync as readFileSync18, mkdirSync as mkdirSync17 } from "fs";
|
|
39421
|
+
import { dirname as dirname14 } from "path";
|
|
39173
39422
|
init_templates();
|
|
39174
39423
|
function listTemplateLibrary(db) {
|
|
39175
39424
|
const d = db || getDatabase();
|
|
@@ -39212,7 +39461,7 @@ function exportTemplateLibraryCatalog(path, db) {
|
|
|
39212
39461
|
templates: listTemplateLibrary(db)
|
|
39213
39462
|
};
|
|
39214
39463
|
if (path) {
|
|
39215
|
-
mkdirSync17(
|
|
39464
|
+
mkdirSync17(dirname14(path), { recursive: true });
|
|
39216
39465
|
writeFileSync15(path, JSON.stringify(catalog, null, 2), "utf8");
|
|
39217
39466
|
}
|
|
39218
39467
|
return catalog;
|
|
@@ -39230,7 +39479,7 @@ function exportInstalledTemplate(name, db) {
|
|
|
39230
39479
|
}
|
|
39231
39480
|
function importTemplateFromFile(path, db) {
|
|
39232
39481
|
const d = db || getDatabase();
|
|
39233
|
-
const raw = JSON.parse(
|
|
39482
|
+
const raw = JSON.parse(readFileSync18(path, "utf8"));
|
|
39234
39483
|
const payload = raw.template ?? raw;
|
|
39235
39484
|
const created = importTemplate(payload, d);
|
|
39236
39485
|
return { id: created.id, name: created.name };
|
|
@@ -39416,9 +39665,9 @@ init_tasks();
|
|
|
39416
39665
|
init_redaction();
|
|
39417
39666
|
init_sync_utils();
|
|
39418
39667
|
import { createHash as createHash13 } from "crypto";
|
|
39419
|
-
import { existsSync as
|
|
39668
|
+
import { existsSync as existsSync22, readFileSync as readFileSync19, statSync as statSync9 } from "fs";
|
|
39420
39669
|
import { hostname as hostname3, platform, arch } from "os";
|
|
39421
|
-
import { dirname as
|
|
39670
|
+
import { dirname as dirname15, join as join18, resolve as resolve17 } from "path";
|
|
39422
39671
|
import { tmpdir as tmpdir3 } from "os";
|
|
39423
39672
|
var MANIFEST_FILES = ["package.json", "dashboard/package.json", "sdk/package.json"];
|
|
39424
39673
|
var LOCKFILES = ["bun.lock", "bun.lockb", "package-lock.json", "npm-shrinkwrap.json"];
|
|
@@ -39440,20 +39689,20 @@ function sha2565(value) {
|
|
|
39440
39689
|
return createHash13("sha256").update(value).digest("hex");
|
|
39441
39690
|
}
|
|
39442
39691
|
function fileRecord(root, relativePath) {
|
|
39443
|
-
const path =
|
|
39444
|
-
if (!
|
|
39692
|
+
const path = join18(root, relativePath);
|
|
39693
|
+
if (!existsSync22(path))
|
|
39445
39694
|
return null;
|
|
39446
39695
|
const stat = statSync9(path);
|
|
39447
39696
|
if (!stat.isFile())
|
|
39448
39697
|
return null;
|
|
39449
|
-
const content =
|
|
39698
|
+
const content = readFileSync19(path);
|
|
39450
39699
|
return { path: relativePath, sha256: sha2565(content), size_bytes: content.length };
|
|
39451
39700
|
}
|
|
39452
39701
|
function manifestRecord(root, relativePath) {
|
|
39453
39702
|
const base = fileRecord(root, relativePath);
|
|
39454
39703
|
if (!base)
|
|
39455
39704
|
return null;
|
|
39456
|
-
const parsed = readJsonFile(
|
|
39705
|
+
const parsed = readJsonFile(join18(root, relativePath));
|
|
39457
39706
|
if (!parsed)
|
|
39458
39707
|
return { ...base, redacted: {} };
|
|
39459
39708
|
const redacted = redactValue({
|
|
@@ -39548,8 +39797,8 @@ function commandEnv(env, includeValues) {
|
|
|
39548
39797
|
function defaultSnapshotDir() {
|
|
39549
39798
|
const dbPath = getDatabasePath();
|
|
39550
39799
|
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
39551
|
-
return
|
|
39552
|
-
return
|
|
39800
|
+
return join18(tmpdir3(), "hasna-todos", "environment-snapshots");
|
|
39801
|
+
return join18(dirname15(resolve17(dbPath)), "environment-snapshots");
|
|
39553
39802
|
}
|
|
39554
39803
|
function snapshotWithId(snapshot) {
|
|
39555
39804
|
const digest = sha2565(JSON.stringify(snapshot)).slice(0, 24);
|
|
@@ -39596,8 +39845,8 @@ function captureEnvironmentSnapshot(input = {}) {
|
|
|
39596
39845
|
});
|
|
39597
39846
|
}
|
|
39598
39847
|
function writeEnvironmentSnapshot(snapshot, outputPath) {
|
|
39599
|
-
const path = outputPath ? resolve17(outputPath) :
|
|
39600
|
-
ensureDir2(
|
|
39848
|
+
const path = outputPath ? resolve17(outputPath) : join18(defaultSnapshotDir(), `${snapshot.id}.json`);
|
|
39849
|
+
ensureDir2(dirname15(path));
|
|
39601
39850
|
writeJsonFile(path, snapshot);
|
|
39602
39851
|
return path;
|
|
39603
39852
|
}
|
|
@@ -39691,7 +39940,7 @@ init_projects();
|
|
|
39691
39940
|
init_plans();
|
|
39692
39941
|
import { createHash as createHash14 } from "crypto";
|
|
39693
39942
|
import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
|
|
39694
|
-
import { dirname as
|
|
39943
|
+
import { dirname as dirname16, join as join19 } from "path";
|
|
39695
39944
|
var DECISION_RECORD_SCHEMA = "todos.decision_record.v1";
|
|
39696
39945
|
var KNOWLEDGE_SNAPSHOT_SCHEMA = "todos.knowledge_snapshot.v1";
|
|
39697
39946
|
var DECISION_STATUSES = ["proposed", "accepted", "deprecated", "superseded", "rejected"];
|
|
@@ -39921,8 +40170,8 @@ function exportDecisionRecord(id, outputPath, format = "markdown", db) {
|
|
|
39921
40170
|
if (!record)
|
|
39922
40171
|
throw new Error(`Decision record not found: ${id}`);
|
|
39923
40172
|
const content = format === "markdown" ? formatDecisionRecordMarkdown(record) : JSON.stringify(record, null, 2);
|
|
39924
|
-
const path = outputPath ??
|
|
39925
|
-
mkdirSync18(
|
|
40173
|
+
const path = outputPath ?? join19(process.cwd(), ".todos", "decisions", `${record.short_ref}.${format === "markdown" ? "md" : "json"}`);
|
|
40174
|
+
mkdirSync18(dirname16(path), { recursive: true });
|
|
39926
40175
|
writeFileSync16(path, content, "utf8");
|
|
39927
40176
|
return { path, content };
|
|
39928
40177
|
}
|
|
@@ -40069,8 +40318,8 @@ function exportKnowledgeSnapshot(id, outputPath, format = "markdown", db) {
|
|
|
40069
40318
|
throw new Error(`Knowledge snapshot not found: ${id}`);
|
|
40070
40319
|
const content = format === "markdown" ? formatKnowledgeSnapshotMarkdown(record) : JSON.stringify(record, null, 2);
|
|
40071
40320
|
const slug = record.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
40072
|
-
const path = outputPath ??
|
|
40073
|
-
mkdirSync18(
|
|
40321
|
+
const path = outputPath ?? join19(process.cwd(), ".todos", "knowledge", `${slug || record.id.slice(0, 8)}.${format === "markdown" ? "md" : "json"}`);
|
|
40322
|
+
mkdirSync18(dirname16(path), { recursive: true });
|
|
40074
40323
|
writeFileSync16(path, content, "utf8");
|
|
40075
40324
|
return { path, content };
|
|
40076
40325
|
}
|
|
@@ -40103,7 +40352,7 @@ init_tasks();
|
|
|
40103
40352
|
init_task_status();
|
|
40104
40353
|
init_secret_redaction();
|
|
40105
40354
|
import { writeFileSync as writeFileSync17, mkdirSync as mkdirSync19 } from "fs";
|
|
40106
|
-
import { dirname as
|
|
40355
|
+
import { dirname as dirname17 } from "path";
|
|
40107
40356
|
var REPORT_EXPORT_SCHEMA = "todos.report_export.v1";
|
|
40108
40357
|
var REPORT_KINDS = ["project", "plan", "run", "evidence", "roadmap", "retrospective"];
|
|
40109
40358
|
var REPORT_FORMATS = ["markdown", "html"];
|
|
@@ -40334,7 +40583,7 @@ function formatReportExport(data, format) {
|
|
|
40334
40583
|
return format === "html" ? formatReportHtml(data) : formatReportMarkdown(data);
|
|
40335
40584
|
}
|
|
40336
40585
|
function writeReportExport(data, format, path) {
|
|
40337
|
-
mkdirSync19(
|
|
40586
|
+
mkdirSync19(dirname17(path), { recursive: true });
|
|
40338
40587
|
writeFileSync17(path, formatReportExport(data, format), "utf8");
|
|
40339
40588
|
}
|
|
40340
40589
|
function exportReport(input, db) {
|
|
@@ -40368,8 +40617,8 @@ todos report export --kind retrospective --days 14 --format markdown --out retro
|
|
|
40368
40617
|
`;
|
|
40369
40618
|
}
|
|
40370
40619
|
// src/lib/command-aliases.ts
|
|
40371
|
-
import { existsSync as
|
|
40372
|
-
import { join as
|
|
40620
|
+
import { existsSync as existsSync23, readFileSync as readFileSync20, writeFileSync as writeFileSync18, mkdirSync as mkdirSync20 } from "fs";
|
|
40621
|
+
import { join as join20 } from "path";
|
|
40373
40622
|
var COMMAND_ALIASES_SCHEMA = "todos.command_aliases.v1";
|
|
40374
40623
|
var RESERVED = new Set([...listTopLevelCommands(), "help", "version", "alias", "shortcuts"]);
|
|
40375
40624
|
var BUILTIN_SHORTCUTS = [
|
|
@@ -40388,7 +40637,7 @@ var BUILTIN_SHORTCUTS = [
|
|
|
40388
40637
|
{ pattern: /^reports?$/, argv: ["report", "docs"], explain: "Report export documentation" }
|
|
40389
40638
|
];
|
|
40390
40639
|
function aliasesPath(cwd = process.cwd()) {
|
|
40391
|
-
return
|
|
40640
|
+
return join20(cwd, ".todos", "aliases.json");
|
|
40392
40641
|
}
|
|
40393
40642
|
function emptyStore() {
|
|
40394
40643
|
return { schema_version: COMMAND_ALIASES_SCHEMA, aliases: {}, updated_at: new Date(0).toISOString() };
|
|
@@ -40405,9 +40654,9 @@ function validateAliasName(name) {
|
|
|
40405
40654
|
}
|
|
40406
40655
|
function loadAliasStore(cwd) {
|
|
40407
40656
|
const path = aliasesPath(cwd);
|
|
40408
|
-
if (!
|
|
40657
|
+
if (!existsSync23(path))
|
|
40409
40658
|
return emptyStore();
|
|
40410
|
-
const parsed = JSON.parse(
|
|
40659
|
+
const parsed = JSON.parse(readFileSync20(path, "utf8"));
|
|
40411
40660
|
if (parsed.schema_version !== COMMAND_ALIASES_SCHEMA) {
|
|
40412
40661
|
throw new Error(`Unsupported alias store schema: ${parsed.schema_version}`);
|
|
40413
40662
|
}
|
|
@@ -40415,7 +40664,7 @@ function loadAliasStore(cwd) {
|
|
|
40415
40664
|
}
|
|
40416
40665
|
function saveAliasStore(store, cwd) {
|
|
40417
40666
|
const path = aliasesPath(cwd);
|
|
40418
|
-
mkdirSync20(
|
|
40667
|
+
mkdirSync20(join20(path, ".."), { recursive: true });
|
|
40419
40668
|
store.updated_at = new Date().toISOString();
|
|
40420
40669
|
writeFileSync18(path, JSON.stringify(store, null, 2), "utf8");
|
|
40421
40670
|
}
|
|
@@ -41070,18 +41319,18 @@ function createBranchWorkPlan(input, db) {
|
|
|
41070
41319
|
init_database();
|
|
41071
41320
|
init_templates();
|
|
41072
41321
|
init_plans();
|
|
41073
|
-
import { existsSync as
|
|
41074
|
-
import { join as
|
|
41322
|
+
import { existsSync as existsSync24, readFileSync as readFileSync21, writeFileSync as writeFileSync19, mkdirSync as mkdirSync21 } from "fs";
|
|
41323
|
+
import { join as join21 } from "path";
|
|
41075
41324
|
var USER_SCAFFOLD_SCHEMA = "todos.user_scaffold.v1";
|
|
41076
41325
|
var SCAFFOLD_KINDS = ["task", "project", "plan", "checklist", "contract", "verification_policy"];
|
|
41077
41326
|
function storeDir(cwd = process.cwd()) {
|
|
41078
|
-
return
|
|
41327
|
+
return join21(cwd, ".todos", "scaffolds");
|
|
41079
41328
|
}
|
|
41080
41329
|
function storePath(cwd) {
|
|
41081
|
-
return
|
|
41330
|
+
return join21(storeDir(cwd), "store.json");
|
|
41082
41331
|
}
|
|
41083
41332
|
function versionsDir(cwd) {
|
|
41084
|
-
return
|
|
41333
|
+
return join21(storeDir(cwd), "versions");
|
|
41085
41334
|
}
|
|
41086
41335
|
function slugify4(name) {
|
|
41087
41336
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -41091,9 +41340,9 @@ function emptyStore2() {
|
|
|
41091
41340
|
}
|
|
41092
41341
|
function loadUserScaffoldStore(cwd) {
|
|
41093
41342
|
const path = storePath(cwd);
|
|
41094
|
-
if (!
|
|
41343
|
+
if (!existsSync24(path))
|
|
41095
41344
|
return emptyStore2();
|
|
41096
|
-
const parsed = JSON.parse(
|
|
41345
|
+
const parsed = JSON.parse(readFileSync21(path, "utf8"));
|
|
41097
41346
|
if (parsed.schema_version !== USER_SCAFFOLD_SCHEMA) {
|
|
41098
41347
|
throw new Error(`Unsupported scaffold store schema: ${parsed.schema_version}`);
|
|
41099
41348
|
}
|
|
@@ -41106,7 +41355,7 @@ function saveUserScaffoldStore(store, cwd) {
|
|
|
41106
41355
|
}
|
|
41107
41356
|
function snapshotVersion(scaffold, cwd) {
|
|
41108
41357
|
mkdirSync21(versionsDir(cwd), { recursive: true });
|
|
41109
|
-
const path =
|
|
41358
|
+
const path = join21(versionsDir(cwd), `${scaffold.id}-v${scaffold.version}.json`);
|
|
41110
41359
|
writeFileSync19(path, JSON.stringify(scaffold, null, 2), "utf8");
|
|
41111
41360
|
}
|
|
41112
41361
|
function listUserScaffolds(kind, cwd) {
|
|
@@ -41353,7 +41602,7 @@ function listLinkedTemplates(db, cwd) {
|
|
|
41353
41602
|
// src/lib/agent-workflow-demo.ts
|
|
41354
41603
|
init_database();
|
|
41355
41604
|
import { mkdtempSync } from "fs";
|
|
41356
|
-
import { join as
|
|
41605
|
+
import { join as join22 } from "path";
|
|
41357
41606
|
import { tmpdir as tmpdir4 } from "os";
|
|
41358
41607
|
init_projects();
|
|
41359
41608
|
init_task_lists();
|
|
@@ -41374,7 +41623,7 @@ function setupEphemeralDemoDb(options = {}) {
|
|
|
41374
41623
|
if (options.db_path) {
|
|
41375
41624
|
db_path = options.db_path;
|
|
41376
41625
|
} else if (options.persist) {
|
|
41377
|
-
db_path =
|
|
41626
|
+
db_path = join22(mkdtempSync(join22(tmpdir4(), "todos-demo-")), "todos.db");
|
|
41378
41627
|
} else {
|
|
41379
41628
|
db_path = ":memory:";
|
|
41380
41629
|
}
|
|
@@ -43708,16 +43957,16 @@ function runSearchView(idOrName, db) {
|
|
|
43708
43957
|
init_tasks();
|
|
43709
43958
|
init_config2();
|
|
43710
43959
|
init_sync_utils();
|
|
43711
|
-
import { existsSync as
|
|
43712
|
-
import { join as
|
|
43960
|
+
import { existsSync as existsSync25, readFileSync as readFileSync22, readdirSync as readdirSync5, writeFileSync as writeFileSync20 } from "fs";
|
|
43961
|
+
import { join as join23 } from "path";
|
|
43713
43962
|
function getTaskListDir(taskListId) {
|
|
43714
|
-
return
|
|
43963
|
+
return join23(HOME, ".claude", "tasks", taskListId);
|
|
43715
43964
|
}
|
|
43716
43965
|
function readClaudeTask(dir, filename) {
|
|
43717
|
-
return readJsonFile(
|
|
43966
|
+
return readJsonFile(join23(dir, filename));
|
|
43718
43967
|
}
|
|
43719
43968
|
function writeClaudeTask(dir, task2) {
|
|
43720
|
-
writeJsonFile(
|
|
43969
|
+
writeJsonFile(join23(dir, `${task2.id}.json`), task2);
|
|
43721
43970
|
}
|
|
43722
43971
|
function toClaudeStatus(status) {
|
|
43723
43972
|
if (status === "pending" || status === "in_progress" || status === "completed") {
|
|
@@ -43729,14 +43978,14 @@ function toSqliteStatus(status) {
|
|
|
43729
43978
|
return status;
|
|
43730
43979
|
}
|
|
43731
43980
|
function readPrefixCounter(dir) {
|
|
43732
|
-
const path =
|
|
43733
|
-
if (!
|
|
43981
|
+
const path = join23(dir, ".prefix-counter");
|
|
43982
|
+
if (!existsSync25(path))
|
|
43734
43983
|
return 0;
|
|
43735
|
-
const val = parseInt(
|
|
43984
|
+
const val = parseInt(readFileSync22(path, "utf-8").trim(), 10);
|
|
43736
43985
|
return isNaN(val) ? 0 : val;
|
|
43737
43986
|
}
|
|
43738
43987
|
function writePrefixCounter(dir, value) {
|
|
43739
|
-
writeFileSync20(
|
|
43988
|
+
writeFileSync20(join23(dir, ".prefix-counter"), String(value));
|
|
43740
43989
|
}
|
|
43741
43990
|
function formatPrefixedSubject(title, prefix, counter) {
|
|
43742
43991
|
const padded = String(counter).padStart(5, "0");
|
|
@@ -43763,7 +44012,7 @@ function taskToClaudeTask(task2, claudeTaskId, existingMeta) {
|
|
|
43763
44012
|
}
|
|
43764
44013
|
function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
43765
44014
|
const dir = getTaskListDir(taskListId);
|
|
43766
|
-
if (!
|
|
44015
|
+
if (!existsSync25(dir))
|
|
43767
44016
|
ensureDir2(dir);
|
|
43768
44017
|
const filter = {};
|
|
43769
44018
|
if (projectId)
|
|
@@ -43772,7 +44021,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
43772
44021
|
const existingByTodosId = new Map;
|
|
43773
44022
|
const files = listJsonFiles(dir);
|
|
43774
44023
|
for (const f of files) {
|
|
43775
|
-
const path =
|
|
44024
|
+
const path = join23(dir, f);
|
|
43776
44025
|
const ct = readClaudeTask(dir, f);
|
|
43777
44026
|
if (ct?.metadata?.["todos_id"]) {
|
|
43778
44027
|
existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -43859,7 +44108,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
43859
44108
|
}
|
|
43860
44109
|
function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
|
|
43861
44110
|
const dir = getTaskListDir(taskListId);
|
|
43862
|
-
if (!
|
|
44111
|
+
if (!existsSync25(dir)) {
|
|
43863
44112
|
return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
|
|
43864
44113
|
}
|
|
43865
44114
|
const files = readdirSync5(dir).filter((f) => f.endsWith(".json"));
|
|
@@ -43879,7 +44128,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
43879
44128
|
}
|
|
43880
44129
|
for (const f of files) {
|
|
43881
44130
|
try {
|
|
43882
|
-
const filePath =
|
|
44131
|
+
const filePath = join23(dir, f);
|
|
43883
44132
|
const ct = readClaudeTask(dir, f);
|
|
43884
44133
|
if (!ct)
|
|
43885
44134
|
continue;
|
|
@@ -43950,20 +44199,20 @@ function syncClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
43950
44199
|
init_tasks();
|
|
43951
44200
|
init_sync_utils();
|
|
43952
44201
|
init_config2();
|
|
43953
|
-
import { existsSync as
|
|
43954
|
-
import { join as
|
|
44202
|
+
import { existsSync as existsSync26 } from "fs";
|
|
44203
|
+
import { join as join24 } from "path";
|
|
43955
44204
|
function agentBaseDir(agent) {
|
|
43956
44205
|
const key = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
|
|
43957
|
-
return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] ||
|
|
44206
|
+
return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join24(getTodosGlobalDir(), "agents");
|
|
43958
44207
|
}
|
|
43959
44208
|
function getTaskListDir2(agent, taskListId) {
|
|
43960
|
-
return
|
|
44209
|
+
return join24(agentBaseDir(agent), agent, taskListId);
|
|
43961
44210
|
}
|
|
43962
44211
|
function readAgentTask(dir, filename) {
|
|
43963
|
-
return readJsonFile(
|
|
44212
|
+
return readJsonFile(join24(dir, filename));
|
|
43964
44213
|
}
|
|
43965
44214
|
function writeAgentTask(dir, task2) {
|
|
43966
|
-
writeJsonFile(
|
|
44215
|
+
writeJsonFile(join24(dir, `${task2.id}.json`), task2);
|
|
43967
44216
|
}
|
|
43968
44217
|
function taskToAgentTask(task2, externalId, existingMeta) {
|
|
43969
44218
|
return {
|
|
@@ -43988,7 +44237,7 @@ function metadataKey(agent) {
|
|
|
43988
44237
|
}
|
|
43989
44238
|
function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
43990
44239
|
const dir = getTaskListDir2(agent, taskListId);
|
|
43991
|
-
if (!
|
|
44240
|
+
if (!existsSync26(dir))
|
|
43992
44241
|
ensureDir2(dir);
|
|
43993
44242
|
const filter = {};
|
|
43994
44243
|
if (projectId)
|
|
@@ -43997,7 +44246,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
43997
44246
|
const existingByTodosId = new Map;
|
|
43998
44247
|
const files = listJsonFiles(dir);
|
|
43999
44248
|
for (const f of files) {
|
|
44000
|
-
const path =
|
|
44249
|
+
const path = join24(dir, f);
|
|
44001
44250
|
const at = readAgentTask(dir, f);
|
|
44002
44251
|
if (at?.metadata?.["todos_id"]) {
|
|
44003
44252
|
existingByTodosId.set(at.metadata["todos_id"], { task: at, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -44071,7 +44320,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
44071
44320
|
}
|
|
44072
44321
|
function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
44073
44322
|
const dir = getTaskListDir2(agent, taskListId);
|
|
44074
|
-
if (!
|
|
44323
|
+
if (!existsSync26(dir)) {
|
|
44075
44324
|
return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
|
|
44076
44325
|
}
|
|
44077
44326
|
const files = listJsonFiles(dir);
|
|
@@ -44090,7 +44339,7 @@ function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
44090
44339
|
}
|
|
44091
44340
|
for (const f of files) {
|
|
44092
44341
|
try {
|
|
44093
|
-
const filePath =
|
|
44342
|
+
const filePath = join24(dir, f);
|
|
44094
44343
|
const at = readAgentTask(dir, f);
|
|
44095
44344
|
if (!at)
|
|
44096
44345
|
continue;
|
|
@@ -44230,9 +44479,9 @@ function syncWithAgents(agents, taskListIdByAgent, projectId, direction = "both"
|
|
|
44230
44479
|
// src/lib/extract.ts
|
|
44231
44480
|
init_tasks();
|
|
44232
44481
|
init_task_files();
|
|
44233
|
-
import { existsSync as
|
|
44482
|
+
import { existsSync as existsSync27, readFileSync as readFileSync23, statSync as statSync10 } from "fs";
|
|
44234
44483
|
import { createHash as createHash15 } from "crypto";
|
|
44235
|
-
import { relative as relative6, resolve as resolve18, join as
|
|
44484
|
+
import { relative as relative6, resolve as resolve18, join as join25 } from "path";
|
|
44236
44485
|
var EXTRACT_TAGS = ["TODO", "FIXME", "HACK", "XXX", "BUG", "NOTE"];
|
|
44237
44486
|
var DEFAULT_EXTENSIONS = new Set([
|
|
44238
44487
|
".ts",
|
|
@@ -44303,11 +44552,11 @@ function normalizePathForMatch(value) {
|
|
|
44303
44552
|
}
|
|
44304
44553
|
function readGitignorePatterns(basePath) {
|
|
44305
44554
|
const root = statSync10(basePath).isFile() ? resolve18(basePath, "..") : basePath;
|
|
44306
|
-
const gitignorePath =
|
|
44307
|
-
if (!
|
|
44555
|
+
const gitignorePath = join25(root, ".gitignore");
|
|
44556
|
+
if (!existsSync27(gitignorePath))
|
|
44308
44557
|
return [];
|
|
44309
44558
|
try {
|
|
44310
|
-
return
|
|
44559
|
+
return readFileSync23(gitignorePath, "utf-8").split(`
|
|
44311
44560
|
`).map((line) => line.trim()).filter((line) => line && !line.startsWith("#") && !line.startsWith("!"));
|
|
44312
44561
|
} catch {
|
|
44313
44562
|
return [];
|
|
@@ -44446,9 +44695,9 @@ function buildCodebaseIndex(options) {
|
|
|
44446
44695
|
const files = collectFiles(basePath, extensions, excludes, respectGitignore);
|
|
44447
44696
|
const indexed = [];
|
|
44448
44697
|
for (const file of files) {
|
|
44449
|
-
const fullPath = statSync10(basePath).isFile() ? basePath :
|
|
44698
|
+
const fullPath = statSync10(basePath).isFile() ? basePath : join25(basePath, file);
|
|
44450
44699
|
try {
|
|
44451
|
-
const source9 =
|
|
44700
|
+
const source9 = readFileSync23(fullPath, "utf-8");
|
|
44452
44701
|
const relPath = statSync10(basePath).isFile() ? relative6(resolve18(basePath, ".."), fullPath) : file;
|
|
44453
44702
|
indexed.push({
|
|
44454
44703
|
file: relPath,
|
|
@@ -44477,9 +44726,9 @@ function extractTodos(options, db) {
|
|
|
44477
44726
|
const files = collectFiles(basePath, extensions, excludes, respectGitignore);
|
|
44478
44727
|
const allComments = [];
|
|
44479
44728
|
for (const file of files) {
|
|
44480
|
-
const fullPath = statSync10(basePath).isFile() ? basePath :
|
|
44729
|
+
const fullPath = statSync10(basePath).isFile() ? basePath : join25(basePath, file);
|
|
44481
44730
|
try {
|
|
44482
|
-
const source9 =
|
|
44731
|
+
const source9 = readFileSync23(fullPath, "utf-8");
|
|
44483
44732
|
const relPath = statSync10(basePath).isFile() ? relative6(resolve18(basePath, ".."), fullPath) : file;
|
|
44484
44733
|
const comments = extractFromSource(source9, relPath, tags);
|
|
44485
44734
|
allComments.push(...comments);
|
|
@@ -45193,7 +45442,7 @@ function renderWorkflowStatesMarkdown(states = listWorkflowStates()) {
|
|
|
45193
45442
|
// src/lib/agent-replay-simulator.ts
|
|
45194
45443
|
init_redaction();
|
|
45195
45444
|
import { createHash as createHash16 } from "crypto";
|
|
45196
|
-
import { readFileSync as
|
|
45445
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
45197
45446
|
function isObject(value) {
|
|
45198
45447
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
45199
45448
|
}
|
|
@@ -45426,7 +45675,7 @@ function simulateAgentReplay(input, options = {}) {
|
|
|
45426
45675
|
};
|
|
45427
45676
|
}
|
|
45428
45677
|
function simulateAgentReplayFile(path, options = {}) {
|
|
45429
|
-
const parsed = JSON.parse(
|
|
45678
|
+
const parsed = JSON.parse(readFileSync24(path, "utf8"));
|
|
45430
45679
|
return simulateAgentReplay(parsed, options);
|
|
45431
45680
|
}
|
|
45432
45681
|
function renderAgentReplaySimulationMarkdown(simulation) {
|
|
@@ -45454,8 +45703,8 @@ function renderAgentReplaySimulationMarkdown(simulation) {
|
|
|
45454
45703
|
// src/lib/local-extensions.ts
|
|
45455
45704
|
init_config2();
|
|
45456
45705
|
import { createHash as createHash17, createVerify } from "crypto";
|
|
45457
|
-
import { existsSync as
|
|
45458
|
-
import { basename as basename6, join as
|
|
45706
|
+
import { existsSync as existsSync28, readdirSync as readdirSync6, readFileSync as readFileSync25, statSync as statSync11 } from "fs";
|
|
45707
|
+
import { basename as basename6, join as join26, resolve as resolve19 } from "path";
|
|
45459
45708
|
init_redaction();
|
|
45460
45709
|
init_runner_sandbox();
|
|
45461
45710
|
function isObject2(value) {
|
|
@@ -45538,7 +45787,7 @@ function normalizeManifest(input) {
|
|
|
45538
45787
|
};
|
|
45539
45788
|
}
|
|
45540
45789
|
function parseJson(path) {
|
|
45541
|
-
return JSON.parse(
|
|
45790
|
+
return JSON.parse(readFileSync25(path, "utf8"));
|
|
45542
45791
|
}
|
|
45543
45792
|
function sha2566(bytes) {
|
|
45544
45793
|
return `sha256:${createHash17("sha256").update(bytes).digest("hex")}`;
|
|
@@ -45737,13 +45986,13 @@ function verifyExtensionSignature(input) {
|
|
|
45737
45986
|
}
|
|
45738
45987
|
function inspectExtensionSource(source9) {
|
|
45739
45988
|
const resolved = resolve19(source9);
|
|
45740
|
-
if (!
|
|
45989
|
+
if (!existsSync28(resolved))
|
|
45741
45990
|
throw new Error(`extension source not found: ${source9}`);
|
|
45742
45991
|
const stat = statSync11(resolved);
|
|
45743
|
-
const manifestPath = stat.isDirectory() ? [
|
|
45992
|
+
const manifestPath = stat.isDirectory() ? [join26(resolved, "todos.extension.json"), join26(resolved, "extension.json")].find(existsSync28) : resolved;
|
|
45744
45993
|
if (!manifestPath)
|
|
45745
45994
|
throw new Error(`extension directory ${source9} is missing todos.extension.json`);
|
|
45746
|
-
const raw =
|
|
45995
|
+
const raw = readFileSync25(manifestPath);
|
|
45747
45996
|
const parsed = parseJson(manifestPath);
|
|
45748
45997
|
const bundle = isObject2(parsed) && isObject2(parsed["manifest"]);
|
|
45749
45998
|
const manifest = normalizeManifest(bundle ? parsed["manifest"] : parsed);
|
|
@@ -45836,20 +46085,20 @@ function projectExtensionSources(projectPath) {
|
|
|
45836
46085
|
return [];
|
|
45837
46086
|
const root = resolve19(projectPath);
|
|
45838
46087
|
const candidates = [
|
|
45839
|
-
|
|
45840
|
-
|
|
46088
|
+
join26(root, "todos.extension.json"),
|
|
46089
|
+
join26(root, ".todos", "todos.extension.json")
|
|
45841
46090
|
];
|
|
45842
|
-
const extensionDir =
|
|
45843
|
-
if (
|
|
46091
|
+
const extensionDir = join26(root, ".todos", "extensions");
|
|
46092
|
+
if (existsSync28(extensionDir)) {
|
|
45844
46093
|
for (const entry2 of readdirSync6(extensionDir)) {
|
|
45845
46094
|
if (entry2.startsWith("."))
|
|
45846
46095
|
continue;
|
|
45847
|
-
const full =
|
|
46096
|
+
const full = join26(extensionDir, entry2);
|
|
45848
46097
|
if (statSync11(full).isDirectory() || entry2.endsWith(".json"))
|
|
45849
46098
|
candidates.push(full);
|
|
45850
46099
|
}
|
|
45851
46100
|
}
|
|
45852
|
-
return candidates.filter(
|
|
46101
|
+
return candidates.filter(existsSync28);
|
|
45853
46102
|
}
|
|
45854
46103
|
function discoverLocalExtensions(options = {}) {
|
|
45855
46104
|
const config = loadConfig();
|
|
@@ -46151,6 +46400,11 @@ var IDLE_TMUX_COMMANDS = new Set([
|
|
|
46151
46400
|
"tmux",
|
|
46152
46401
|
"zsh"
|
|
46153
46402
|
]);
|
|
46403
|
+
function ensureTmuxAvailable() {
|
|
46404
|
+
if (!Bun.which("tmux", { PATH: process.env.PATH ?? "" })) {
|
|
46405
|
+
throw new Error("tmux is not installed or not in PATH");
|
|
46406
|
+
}
|
|
46407
|
+
}
|
|
46154
46408
|
function parseTmuxTarget(spec) {
|
|
46155
46409
|
if (!spec || spec.trim() === "") {
|
|
46156
46410
|
throw new Error("tmux target spec cannot be empty");
|
|
@@ -46192,6 +46446,7 @@ function formatTmuxTarget(target) {
|
|
|
46192
46446
|
async function validateTmuxTarget(spec) {
|
|
46193
46447
|
const target = parseTmuxTarget(spec);
|
|
46194
46448
|
const targetStr = formatTmuxTarget(target);
|
|
46449
|
+
ensureTmuxAvailable();
|
|
46195
46450
|
let proc;
|
|
46196
46451
|
try {
|
|
46197
46452
|
proc = Bun.spawn(["tmux", "list-panes", "-t", targetStr], {
|
|
@@ -46208,6 +46463,7 @@ async function validateTmuxTarget(spec) {
|
|
|
46208
46463
|
}
|
|
46209
46464
|
}
|
|
46210
46465
|
async function inspectTmuxPane(target) {
|
|
46466
|
+
ensureTmuxAvailable();
|
|
46211
46467
|
const proc = Bun.spawn([
|
|
46212
46468
|
"tmux",
|
|
46213
46469
|
"display-message",
|
|
@@ -46267,6 +46523,7 @@ async function sendToTmux(target, message, delayMs, options = false) {
|
|
|
46267
46523
|
console.log(`[dry-run] message: ${message.slice(0, 200)}`);
|
|
46268
46524
|
return;
|
|
46269
46525
|
}
|
|
46526
|
+
ensureTmuxAvailable();
|
|
46270
46527
|
if (!opts.confirmBusy) {
|
|
46271
46528
|
const pane = await inspectTmuxPane(target);
|
|
46272
46529
|
const status = tmuxPaneBusyStatus(pane);
|
|
@@ -46721,7 +46978,7 @@ init_redaction();
|
|
|
46721
46978
|
// src/lib/retention-cleanup.ts
|
|
46722
46979
|
init_artifact_store();
|
|
46723
46980
|
init_database();
|
|
46724
|
-
import { existsSync as
|
|
46981
|
+
import { existsSync as existsSync29, unlinkSync as unlinkSync2 } from "fs";
|
|
46725
46982
|
var RETENTION_CLEANUP_CONFIRMATION = "delete-local-retention-data";
|
|
46726
46983
|
var ALL_SCOPES = ["comments", "runs", "verifications", "expired_artifacts"];
|
|
46727
46984
|
var EMPTY_COUNTS = {
|
|
@@ -46932,7 +47189,7 @@ function applyRetentionCleanup(input, db) {
|
|
|
46932
47189
|
for (const artifact of report.candidates.artifact_files) {
|
|
46933
47190
|
try {
|
|
46934
47191
|
const path = artifactStorePath(artifact.relative_path);
|
|
46935
|
-
if (!
|
|
47192
|
+
if (!existsSync29(path)) {
|
|
46936
47193
|
report.warnings.push(`stored artifact already missing: ${artifact.relative_path}`);
|
|
46937
47194
|
continue;
|
|
46938
47195
|
}
|
|
@@ -47176,8 +47433,8 @@ init_database();
|
|
|
47176
47433
|
init_migrations();
|
|
47177
47434
|
init_schema();
|
|
47178
47435
|
init_recurrence();
|
|
47179
|
-
import { chmodSync, copyFileSync as copyFileSync2, existsSync as
|
|
47180
|
-
import { basename as basename7, dirname as
|
|
47436
|
+
import { chmodSync, copyFileSync as copyFileSync2, existsSync as existsSync30, mkdirSync as mkdirSync22, statSync as statSync12 } from "fs";
|
|
47437
|
+
import { basename as basename7, dirname as dirname18, join as join27 } from "path";
|
|
47181
47438
|
var REQUIRED_TABLES2 = [
|
|
47182
47439
|
"_migrations",
|
|
47183
47440
|
"projects",
|
|
@@ -47284,7 +47541,7 @@ function findMissingProjectRoots(db) {
|
|
|
47284
47541
|
continue;
|
|
47285
47542
|
if (!row.path.startsWith("/"))
|
|
47286
47543
|
continue;
|
|
47287
|
-
if (!
|
|
47544
|
+
if (!existsSync30(row.path))
|
|
47288
47545
|
missing++;
|
|
47289
47546
|
}
|
|
47290
47547
|
return missing;
|
|
@@ -47344,16 +47601,16 @@ function databasePermissionsAreUnsafe(dbPath) {
|
|
|
47344
47601
|
function createBackup(dbPath) {
|
|
47345
47602
|
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
47346
47603
|
return;
|
|
47347
|
-
if (!
|
|
47604
|
+
if (!existsSync30(dbPath))
|
|
47348
47605
|
return;
|
|
47349
47606
|
const stamp = now().replace(/[:.]/g, "-");
|
|
47350
|
-
const backupDir =
|
|
47607
|
+
const backupDir = join27(dirname18(dbPath), `${basename7(dbPath)}.backup-${stamp}`);
|
|
47351
47608
|
const files = [];
|
|
47352
47609
|
mkdirSync22(backupDir, { recursive: true });
|
|
47353
47610
|
for (const source9 of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
|
|
47354
|
-
if (!
|
|
47611
|
+
if (!existsSync30(source9))
|
|
47355
47612
|
continue;
|
|
47356
|
-
const target =
|
|
47613
|
+
const target = join27(backupDir, basename7(source9));
|
|
47357
47614
|
copyFileSync2(source9, target);
|
|
47358
47615
|
files.push(target);
|
|
47359
47616
|
}
|