@hasna/todos 0.11.71 → 0.11.72
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/query-commands.d.ts.map +1 -1
- package/dist/cli/index.js +444 -53
- package/dist/contracts.js +24 -5
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +765 -422
- package/dist/lib/shared-events.d.ts.map +1 -1
- package/dist/lib/task-route-contract.d.ts +2 -1
- package/dist/lib/task-route-contract.d.ts.map +1 -1
- package/dist/lib/task-route-sources.d.ts +68 -0
- package/dist/lib/task-route-sources.d.ts.map +1 -0
- package/dist/lib/task-routing.d.ts.map +1 -1
- package/dist/mcp/index.js +24 -5
- package/dist/registry.js +24 -5
- package/dist/release-provenance.json +3 -3
- package/dist/server/index.js +26 -7
- package/dist/storage.js +24 -5
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8031,8 +8031,6 @@ function routeEnabledForTask(task, taskList) {
|
|
|
8031
8031
|
const explicit = booleanField(task.metadata.route_enabled);
|
|
8032
8032
|
if (explicit !== undefined)
|
|
8033
8033
|
return explicit;
|
|
8034
|
-
if (task.tags.includes("auto:route") || task.tags.includes("route:enabled"))
|
|
8035
|
-
return true;
|
|
8036
8034
|
const taskListDefault = taskList ? booleanField(taskList.metadata.route_enabled) : undefined;
|
|
8037
8035
|
if (taskListDefault !== undefined)
|
|
8038
8036
|
return taskListDefault;
|
|
@@ -8054,8 +8052,26 @@ function workflowPointersFromMetadata(metadata) {
|
|
|
8054
8052
|
function compactWorkflowPointers(pointers) {
|
|
8055
8053
|
return Object.fromEntries(Object.entries(pointers).filter(([, value]) => typeof value === "string" && value.length > 0));
|
|
8056
8054
|
}
|
|
8057
|
-
function
|
|
8058
|
-
|
|
8055
|
+
function metadataStringField(record, keys) {
|
|
8056
|
+
if (!record)
|
|
8057
|
+
return;
|
|
8058
|
+
for (const key of keys) {
|
|
8059
|
+
const value = record[key];
|
|
8060
|
+
if (typeof value === "string" && value.trim())
|
|
8061
|
+
return value.trim();
|
|
8062
|
+
}
|
|
8063
|
+
return;
|
|
8064
|
+
}
|
|
8065
|
+
function projectKindFromMetadata(...records) {
|
|
8066
|
+
for (const record of records) {
|
|
8067
|
+
const value = metadataStringField(record ?? undefined, ["project_kind", "projectKind", "source_kind", "sourceKind"]);
|
|
8068
|
+
if (value)
|
|
8069
|
+
return value;
|
|
8070
|
+
}
|
|
8071
|
+
return null;
|
|
8072
|
+
}
|
|
8073
|
+
function classifyProjectKind(_path, metadata) {
|
|
8074
|
+
return projectKindFromMetadata(metadata);
|
|
8059
8075
|
}
|
|
8060
8076
|
function isWorktreePath(path) {
|
|
8061
8077
|
return path.includes("/.codewith/worktrees/") || path.includes("/.worktrees/");
|
|
@@ -8128,7 +8144,6 @@ function taskEventMetadata(task) {
|
|
|
8128
8144
|
metadata.project_canonical_path = projectPath;
|
|
8129
8145
|
}
|
|
8130
8146
|
if (projectPath) {
|
|
8131
|
-
metadata.project_kind = classifyProjectKind(projectPath);
|
|
8132
8147
|
metadata.project_is_worktree = isWorktreePath(projectPath);
|
|
8133
8148
|
metadata.working_dir = task.working_dir ?? projectPath;
|
|
8134
8149
|
}
|
|
@@ -8140,6 +8155,10 @@ function taskEventMetadata(task) {
|
|
|
8140
8155
|
metadata.task_list_project_id = taskList.project_id;
|
|
8141
8156
|
metadata.task_list_is_project_default = Boolean(project?.task_list_id && taskList.slug === project.task_list_id);
|
|
8142
8157
|
}
|
|
8158
|
+
const projectKind = projectKindFromMetadata(task.metadata, taskList?.metadata);
|
|
8159
|
+
if (projectKind) {
|
|
8160
|
+
metadata.project_kind = classifyProjectKind(projectPath ?? "", { project_kind: projectKind });
|
|
8161
|
+
}
|
|
8143
8162
|
const routeEnabled = routeEnabledForTask(task, taskList);
|
|
8144
8163
|
if (routeEnabled !== undefined) {
|
|
8145
8164
|
metadata.route_enabled = routeEnabled;
|
|
@@ -25004,6 +25023,496 @@ function clean2(value) {
|
|
|
25004
25023
|
const trimmed = value?.trim();
|
|
25005
25024
|
return trimmed ? trimmed : undefined;
|
|
25006
25025
|
}
|
|
25026
|
+
// src/lib/task-route-sources.ts
|
|
25027
|
+
init_database();
|
|
25028
|
+
import { Database as Database3 } from "bun:sqlite";
|
|
25029
|
+
import { createHash as createHash10 } from "crypto";
|
|
25030
|
+
import { existsSync as existsSync9, readdirSync as readdirSync2, statSync as statSync3 } from "fs";
|
|
25031
|
+
import { basename as basename2, dirname as dirname7, join as join10, resolve as resolve10 } from "path";
|
|
25032
|
+
init_redaction();
|
|
25033
|
+
|
|
25034
|
+
// src/lib/task-routing.ts
|
|
25035
|
+
init_database();
|
|
25036
|
+
function machineLocalPath(project, db) {
|
|
25037
|
+
const machineId = process.env["TODOS_MACHINE_ID"];
|
|
25038
|
+
if (!machineId)
|
|
25039
|
+
return null;
|
|
25040
|
+
try {
|
|
25041
|
+
const row = db.query("SELECT path FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(project.id, machineId);
|
|
25042
|
+
return row?.path ?? null;
|
|
25043
|
+
} catch {
|
|
25044
|
+
return null;
|
|
25045
|
+
}
|
|
25046
|
+
}
|
|
25047
|
+
function resolveProject(task2, db) {
|
|
25048
|
+
const project = task2.project_id ? getProject(task2.project_id, db) : null;
|
|
25049
|
+
const projectPath = project ? machineLocalPath(project, db) ?? project.path : task2.working_dir;
|
|
25050
|
+
return { project, projectPath: projectPath ?? null };
|
|
25051
|
+
}
|
|
25052
|
+
function resolveTaskList(task2, project, db) {
|
|
25053
|
+
if (task2.task_list_id) {
|
|
25054
|
+
return getTaskList(task2.task_list_id, db) ?? (project ? getTaskListBySlug(task2.task_list_id, project.id, db) : null);
|
|
25055
|
+
}
|
|
25056
|
+
if (project?.task_list_id) {
|
|
25057
|
+
return getTaskListBySlug(project.task_list_id, project.id, db);
|
|
25058
|
+
}
|
|
25059
|
+
return null;
|
|
25060
|
+
}
|
|
25061
|
+
function isTerminal2(status) {
|
|
25062
|
+
return status === "completed" || status === "cancelled" || status === "failed";
|
|
25063
|
+
}
|
|
25064
|
+
function routeConcurrencyKey(task2, project, taskList, projectPath) {
|
|
25065
|
+
if (project?.id)
|
|
25066
|
+
return `project:${project.id}`;
|
|
25067
|
+
if (taskList?.id)
|
|
25068
|
+
return `task-list:${taskList.id}`;
|
|
25069
|
+
if (projectPath)
|
|
25070
|
+
return `path:${projectPath}`;
|
|
25071
|
+
return `task:${task2.id}`;
|
|
25072
|
+
}
|
|
25073
|
+
function getTaskRouteState(taskOrId, db) {
|
|
25074
|
+
const d = db || getDatabase();
|
|
25075
|
+
const task2 = typeof taskOrId === "string" ? getTask(taskOrId, d) : taskOrId;
|
|
25076
|
+
if (!task2)
|
|
25077
|
+
throw new Error(`Task not found: ${taskOrId}`);
|
|
25078
|
+
const { project, projectPath } = resolveProject(task2, d);
|
|
25079
|
+
const taskList = resolveTaskList(task2, project, d);
|
|
25080
|
+
const automation = routingAutomationMetadata(task2, taskList) ?? {};
|
|
25081
|
+
const routeEnabled = routeEnabledForTask(task2, taskList) === true;
|
|
25082
|
+
const tagOptIn = task2.tags.includes("auto:route") || task2.tags.includes("route:enabled");
|
|
25083
|
+
const projectKind = projectKindFromMetadata(task2.metadata, taskList?.metadata);
|
|
25084
|
+
const locked = Boolean(task2.locked_by && !isLockExpired(task2.locked_at));
|
|
25085
|
+
const blockers = getBlockingDeps(task2.id, d);
|
|
25086
|
+
const blocked = blockers.length > 0;
|
|
25087
|
+
const terminal = isTerminal2(task2.status);
|
|
25088
|
+
const requiresApproval = automation.requires_approval === true || task2.requires_approval === true;
|
|
25089
|
+
const approvalRequired = automation.approval_required === true;
|
|
25090
|
+
const approved = Boolean(task2.approved_by);
|
|
25091
|
+
const gates = {
|
|
25092
|
+
route_enabled: routeEnabled,
|
|
25093
|
+
tag_opt_in: tagOptIn,
|
|
25094
|
+
no_auto: automation.no_auto === true,
|
|
25095
|
+
manual: automation.manual === true,
|
|
25096
|
+
manual_required: automation.manual_required === true,
|
|
25097
|
+
requires_approval: requiresApproval,
|
|
25098
|
+
approval_required: approvalRequired,
|
|
25099
|
+
approved,
|
|
25100
|
+
locked,
|
|
25101
|
+
blocked,
|
|
25102
|
+
terminal
|
|
25103
|
+
};
|
|
25104
|
+
const reasons = [];
|
|
25105
|
+
if (task2.status !== "pending")
|
|
25106
|
+
reasons.push("task_not_pending");
|
|
25107
|
+
if (terminal)
|
|
25108
|
+
reasons.push("task_terminal");
|
|
25109
|
+
if (!routeEnabled)
|
|
25110
|
+
reasons.push("route_not_enabled");
|
|
25111
|
+
if (locked)
|
|
25112
|
+
reasons.push("task_locked");
|
|
25113
|
+
if (blocked)
|
|
25114
|
+
reasons.push("task_blocked");
|
|
25115
|
+
if (gates.no_auto)
|
|
25116
|
+
reasons.push("no_auto");
|
|
25117
|
+
if (gates.manual)
|
|
25118
|
+
reasons.push("manual");
|
|
25119
|
+
if (gates.manual_required)
|
|
25120
|
+
reasons.push("manual_required");
|
|
25121
|
+
if (requiresApproval && !approved)
|
|
25122
|
+
reasons.push("requires_approval");
|
|
25123
|
+
if (approvalRequired && !approved)
|
|
25124
|
+
reasons.push("approval_required");
|
|
25125
|
+
if (automation.allowed === false)
|
|
25126
|
+
reasons.push("automation_disallowed");
|
|
25127
|
+
return {
|
|
25128
|
+
schema_version: TODOS_TASK_ROUTE_STATE_SCHEMA_VERSION,
|
|
25129
|
+
task_id: task2.id,
|
|
25130
|
+
task_short_id: task2.short_id,
|
|
25131
|
+
status: task2.status,
|
|
25132
|
+
eligible: reasons.length === 0,
|
|
25133
|
+
reasons,
|
|
25134
|
+
blockers: blockers.map((blocker) => ({
|
|
25135
|
+
id: blocker.id,
|
|
25136
|
+
short_id: blocker.short_id,
|
|
25137
|
+
title: blocker.title,
|
|
25138
|
+
status: blocker.status
|
|
25139
|
+
})),
|
|
25140
|
+
gates,
|
|
25141
|
+
automation: Object.keys(automation).length > 0 ? automation : null,
|
|
25142
|
+
route: {
|
|
25143
|
+
project_id: project?.id ?? task2.project_id,
|
|
25144
|
+
project_path: projectPath,
|
|
25145
|
+
working_dir: task2.working_dir ?? projectPath,
|
|
25146
|
+
project_kind: projectKind,
|
|
25147
|
+
task_list_id: taskList?.id ?? task2.task_list_id,
|
|
25148
|
+
task_list_slug: taskList?.slug ?? null,
|
|
25149
|
+
task_list_name: taskList?.name ?? null,
|
|
25150
|
+
concurrency_key: routeConcurrencyKey(task2, project, taskList, projectPath)
|
|
25151
|
+
},
|
|
25152
|
+
pointers: workflowPointersFromMetadata(task2.metadata)
|
|
25153
|
+
};
|
|
25154
|
+
}
|
|
25155
|
+
function setTaskWorkflowPointers(taskId, input, db) {
|
|
25156
|
+
const d = db || getDatabase();
|
|
25157
|
+
const task2 = getTask(taskId, d);
|
|
25158
|
+
if (!task2)
|
|
25159
|
+
throw new Error(`Task not found: ${taskId}`);
|
|
25160
|
+
const previous = workflowPointersFromMetadata(task2.metadata);
|
|
25161
|
+
const next = compactWorkflowPointers({
|
|
25162
|
+
current_workflow_invocation_id: pointerPatch(previous.current_workflow_invocation_id, input, "current_workflow_invocation_id"),
|
|
25163
|
+
current_run_id: pointerPatch(previous.current_run_id, input, "current_run_id"),
|
|
25164
|
+
latest_manifest_path: pointerPatch(previous.latest_manifest_path, input, "latest_manifest_path"),
|
|
25165
|
+
latest_evaluation_path: pointerPatch(previous.latest_evaluation_path, input, "latest_evaluation_path"),
|
|
25166
|
+
workflow_state: pointerPatch(previous.workflow_state, input, "workflow_state")
|
|
25167
|
+
});
|
|
25168
|
+
const timestamp2 = now();
|
|
25169
|
+
const {
|
|
25170
|
+
current_workflow_invocation_id,
|
|
25171
|
+
current_run_id,
|
|
25172
|
+
latest_manifest_path,
|
|
25173
|
+
latest_evaluation_path,
|
|
25174
|
+
workflow_state,
|
|
25175
|
+
workflow_invocation,
|
|
25176
|
+
...baseMetadata
|
|
25177
|
+
} = task2.metadata;
|
|
25178
|
+
const metadata = {
|
|
25179
|
+
...baseMetadata,
|
|
25180
|
+
...next,
|
|
25181
|
+
workflow_invocation: {
|
|
25182
|
+
schema_version: TASK_WORKFLOW_POINTER_SCHEMA_VERSION,
|
|
25183
|
+
...next,
|
|
25184
|
+
updated_at: timestamp2,
|
|
25185
|
+
updated_by: input.actor ?? null
|
|
25186
|
+
}
|
|
25187
|
+
};
|
|
25188
|
+
return updateTask(task2.id, { version: task2.version, metadata }, d);
|
|
25189
|
+
}
|
|
25190
|
+
function pointerPatch(previous, input, key) {
|
|
25191
|
+
if (!Object.prototype.hasOwnProperty.call(input, key))
|
|
25192
|
+
return previous;
|
|
25193
|
+
const value = input[key];
|
|
25194
|
+
if (value === undefined)
|
|
25195
|
+
return previous;
|
|
25196
|
+
return typeof value === "string" && value.trim() ? value : undefined;
|
|
25197
|
+
}
|
|
25198
|
+
|
|
25199
|
+
// src/lib/task-route-sources.ts
|
|
25200
|
+
var TASK_ROUTE_SOURCE_DISCOVERY_SCHEMA_VERSION = "todos.task_route_sources.v1";
|
|
25201
|
+
var TODO_STORE_RELATIVE_PATH = join10(".hasna", "todos", "todos.db");
|
|
25202
|
+
var ROOT_SCAN_MAX_DEPTH = 5;
|
|
25203
|
+
var SKIPPED_SCAN_DIRS = new Set([
|
|
25204
|
+
".git",
|
|
25205
|
+
".hg",
|
|
25206
|
+
".svn",
|
|
25207
|
+
"node_modules",
|
|
25208
|
+
"dist",
|
|
25209
|
+
"build",
|
|
25210
|
+
".next",
|
|
25211
|
+
".turbo",
|
|
25212
|
+
".cache"
|
|
25213
|
+
]);
|
|
25214
|
+
function normalizePath3(input) {
|
|
25215
|
+
return resolve10(input);
|
|
25216
|
+
}
|
|
25217
|
+
function sourceStoreId(sourceDbPath) {
|
|
25218
|
+
const digest = createHash10("sha256").update(sourceDbPath).digest("hex").slice(0, 16);
|
|
25219
|
+
return `sqlite:${digest}`;
|
|
25220
|
+
}
|
|
25221
|
+
function inferSourceRepoPath(sourceDbPath) {
|
|
25222
|
+
const normalized = normalizePath3(sourceDbPath);
|
|
25223
|
+
if (normalized.endsWith(TODO_STORE_RELATIVE_PATH)) {
|
|
25224
|
+
return dirname7(dirname7(dirname7(normalized)));
|
|
25225
|
+
}
|
|
25226
|
+
return dirname7(normalized);
|
|
25227
|
+
}
|
|
25228
|
+
function createStoreRef(sourceDbPath) {
|
|
25229
|
+
const normalized = normalizePath3(sourceDbPath);
|
|
25230
|
+
return {
|
|
25231
|
+
source_store_id: sourceStoreId(normalized),
|
|
25232
|
+
source_repo_path: inferSourceRepoPath(normalized),
|
|
25233
|
+
source_db_path: normalized
|
|
25234
|
+
};
|
|
25235
|
+
}
|
|
25236
|
+
function normalizePatterns(patterns) {
|
|
25237
|
+
return (patterns ?? []).map((pattern) => pattern.trim()).filter(Boolean);
|
|
25238
|
+
}
|
|
25239
|
+
function escapeRegExp(value) {
|
|
25240
|
+
return value.replace(/[|\\{}()[\]^$+?.]/g, "\\$&");
|
|
25241
|
+
}
|
|
25242
|
+
function globPatternToRegExp(pattern) {
|
|
25243
|
+
let source9 = "";
|
|
25244
|
+
for (const char of pattern) {
|
|
25245
|
+
if (char === "*")
|
|
25246
|
+
source9 += ".*";
|
|
25247
|
+
else if (char === "?")
|
|
25248
|
+
source9 += ".";
|
|
25249
|
+
else
|
|
25250
|
+
source9 += escapeRegExp(char);
|
|
25251
|
+
}
|
|
25252
|
+
return new RegExp(`^${source9}$`);
|
|
25253
|
+
}
|
|
25254
|
+
function matchesPattern3(value, pattern) {
|
|
25255
|
+
const normalizedValue = value.replace(/\\/g, "/");
|
|
25256
|
+
const normalizedPattern = pattern.replace(/\\/g, "/");
|
|
25257
|
+
if (normalizedPattern.includes("*") || normalizedPattern.includes("?")) {
|
|
25258
|
+
return globPatternToRegExp(normalizedPattern).test(normalizedValue);
|
|
25259
|
+
}
|
|
25260
|
+
return normalizedValue.includes(normalizedPattern);
|
|
25261
|
+
}
|
|
25262
|
+
function storeMatchesAny(ref, patterns) {
|
|
25263
|
+
if (patterns.length === 0)
|
|
25264
|
+
return false;
|
|
25265
|
+
const paths = [ref.source_db_path, ref.source_repo_path].filter((value) => Boolean(value));
|
|
25266
|
+
const values = paths.flatMap((value) => [value, basename2(value)]);
|
|
25267
|
+
return patterns.some((pattern) => values.some((value) => matchesPattern3(value, pattern)));
|
|
25268
|
+
}
|
|
25269
|
+
function shouldIncludeStore(ref, include, exclude) {
|
|
25270
|
+
const included = include.length === 0 || storeMatchesAny(ref, include);
|
|
25271
|
+
return included && !storeMatchesAny(ref, exclude);
|
|
25272
|
+
}
|
|
25273
|
+
function discoverStoresUnderRoot(sourceRoot) {
|
|
25274
|
+
const rootPath = normalizePath3(sourceRoot);
|
|
25275
|
+
const errors = [];
|
|
25276
|
+
const stores = [];
|
|
25277
|
+
if (!existsSync9(rootPath)) {
|
|
25278
|
+
const ref = createStoreRef(join10(rootPath, TODO_STORE_RELATIVE_PATH));
|
|
25279
|
+
errors.push({
|
|
25280
|
+
...ref,
|
|
25281
|
+
code: "SOURCE_ROOT_MISSING",
|
|
25282
|
+
message: `Source root does not exist: ${rootPath}`
|
|
25283
|
+
});
|
|
25284
|
+
return { stores, errors };
|
|
25285
|
+
}
|
|
25286
|
+
let rootStat;
|
|
25287
|
+
try {
|
|
25288
|
+
rootStat = statSync3(rootPath);
|
|
25289
|
+
} catch (error) {
|
|
25290
|
+
const ref = createStoreRef(join10(rootPath, TODO_STORE_RELATIVE_PATH));
|
|
25291
|
+
errors.push({
|
|
25292
|
+
...ref,
|
|
25293
|
+
code: "SOURCE_ROOT_UNREADABLE",
|
|
25294
|
+
message: error instanceof Error ? error.message : `Unable to read source root: ${rootPath}`
|
|
25295
|
+
});
|
|
25296
|
+
return { stores, errors };
|
|
25297
|
+
}
|
|
25298
|
+
if (rootStat.isFile()) {
|
|
25299
|
+
stores.push(createStoreRef(rootPath));
|
|
25300
|
+
return { stores, errors };
|
|
25301
|
+
}
|
|
25302
|
+
function scanDirectory(dir, depth) {
|
|
25303
|
+
const candidate = join10(dir, TODO_STORE_RELATIVE_PATH);
|
|
25304
|
+
if (existsSync9(candidate)) {
|
|
25305
|
+
stores.push(createStoreRef(candidate));
|
|
25306
|
+
}
|
|
25307
|
+
if (depth >= ROOT_SCAN_MAX_DEPTH)
|
|
25308
|
+
return;
|
|
25309
|
+
let entries;
|
|
25310
|
+
try {
|
|
25311
|
+
entries = readdirSync2(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
|
|
25312
|
+
} catch (error) {
|
|
25313
|
+
const ref = createStoreRef(candidate);
|
|
25314
|
+
errors.push({
|
|
25315
|
+
...ref,
|
|
25316
|
+
code: "SOURCE_ROOT_UNREADABLE",
|
|
25317
|
+
message: error instanceof Error ? error.message : `Unable to read source root: ${dir}`
|
|
25318
|
+
});
|
|
25319
|
+
return;
|
|
25320
|
+
}
|
|
25321
|
+
for (const entry2 of entries) {
|
|
25322
|
+
if (!entry2.isDirectory() || SKIPPED_SCAN_DIRS.has(entry2.name))
|
|
25323
|
+
continue;
|
|
25324
|
+
scanDirectory(join10(dir, entry2.name), depth + 1);
|
|
25325
|
+
}
|
|
25326
|
+
}
|
|
25327
|
+
scanDirectory(rootPath, 0);
|
|
25328
|
+
return { stores, errors };
|
|
25329
|
+
}
|
|
25330
|
+
function collectStoreRefs(input) {
|
|
25331
|
+
const byPath = new Map;
|
|
25332
|
+
const errors = [];
|
|
25333
|
+
for (const storePath of input.sourceStores ?? []) {
|
|
25334
|
+
const ref = createStoreRef(storePath);
|
|
25335
|
+
byPath.set(ref.source_db_path, ref);
|
|
25336
|
+
}
|
|
25337
|
+
for (const sourceRoot of input.sourceRoots ?? []) {
|
|
25338
|
+
const discovered = discoverStoresUnderRoot(sourceRoot);
|
|
25339
|
+
for (const ref of discovered.stores) {
|
|
25340
|
+
byPath.set(ref.source_db_path, ref);
|
|
25341
|
+
}
|
|
25342
|
+
errors.push(...discovered.errors);
|
|
25343
|
+
}
|
|
25344
|
+
return {
|
|
25345
|
+
stores: [...byPath.values()].sort((a, b) => a.source_db_path.localeCompare(b.source_db_path)),
|
|
25346
|
+
errors: errors.sort((a, b) => a.source_db_path.localeCompare(b.source_db_path))
|
|
25347
|
+
};
|
|
25348
|
+
}
|
|
25349
|
+
function openReadonlyStore(ref) {
|
|
25350
|
+
if (!existsSync9(ref.source_db_path)) {
|
|
25351
|
+
throw Object.assign(new Error(`Store does not exist: ${ref.source_db_path}`), { code: "STORE_MISSING" });
|
|
25352
|
+
}
|
|
25353
|
+
return new Database3(ref.source_db_path, { readonly: true, create: false });
|
|
25354
|
+
}
|
|
25355
|
+
function hasTable2(db, tableName) {
|
|
25356
|
+
const row = db.query("SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?").get(tableName);
|
|
25357
|
+
return Boolean(row);
|
|
25358
|
+
}
|
|
25359
|
+
function tableColumns2(db, tableName) {
|
|
25360
|
+
const rows = db.query(`PRAGMA table_info(${tableName})`).all();
|
|
25361
|
+
return new Set(rows.map((row) => row.name));
|
|
25362
|
+
}
|
|
25363
|
+
function listPendingTasksReadonly(db) {
|
|
25364
|
+
if (!hasTable2(db, "tasks")) {
|
|
25365
|
+
throw Object.assign(new Error("Store does not contain a tasks table"), { code: "STORE_INVALID" });
|
|
25366
|
+
}
|
|
25367
|
+
const columns = tableColumns2(db, "tasks");
|
|
25368
|
+
const conditions = ["status = 'pending'"];
|
|
25369
|
+
if (columns.has("archived_at"))
|
|
25370
|
+
conditions.push("archived_at IS NULL");
|
|
25371
|
+
const rows = db.query(`SELECT * FROM tasks WHERE ${conditions.join(" AND ")}
|
|
25372
|
+
ORDER BY CASE priority WHEN 'critical' THEN 0 WHEN 'high' THEN 1 WHEN 'medium' THEN 2 WHEN 'low' THEN 3 END, created_at DESC`).all();
|
|
25373
|
+
return rows.map(rowToTask);
|
|
25374
|
+
}
|
|
25375
|
+
function isReadyTask(task2, db) {
|
|
25376
|
+
if (task2.locked_by && !isLockExpired(task2.locked_at))
|
|
25377
|
+
return false;
|
|
25378
|
+
return getBlockingDeps(task2.id, db).length === 0;
|
|
25379
|
+
}
|
|
25380
|
+
function metadataFingerprint(metadata) {
|
|
25381
|
+
const value = metadata.fingerprint;
|
|
25382
|
+
if (typeof value === "string" && value.trim())
|
|
25383
|
+
return value;
|
|
25384
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
25385
|
+
return String(value);
|
|
25386
|
+
return null;
|
|
25387
|
+
}
|
|
25388
|
+
function boundedMetadataValue(value, depth = 0) {
|
|
25389
|
+
if (depth > 6)
|
|
25390
|
+
return "[TRUNCATED]";
|
|
25391
|
+
if (typeof value === "string") {
|
|
25392
|
+
return value.length > 2000 ? `${value.slice(0, 2000)}[TRUNCATED]` : value;
|
|
25393
|
+
}
|
|
25394
|
+
if (Array.isArray(value)) {
|
|
25395
|
+
return value.slice(0, 50).map((item) => boundedMetadataValue(item, depth + 1));
|
|
25396
|
+
}
|
|
25397
|
+
if (value && typeof value === "object") {
|
|
25398
|
+
const result = {};
|
|
25399
|
+
for (const [key, child] of Object.entries(value).slice(0, 80)) {
|
|
25400
|
+
const normalized = key.toLowerCase();
|
|
25401
|
+
if (normalized === "comment" || normalized === "comments" || normalized === "task_comments") {
|
|
25402
|
+
result[key] = "[REDACTED_COMMENT]";
|
|
25403
|
+
continue;
|
|
25404
|
+
}
|
|
25405
|
+
result[key] = boundedMetadataValue(child, depth + 1);
|
|
25406
|
+
}
|
|
25407
|
+
return result;
|
|
25408
|
+
}
|
|
25409
|
+
return value;
|
|
25410
|
+
}
|
|
25411
|
+
function discoveryMetadata(metadata) {
|
|
25412
|
+
return redactValue(boundedMetadataValue(metadata));
|
|
25413
|
+
}
|
|
25414
|
+
function sourceCandidate(ref, task2, db) {
|
|
25415
|
+
const routeState = getTaskRouteState(task2, db);
|
|
25416
|
+
const autoRoute = task2.tags.includes("auto:route") || task2.tags.includes("route:enabled");
|
|
25417
|
+
return {
|
|
25418
|
+
source_store_id: ref.source_store_id,
|
|
25419
|
+
source_repo_path: ref.source_repo_path,
|
|
25420
|
+
source_db_path: ref.source_db_path,
|
|
25421
|
+
source_task_key: `${ref.source_store_id}:${task2.id}`,
|
|
25422
|
+
source_selected_by_input: true,
|
|
25423
|
+
task_id: task2.id,
|
|
25424
|
+
task_short_id: task2.short_id,
|
|
25425
|
+
title: task2.title,
|
|
25426
|
+
status: task2.status,
|
|
25427
|
+
priority: task2.priority,
|
|
25428
|
+
project_path: routeState.route.project_path ?? task2.working_dir ?? ref.source_repo_path,
|
|
25429
|
+
task_version: task2.version,
|
|
25430
|
+
task_updated_at: task2.updated_at,
|
|
25431
|
+
task_fingerprint: metadataFingerprint(task2.metadata),
|
|
25432
|
+
tags: task2.tags,
|
|
25433
|
+
task_intent: {
|
|
25434
|
+
auto_route: autoRoute
|
|
25435
|
+
},
|
|
25436
|
+
metadata: discoveryMetadata(task2.metadata),
|
|
25437
|
+
route_state: routeState
|
|
25438
|
+
};
|
|
25439
|
+
}
|
|
25440
|
+
function discoveryError(ref, code, error) {
|
|
25441
|
+
return {
|
|
25442
|
+
...ref,
|
|
25443
|
+
code,
|
|
25444
|
+
message: error instanceof Error ? error.message : String(error)
|
|
25445
|
+
};
|
|
25446
|
+
}
|
|
25447
|
+
function errorCode(error) {
|
|
25448
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "STORE_MISSING") {
|
|
25449
|
+
return "STORE_MISSING";
|
|
25450
|
+
}
|
|
25451
|
+
if (typeof error === "object" && error !== null && "code" in error && error.code === "STORE_INVALID") {
|
|
25452
|
+
return "STORE_INVALID";
|
|
25453
|
+
}
|
|
25454
|
+
return "STORE_UNREADABLE";
|
|
25455
|
+
}
|
|
25456
|
+
function discoverTaskRouteSources(input) {
|
|
25457
|
+
const include = normalizePatterns(input.include);
|
|
25458
|
+
const exclude = normalizePatterns(input.exclude);
|
|
25459
|
+
const sourceRoots = (input.sourceRoots ?? []).map(normalizePath3).sort();
|
|
25460
|
+
const sourceStores = (input.sourceStores ?? []).map(normalizePath3).sort();
|
|
25461
|
+
const limit = Number.isFinite(input.limit ?? NaN) && (input.limit ?? 0) >= 0 ? Math.floor(input.limit ?? 0) : null;
|
|
25462
|
+
const collected = collectStoreRefs(input);
|
|
25463
|
+
const stores = [];
|
|
25464
|
+
const errors = [...collected.errors];
|
|
25465
|
+
const candidates = [];
|
|
25466
|
+
let totalCandidateCount = 0;
|
|
25467
|
+
for (const ref of collected.stores) {
|
|
25468
|
+
if (!shouldIncludeStore(ref, include, exclude))
|
|
25469
|
+
continue;
|
|
25470
|
+
const storeErrors = [];
|
|
25471
|
+
let db = null;
|
|
25472
|
+
try {
|
|
25473
|
+
db = openReadonlyStore(ref);
|
|
25474
|
+
const readyTasks = listPendingTasksReadonly(db).filter((task2) => isReadyTask(task2, db));
|
|
25475
|
+
totalCandidateCount += readyTasks.length;
|
|
25476
|
+
const remaining = limit === null ? readyTasks.length : Math.max(0, limit - candidates.length);
|
|
25477
|
+
const selectedTasks = limit === null ? readyTasks : readyTasks.slice(0, remaining);
|
|
25478
|
+
candidates.push(...selectedTasks.map((task2) => sourceCandidate(ref, task2, db)));
|
|
25479
|
+
stores.push({
|
|
25480
|
+
...ref,
|
|
25481
|
+
status: "ok",
|
|
25482
|
+
candidate_count: readyTasks.length,
|
|
25483
|
+
returned_candidate_count: selectedTasks.length,
|
|
25484
|
+
errors: []
|
|
25485
|
+
});
|
|
25486
|
+
} catch (error) {
|
|
25487
|
+
const storeError = discoveryError(ref, errorCode(error), error);
|
|
25488
|
+
storeErrors.push(storeError);
|
|
25489
|
+
errors.push(storeError);
|
|
25490
|
+
stores.push({
|
|
25491
|
+
...ref,
|
|
25492
|
+
status: storeError.code === "STORE_MISSING" ? "missing" : "error",
|
|
25493
|
+
candidate_count: 0,
|
|
25494
|
+
returned_candidate_count: 0,
|
|
25495
|
+
errors: storeErrors
|
|
25496
|
+
});
|
|
25497
|
+
} finally {
|
|
25498
|
+
db?.close();
|
|
25499
|
+
}
|
|
25500
|
+
}
|
|
25501
|
+
return {
|
|
25502
|
+
schema_version: TASK_ROUTE_SOURCE_DISCOVERY_SCHEMA_VERSION,
|
|
25503
|
+
sourceRoots,
|
|
25504
|
+
sourceStores,
|
|
25505
|
+
include,
|
|
25506
|
+
exclude,
|
|
25507
|
+
limit,
|
|
25508
|
+
total_candidate_count: totalCandidateCount,
|
|
25509
|
+
returned_candidate_count: candidates.length,
|
|
25510
|
+
truncated: limit !== null && totalCandidateCount > candidates.length,
|
|
25511
|
+
stores,
|
|
25512
|
+
candidates,
|
|
25513
|
+
errors
|
|
25514
|
+
};
|
|
25515
|
+
}
|
|
25007
25516
|
|
|
25008
25517
|
// src/index.ts
|
|
25009
25518
|
init_database();
|
|
@@ -25141,8 +25650,8 @@ function listCyclesWithStats(options = {}, db) {
|
|
|
25141
25650
|
}
|
|
25142
25651
|
// src/lib/plan-artifacts.ts
|
|
25143
25652
|
init_database();
|
|
25144
|
-
import { existsSync as
|
|
25145
|
-
import { join as
|
|
25653
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
25654
|
+
import { join as join11, resolve as resolve11 } from "path";
|
|
25146
25655
|
var PLAN_MARKDOWN_SCHEMA = "hasna.todos.plan/v1";
|
|
25147
25656
|
function assertSafePathSegment(value, label) {
|
|
25148
25657
|
const trimmed = value.trim();
|
|
@@ -25186,7 +25695,7 @@ function resolvePlanArtifactProject(input) {
|
|
|
25186
25695
|
const ref = input.project_id || input.project_ref;
|
|
25187
25696
|
if (!ref)
|
|
25188
25697
|
throw new Error("Plan artifacts require a project id or project reference");
|
|
25189
|
-
const byPath = getProjectByPath(
|
|
25698
|
+
const byPath = getProjectByPath(resolve11(ref), db);
|
|
25190
25699
|
if (byPath)
|
|
25191
25700
|
return byPath;
|
|
25192
25701
|
const resolvedId = resolvePartialId(db, "projects", ref);
|
|
@@ -25203,8 +25712,8 @@ function resolvePlanArtifactProject(input) {
|
|
|
25203
25712
|
function resolvePlanArtifactPaths(input) {
|
|
25204
25713
|
const project = resolvePlanArtifactProject(input);
|
|
25205
25714
|
const projectId = assertSafePathSegment(project.id, "project id");
|
|
25206
|
-
const projectRoot =
|
|
25207
|
-
const directory =
|
|
25715
|
+
const projectRoot = resolve11(project.path);
|
|
25716
|
+
const directory = join11(projectRoot, ".hasna", "todos", "plans", projectId);
|
|
25208
25717
|
const planId = input.plan_id ? assertSafePathSegment(input.plan_id, "plan id") : null;
|
|
25209
25718
|
const planSlug = input.plan_slug ? assertSafePathSegment(slugify(input.plan_slug), "plan slug") : null;
|
|
25210
25719
|
const fileName = planId ? planSlug ? `${planSlug}--${planId.slice(0, 8)}.md` : `${planId}.md` : null;
|
|
@@ -25212,7 +25721,7 @@ function resolvePlanArtifactPaths(input) {
|
|
|
25212
25721
|
project_id: project.id,
|
|
25213
25722
|
project_root: projectRoot,
|
|
25214
25723
|
directory,
|
|
25215
|
-
file_path: fileName ?
|
|
25724
|
+
file_path: fileName ? join11(directory, fileName) : directory
|
|
25216
25725
|
};
|
|
25217
25726
|
}
|
|
25218
25727
|
function resolvePlanArtifactCandidatePaths(plan, db) {
|
|
@@ -25378,7 +25887,7 @@ function readPlanArtifact(plan, db) {
|
|
|
25378
25887
|
return null;
|
|
25379
25888
|
const d = db || getDatabase();
|
|
25380
25889
|
const paths = resolvePlanArtifactCandidatePaths(plan, d);
|
|
25381
|
-
const path =
|
|
25890
|
+
const path = existsSync10(paths.primary.file_path) ? paths.primary.file_path : existsSync10(paths.legacy.file_path) ? paths.legacy.file_path : null;
|
|
25382
25891
|
if (!path)
|
|
25383
25892
|
return null;
|
|
25384
25893
|
const markdown = readFileSync7(path, "utf8");
|
|
@@ -25393,7 +25902,7 @@ function inspectPlanArtifact(plan, db) {
|
|
|
25393
25902
|
return null;
|
|
25394
25903
|
const d = db || getDatabase();
|
|
25395
25904
|
const paths = resolvePlanArtifactCandidatePaths(plan, d);
|
|
25396
|
-
const path =
|
|
25905
|
+
const path = existsSync10(paths.primary.file_path) ? paths.primary.file_path : existsSync10(paths.legacy.file_path) ? paths.legacy.file_path : null;
|
|
25397
25906
|
if (!path) {
|
|
25398
25907
|
return {
|
|
25399
25908
|
path: paths.primary.file_path,
|
|
@@ -26479,28 +26988,28 @@ function renderRetrospectiveMarkdown(record) {
|
|
|
26479
26988
|
}
|
|
26480
26989
|
// src/lib/project-bootstrap.ts
|
|
26481
26990
|
init_database();
|
|
26482
|
-
import { existsSync as
|
|
26483
|
-
import { basename as
|
|
26991
|
+
import { existsSync as existsSync11, readFileSync as readFileSync8, statSync as statSync4 } from "fs";
|
|
26992
|
+
import { basename as basename3, dirname as dirname8, resolve as resolve12 } from "path";
|
|
26484
26993
|
function safeStat(path) {
|
|
26485
26994
|
try {
|
|
26486
|
-
return
|
|
26995
|
+
return statSync4(path);
|
|
26487
26996
|
} catch {
|
|
26488
26997
|
return null;
|
|
26489
26998
|
}
|
|
26490
26999
|
}
|
|
26491
27000
|
function canonicalPath(input) {
|
|
26492
|
-
const resolved =
|
|
27001
|
+
const resolved = resolve12(input);
|
|
26493
27002
|
const stats2 = safeStat(resolved);
|
|
26494
27003
|
if (stats2?.isFile())
|
|
26495
|
-
return
|
|
27004
|
+
return dirname8(resolved);
|
|
26496
27005
|
return resolved;
|
|
26497
27006
|
}
|
|
26498
27007
|
function findUp(start, marker) {
|
|
26499
27008
|
let current = canonicalPath(start);
|
|
26500
27009
|
while (true) {
|
|
26501
|
-
if (
|
|
27010
|
+
if (existsSync11(resolve12(current, marker)))
|
|
26502
27011
|
return current;
|
|
26503
|
-
const parent =
|
|
27012
|
+
const parent = dirname8(current);
|
|
26504
27013
|
if (parent === current)
|
|
26505
27014
|
return null;
|
|
26506
27015
|
current = parent;
|
|
@@ -26509,8 +27018,8 @@ function findUp(start, marker) {
|
|
|
26509
27018
|
function readPackageJson2(path) {
|
|
26510
27019
|
if (!path)
|
|
26511
27020
|
return null;
|
|
26512
|
-
const file =
|
|
26513
|
-
if (!
|
|
27021
|
+
const file = resolve12(path, "package.json");
|
|
27022
|
+
if (!existsSync11(file))
|
|
26514
27023
|
return null;
|
|
26515
27024
|
try {
|
|
26516
27025
|
const parsed = JSON.parse(readFileSync8(file, "utf-8"));
|
|
@@ -26521,9 +27030,9 @@ function readPackageJson2(path) {
|
|
|
26521
27030
|
}
|
|
26522
27031
|
function packageDisplayName(name, fallbackPath) {
|
|
26523
27032
|
if (!name)
|
|
26524
|
-
return
|
|
27033
|
+
return basename3(fallbackPath);
|
|
26525
27034
|
const withoutScope = name.startsWith("@") ? name.split("/")[1] : name;
|
|
26526
|
-
return withoutScope ||
|
|
27035
|
+
return withoutScope || basename3(fallbackPath);
|
|
26527
27036
|
}
|
|
26528
27037
|
function workspaceMarker(root, rootPackage) {
|
|
26529
27038
|
if (!root)
|
|
@@ -26532,7 +27041,7 @@ function workspaceMarker(root, rootPackage) {
|
|
|
26532
27041
|
if (rootPackage?.workspaces)
|
|
26533
27042
|
markers.push("package.json#workspaces");
|
|
26534
27043
|
for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
|
|
26535
|
-
if (
|
|
27044
|
+
if (existsSync11(resolve12(root, marker)))
|
|
26536
27045
|
markers.push(marker);
|
|
26537
27046
|
}
|
|
26538
27047
|
const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
|
|
@@ -26650,177 +27159,9 @@ function getProjectByPathForBootstrap(path, db) {
|
|
|
26650
27159
|
WHERE pmp.path = ?`).get(path);
|
|
26651
27160
|
return machineRow ?? null;
|
|
26652
27161
|
}
|
|
26653
|
-
// src/lib/task-routing.ts
|
|
26654
|
-
init_database();
|
|
26655
|
-
function classifyProjectKind2(path) {
|
|
26656
|
-
if (!path)
|
|
26657
|
-
return null;
|
|
26658
|
-
return path.includes("/hasna/opensource/") ? "open-source" : "unknown";
|
|
26659
|
-
}
|
|
26660
|
-
function machineLocalPath(project, db) {
|
|
26661
|
-
const machineId = process.env["TODOS_MACHINE_ID"];
|
|
26662
|
-
if (!machineId)
|
|
26663
|
-
return null;
|
|
26664
|
-
try {
|
|
26665
|
-
const row = db.query("SELECT path FROM project_machine_paths WHERE project_id = ? AND machine_id = ?").get(project.id, machineId);
|
|
26666
|
-
return row?.path ?? null;
|
|
26667
|
-
} catch {
|
|
26668
|
-
return null;
|
|
26669
|
-
}
|
|
26670
|
-
}
|
|
26671
|
-
function resolveProject(task2, db) {
|
|
26672
|
-
const project = task2.project_id ? getProject(task2.project_id, db) : null;
|
|
26673
|
-
const projectPath = project ? machineLocalPath(project, db) ?? project.path : task2.working_dir;
|
|
26674
|
-
return { project, projectPath: projectPath ?? null };
|
|
26675
|
-
}
|
|
26676
|
-
function resolveTaskList(task2, project, db) {
|
|
26677
|
-
if (task2.task_list_id) {
|
|
26678
|
-
return getTaskList(task2.task_list_id, db) ?? (project ? getTaskListBySlug(task2.task_list_id, project.id, db) : null);
|
|
26679
|
-
}
|
|
26680
|
-
if (project?.task_list_id) {
|
|
26681
|
-
return getTaskListBySlug(project.task_list_id, project.id, db);
|
|
26682
|
-
}
|
|
26683
|
-
return null;
|
|
26684
|
-
}
|
|
26685
|
-
function isTerminal2(status) {
|
|
26686
|
-
return status === "completed" || status === "cancelled" || status === "failed";
|
|
26687
|
-
}
|
|
26688
|
-
function routeConcurrencyKey(task2, project, taskList, projectPath) {
|
|
26689
|
-
if (project?.id)
|
|
26690
|
-
return `project:${project.id}`;
|
|
26691
|
-
if (taskList?.id)
|
|
26692
|
-
return `task-list:${taskList.id}`;
|
|
26693
|
-
if (projectPath)
|
|
26694
|
-
return `path:${projectPath}`;
|
|
26695
|
-
return `task:${task2.id}`;
|
|
26696
|
-
}
|
|
26697
|
-
function getTaskRouteState(taskOrId, db) {
|
|
26698
|
-
const d = db || getDatabase();
|
|
26699
|
-
const task2 = typeof taskOrId === "string" ? getTask(taskOrId, d) : taskOrId;
|
|
26700
|
-
if (!task2)
|
|
26701
|
-
throw new Error(`Task not found: ${taskOrId}`);
|
|
26702
|
-
const { project, projectPath } = resolveProject(task2, d);
|
|
26703
|
-
const taskList = resolveTaskList(task2, project, d);
|
|
26704
|
-
const automation = routingAutomationMetadata(task2, taskList) ?? {};
|
|
26705
|
-
const routeEnabled = routeEnabledForTask(task2, taskList) === true;
|
|
26706
|
-
const tagOptIn = task2.tags.includes("auto:route") || task2.tags.includes("route:enabled");
|
|
26707
|
-
const locked = Boolean(task2.locked_by && !isLockExpired(task2.locked_at));
|
|
26708
|
-
const blockers = getBlockingDeps(task2.id, d);
|
|
26709
|
-
const blocked = blockers.length > 0;
|
|
26710
|
-
const terminal = isTerminal2(task2.status);
|
|
26711
|
-
const requiresApproval = automation.requires_approval === true || task2.requires_approval === true;
|
|
26712
|
-
const approvalRequired = automation.approval_required === true;
|
|
26713
|
-
const approved = Boolean(task2.approved_by);
|
|
26714
|
-
const gates = {
|
|
26715
|
-
route_enabled: routeEnabled,
|
|
26716
|
-
tag_opt_in: tagOptIn,
|
|
26717
|
-
no_auto: automation.no_auto === true,
|
|
26718
|
-
manual: automation.manual === true,
|
|
26719
|
-
manual_required: automation.manual_required === true,
|
|
26720
|
-
requires_approval: requiresApproval,
|
|
26721
|
-
approval_required: approvalRequired,
|
|
26722
|
-
approved,
|
|
26723
|
-
locked,
|
|
26724
|
-
blocked,
|
|
26725
|
-
terminal
|
|
26726
|
-
};
|
|
26727
|
-
const reasons = [];
|
|
26728
|
-
if (task2.status !== "pending")
|
|
26729
|
-
reasons.push("task_not_pending");
|
|
26730
|
-
if (terminal)
|
|
26731
|
-
reasons.push("task_terminal");
|
|
26732
|
-
if (!routeEnabled)
|
|
26733
|
-
reasons.push("route_not_enabled");
|
|
26734
|
-
if (locked)
|
|
26735
|
-
reasons.push("task_locked");
|
|
26736
|
-
if (blocked)
|
|
26737
|
-
reasons.push("task_blocked");
|
|
26738
|
-
if (gates.no_auto)
|
|
26739
|
-
reasons.push("no_auto");
|
|
26740
|
-
if (gates.manual)
|
|
26741
|
-
reasons.push("manual");
|
|
26742
|
-
if (gates.manual_required)
|
|
26743
|
-
reasons.push("manual_required");
|
|
26744
|
-
if (requiresApproval && !approved)
|
|
26745
|
-
reasons.push("requires_approval");
|
|
26746
|
-
if (approvalRequired && !approved)
|
|
26747
|
-
reasons.push("approval_required");
|
|
26748
|
-
if (automation.allowed === false)
|
|
26749
|
-
reasons.push("automation_disallowed");
|
|
26750
|
-
return {
|
|
26751
|
-
schema_version: TODOS_TASK_ROUTE_STATE_SCHEMA_VERSION,
|
|
26752
|
-
task_id: task2.id,
|
|
26753
|
-
task_short_id: task2.short_id,
|
|
26754
|
-
status: task2.status,
|
|
26755
|
-
eligible: reasons.length === 0,
|
|
26756
|
-
reasons,
|
|
26757
|
-
blockers: blockers.map((blocker) => ({
|
|
26758
|
-
id: blocker.id,
|
|
26759
|
-
short_id: blocker.short_id,
|
|
26760
|
-
title: blocker.title,
|
|
26761
|
-
status: blocker.status
|
|
26762
|
-
})),
|
|
26763
|
-
gates,
|
|
26764
|
-
automation: Object.keys(automation).length > 0 ? automation : null,
|
|
26765
|
-
route: {
|
|
26766
|
-
project_id: project?.id ?? task2.project_id,
|
|
26767
|
-
project_path: projectPath,
|
|
26768
|
-
working_dir: task2.working_dir ?? projectPath,
|
|
26769
|
-
project_kind: classifyProjectKind2(projectPath),
|
|
26770
|
-
task_list_id: taskList?.id ?? task2.task_list_id,
|
|
26771
|
-
task_list_slug: taskList?.slug ?? null,
|
|
26772
|
-
task_list_name: taskList?.name ?? null,
|
|
26773
|
-
concurrency_key: routeConcurrencyKey(task2, project, taskList, projectPath)
|
|
26774
|
-
},
|
|
26775
|
-
pointers: workflowPointersFromMetadata(task2.metadata)
|
|
26776
|
-
};
|
|
26777
|
-
}
|
|
26778
|
-
function setTaskWorkflowPointers(taskId, input, db) {
|
|
26779
|
-
const d = db || getDatabase();
|
|
26780
|
-
const task2 = getTask(taskId, d);
|
|
26781
|
-
if (!task2)
|
|
26782
|
-
throw new Error(`Task not found: ${taskId}`);
|
|
26783
|
-
const previous = workflowPointersFromMetadata(task2.metadata);
|
|
26784
|
-
const next = compactWorkflowPointers({
|
|
26785
|
-
current_workflow_invocation_id: pointerPatch(previous.current_workflow_invocation_id, input, "current_workflow_invocation_id"),
|
|
26786
|
-
current_run_id: pointerPatch(previous.current_run_id, input, "current_run_id"),
|
|
26787
|
-
latest_manifest_path: pointerPatch(previous.latest_manifest_path, input, "latest_manifest_path"),
|
|
26788
|
-
latest_evaluation_path: pointerPatch(previous.latest_evaluation_path, input, "latest_evaluation_path"),
|
|
26789
|
-
workflow_state: pointerPatch(previous.workflow_state, input, "workflow_state")
|
|
26790
|
-
});
|
|
26791
|
-
const timestamp2 = now();
|
|
26792
|
-
const {
|
|
26793
|
-
current_workflow_invocation_id,
|
|
26794
|
-
current_run_id,
|
|
26795
|
-
latest_manifest_path,
|
|
26796
|
-
latest_evaluation_path,
|
|
26797
|
-
workflow_state,
|
|
26798
|
-
workflow_invocation,
|
|
26799
|
-
...baseMetadata
|
|
26800
|
-
} = task2.metadata;
|
|
26801
|
-
const metadata = {
|
|
26802
|
-
...baseMetadata,
|
|
26803
|
-
...next,
|
|
26804
|
-
workflow_invocation: {
|
|
26805
|
-
schema_version: TASK_WORKFLOW_POINTER_SCHEMA_VERSION,
|
|
26806
|
-
...next,
|
|
26807
|
-
updated_at: timestamp2,
|
|
26808
|
-
updated_by: input.actor ?? null
|
|
26809
|
-
}
|
|
26810
|
-
};
|
|
26811
|
-
return updateTask(task2.id, { version: task2.version, metadata }, d);
|
|
26812
|
-
}
|
|
26813
|
-
function pointerPatch(previous, input, key) {
|
|
26814
|
-
if (!Object.prototype.hasOwnProperty.call(input, key))
|
|
26815
|
-
return previous;
|
|
26816
|
-
const value = input[key];
|
|
26817
|
-
if (value === undefined)
|
|
26818
|
-
return previous;
|
|
26819
|
-
return typeof value === "string" && value.trim() ? value : undefined;
|
|
26820
|
-
}
|
|
26821
27162
|
// src/db/api-keys.ts
|
|
26822
27163
|
init_database();
|
|
26823
|
-
import { createHash as
|
|
27164
|
+
import { createHash as createHash11, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual3 } from "crypto";
|
|
26824
27165
|
function rowToRecord(row) {
|
|
26825
27166
|
return {
|
|
26826
27167
|
id: row.id,
|
|
@@ -26834,7 +27175,7 @@ function rowToRecord(row) {
|
|
|
26834
27175
|
};
|
|
26835
27176
|
}
|
|
26836
27177
|
function hashApiKey(key) {
|
|
26837
|
-
return
|
|
27178
|
+
return createHash11("sha256").update(key).digest("hex");
|
|
26838
27179
|
}
|
|
26839
27180
|
function safeEqualHex(a, b) {
|
|
26840
27181
|
if (a.length !== b.length)
|
|
@@ -27025,18 +27366,18 @@ var gatherTrainingData = async (options = {}) => {
|
|
|
27025
27366
|
};
|
|
27026
27367
|
// src/lib/model-config.ts
|
|
27027
27368
|
init_sync_utils();
|
|
27028
|
-
import { existsSync as
|
|
27029
|
-
import { join as
|
|
27369
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
27370
|
+
import { join as join12 } from "path";
|
|
27030
27371
|
var DEFAULT_MODEL = "gpt-4o-mini";
|
|
27031
27372
|
function getConfigDir() {
|
|
27032
27373
|
return getTodosGlobalDir();
|
|
27033
27374
|
}
|
|
27034
27375
|
function getConfigPath2() {
|
|
27035
|
-
return
|
|
27376
|
+
return join12(getConfigDir(), "config.json");
|
|
27036
27377
|
}
|
|
27037
27378
|
function readConfig() {
|
|
27038
27379
|
const configPath = getConfigPath2();
|
|
27039
|
-
if (!
|
|
27380
|
+
if (!existsSync12(configPath))
|
|
27040
27381
|
return {};
|
|
27041
27382
|
try {
|
|
27042
27383
|
const raw = readFileSync9(configPath, "utf-8");
|
|
@@ -27047,7 +27388,7 @@ function readConfig() {
|
|
|
27047
27388
|
}
|
|
27048
27389
|
function writeConfig(config) {
|
|
27049
27390
|
const configDir = getConfigDir();
|
|
27050
|
-
if (!
|
|
27391
|
+
if (!existsSync12(configDir)) {
|
|
27051
27392
|
mkdirSync9(configDir, { recursive: true });
|
|
27052
27393
|
}
|
|
27053
27394
|
writeFileSync7(getConfigPath2(), JSON.stringify(config, null, 2) + `
|
|
@@ -27772,7 +28113,7 @@ CLI equivalent: \`${r.equivalent_cli}\`
|
|
|
27772
28113
|
`);
|
|
27773
28114
|
}
|
|
27774
28115
|
// src/lib/verification-providers.ts
|
|
27775
|
-
import { existsSync as
|
|
28116
|
+
import { existsSync as existsSync13, readFileSync as readFileSync10 } from "fs";
|
|
27776
28117
|
init_database();
|
|
27777
28118
|
init_config();
|
|
27778
28119
|
init_redaction();
|
|
@@ -27883,7 +28224,7 @@ function classifyLog(text) {
|
|
|
27883
28224
|
async function sleep2(ms) {
|
|
27884
28225
|
if (ms <= 0)
|
|
27885
28226
|
return;
|
|
27886
|
-
await new Promise((
|
|
28227
|
+
await new Promise((resolve13) => setTimeout(resolve13, ms));
|
|
27887
28228
|
}
|
|
27888
28229
|
async function runCommandProvider(provider, input) {
|
|
27889
28230
|
const commandTemplate = input.command || provider.command;
|
|
@@ -27938,7 +28279,7 @@ Timed out after ${provider.timeout_ms}ms`);
|
|
|
27938
28279
|
};
|
|
27939
28280
|
}
|
|
27940
28281
|
function runCiLogProvider(input) {
|
|
27941
|
-
const text = input.log_text ?? (input.log_path &&
|
|
28282
|
+
const text = input.log_text ?? (input.log_path && existsSync13(input.log_path) ? readFileSync10(input.log_path, "utf-8") : "");
|
|
27942
28283
|
return {
|
|
27943
28284
|
status: classifyLog(text),
|
|
27944
28285
|
attempts: 1,
|
|
@@ -27950,7 +28291,7 @@ function runBrowserProvider(input) {
|
|
|
27950
28291
|
if (!input.artifact_path) {
|
|
27951
28292
|
return { status: "unknown", attempts: 1, exit_code: null, output_summary: "browser provider needs a screenshot or artifact path" };
|
|
27952
28293
|
}
|
|
27953
|
-
if (!
|
|
28294
|
+
if (!existsSync13(input.artifact_path)) {
|
|
27954
28295
|
return { status: "failed", attempts: 1, exit_code: null, output_summary: `artifact not found: ${input.artifact_path}` };
|
|
27955
28296
|
}
|
|
27956
28297
|
return {
|
|
@@ -28115,7 +28456,7 @@ function listVerificationRecords(filter = {}, db) {
|
|
|
28115
28456
|
// src/lib/verification-evidence.ts
|
|
28116
28457
|
init_database();
|
|
28117
28458
|
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync10 } from "fs";
|
|
28118
|
-
import { dirname as
|
|
28459
|
+
import { dirname as dirname9 } from "path";
|
|
28119
28460
|
var VERIFICATION_EVIDENCE_SCHEMA = "todos.verification_evidence.v1";
|
|
28120
28461
|
function getMachineId3() {
|
|
28121
28462
|
return process.env["TODOS_MACHINE_ID"] || __require("os").hostname();
|
|
@@ -28235,15 +28576,15 @@ function exportVerificationEvidence(filter = {}, db) {
|
|
|
28235
28576
|
};
|
|
28236
28577
|
}
|
|
28237
28578
|
function writeVerificationExport(bundle, path) {
|
|
28238
|
-
mkdirSync10(
|
|
28579
|
+
mkdirSync10(dirname9(path), { recursive: true });
|
|
28239
28580
|
writeFileSync8(path, JSON.stringify(bundle, null, 2), "utf8");
|
|
28240
28581
|
}
|
|
28241
28582
|
// src/lib/policy-packs.ts
|
|
28242
|
-
import { relative as relative3, resolve as
|
|
28583
|
+
import { relative as relative3, resolve as resolve13 } from "path";
|
|
28243
28584
|
init_database();
|
|
28244
28585
|
init_config();
|
|
28245
|
-
function
|
|
28246
|
-
return
|
|
28586
|
+
function normalizePath4(path) {
|
|
28587
|
+
return resolve13(path);
|
|
28247
28588
|
}
|
|
28248
28589
|
function unique4(values) {
|
|
28249
28590
|
return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
|
|
@@ -28253,13 +28594,13 @@ function parseStatuses(values) {
|
|
|
28253
28594
|
return unique4(values).filter((value) => allowed.has(value));
|
|
28254
28595
|
}
|
|
28255
28596
|
function configuredPacks(config = loadConfig()) {
|
|
28256
|
-
return Object.values(config.policy_packs || {}).map((pack) => ({ ...pack, root:
|
|
28597
|
+
return Object.values(config.policy_packs || {}).map((pack) => ({ ...pack, root: normalizePath4(pack.root) })).sort((a, b) => a.name.localeCompare(b.name));
|
|
28257
28598
|
}
|
|
28258
28599
|
function defaultPolicyPack(name, root) {
|
|
28259
28600
|
return {
|
|
28260
28601
|
name,
|
|
28261
28602
|
version: 1,
|
|
28262
|
-
root:
|
|
28603
|
+
root: normalizePath4(root),
|
|
28263
28604
|
required_commands: [],
|
|
28264
28605
|
prohibited_commands: ["npm install -g", "git reset --hard", "git checkout --", "rm -rf"],
|
|
28265
28606
|
prohibited_paths: [],
|
|
@@ -28277,7 +28618,7 @@ function isPathInside3(root, path) {
|
|
|
28277
28618
|
const rel = relative3(root, path);
|
|
28278
28619
|
return rel === "" || !rel.startsWith("..") && !rel.startsWith("/") && !/^[A-Za-z]:/.test(rel);
|
|
28279
28620
|
}
|
|
28280
|
-
function
|
|
28621
|
+
function matchesPattern4(value, pattern) {
|
|
28281
28622
|
const normalizedValue = value.toLowerCase();
|
|
28282
28623
|
const normalizedPattern = pattern.toLowerCase();
|
|
28283
28624
|
if (pattern.startsWith("/") && pattern.endsWith("/") && pattern.length > 2) {
|
|
@@ -28294,14 +28635,14 @@ function matchesPattern3(value, pattern) {
|
|
|
28294
28635
|
return normalizedValue === normalizedPattern || normalizedValue.includes(normalizedPattern);
|
|
28295
28636
|
}
|
|
28296
28637
|
function commandMatches(commands, pattern) {
|
|
28297
|
-
return commands.filter((command) =>
|
|
28638
|
+
return commands.filter((command) => matchesPattern4(command, pattern));
|
|
28298
28639
|
}
|
|
28299
28640
|
function pathMatches(paths, pattern, root) {
|
|
28300
28641
|
return paths.filter((path) => {
|
|
28301
|
-
const candidate = path.startsWith("/") ? path :
|
|
28642
|
+
const candidate = path.startsWith("/") ? path : resolve13(root, path);
|
|
28302
28643
|
if (!isPathInside3(root, candidate))
|
|
28303
|
-
return
|
|
28304
|
-
return
|
|
28644
|
+
return matchesPattern4(path, pattern);
|
|
28645
|
+
return matchesPattern4(path, pattern) || matchesPattern4(relative3(root, candidate), pattern);
|
|
28305
28646
|
});
|
|
28306
28647
|
}
|
|
28307
28648
|
function finding(id, passed, message, evidence = []) {
|
|
@@ -28380,7 +28721,7 @@ function getPolicyPack(name) {
|
|
|
28380
28721
|
function upsertPolicyPack(input) {
|
|
28381
28722
|
const config = loadConfig();
|
|
28382
28723
|
const existing = config.policy_packs?.[input.name];
|
|
28383
|
-
const root =
|
|
28724
|
+
const root = normalizePath4(input.root || existing?.root || process.cwd());
|
|
28384
28725
|
const base = existing || defaultPolicyPack(input.name, root);
|
|
28385
28726
|
const timestamp2 = new Date().toISOString();
|
|
28386
28727
|
const pack = {
|
|
@@ -28460,7 +28801,7 @@ function validatePolicyPack(input, db) {
|
|
|
28460
28801
|
findings.push(finding("linked-pull-request", refs.length > 0, "at least one linked pull request is required", refs.map((ref) => ref.name)));
|
|
28461
28802
|
}
|
|
28462
28803
|
if (pack.branch_pattern) {
|
|
28463
|
-
const branches = evidence.gitRefs.filter((ref) => ref.ref_type === "branch" &&
|
|
28804
|
+
const branches = evidence.gitRefs.filter((ref) => ref.ref_type === "branch" && matchesPattern4(ref.name, pack.branch_pattern));
|
|
28464
28805
|
findings.push(finding("branch-pattern", branches.length > 0, `at least one linked branch must match: ${pack.branch_pattern}`, branches.map((ref) => ref.name)));
|
|
28465
28806
|
}
|
|
28466
28807
|
if (pack.require_approval) {
|
|
@@ -28589,21 +28930,21 @@ function resourceDiagnostics() {
|
|
|
28589
28930
|
};
|
|
28590
28931
|
}
|
|
28591
28932
|
// src/lib/sandbox-profiles.ts
|
|
28592
|
-
import { existsSync as
|
|
28593
|
-
import { join as
|
|
28933
|
+
import { existsSync as existsSync14, readFileSync as readFileSync11, writeFileSync as writeFileSync9, mkdirSync as mkdirSync11 } from "fs";
|
|
28934
|
+
import { join as join13, dirname as dirname10 } from "path";
|
|
28594
28935
|
var SANDBOX_PROFILE_VERSION = "todos.sandbox-profile.v1";
|
|
28595
28936
|
function getProfilesPath() {
|
|
28596
28937
|
if (process.env["TODOS_SANDBOX_PROFILES_PATH"]) {
|
|
28597
28938
|
return process.env["TODOS_SANDBOX_PROFILES_PATH"];
|
|
28598
28939
|
}
|
|
28599
|
-
const localDir =
|
|
28600
|
-
const local =
|
|
28601
|
-
if (
|
|
28940
|
+
const localDir = join13(process.cwd(), ".todos");
|
|
28941
|
+
const local = join13(localDir, "sandbox-profiles.json");
|
|
28942
|
+
if (existsSync14(localDir))
|
|
28602
28943
|
return local;
|
|
28603
|
-
if (
|
|
28944
|
+
if (existsSync14(local))
|
|
28604
28945
|
return local;
|
|
28605
28946
|
const home = process.env["HOME"] || "~";
|
|
28606
|
-
return
|
|
28947
|
+
return join13(home, ".hasna", "todos", "sandbox-profiles.json");
|
|
28607
28948
|
}
|
|
28608
28949
|
var cached2 = null;
|
|
28609
28950
|
function resetSandboxProfileCache() {
|
|
@@ -28635,7 +28976,7 @@ function loadSandboxProfiles() {
|
|
|
28635
28976
|
if (cached2)
|
|
28636
28977
|
return cached2;
|
|
28637
28978
|
const path = getProfilesPath();
|
|
28638
|
-
if (!
|
|
28979
|
+
if (!existsSync14(path)) {
|
|
28639
28980
|
cached2 = getDefaultSandboxProfiles();
|
|
28640
28981
|
return cached2;
|
|
28641
28982
|
}
|
|
@@ -28648,7 +28989,7 @@ function getSandboxProfile(name) {
|
|
|
28648
28989
|
}
|
|
28649
28990
|
function saveSandboxProfiles(profiles) {
|
|
28650
28991
|
const path = getProfilesPath();
|
|
28651
|
-
mkdirSync11(
|
|
28992
|
+
mkdirSync11(dirname10(path), { recursive: true });
|
|
28652
28993
|
writeFileSync9(path, JSON.stringify({ schema_version: SANDBOX_PROFILE_VERSION, profiles }, null, 2));
|
|
28653
28994
|
cached2 = profiles;
|
|
28654
28995
|
}
|
|
@@ -29008,9 +29349,9 @@ function getDefaultAgentAdapters() {
|
|
|
29008
29349
|
}
|
|
29009
29350
|
function resetAgentAdapterCache() {}
|
|
29010
29351
|
// src/lib/git-traceability.ts
|
|
29011
|
-
import { existsSync as
|
|
29352
|
+
import { existsSync as existsSync15, readFileSync as readFileSync12 } from "fs";
|
|
29012
29353
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
29013
|
-
import { resolve as
|
|
29354
|
+
import { resolve as resolve14 } from "path";
|
|
29014
29355
|
var GIT_TRACEABILITY_SCHEMA_VERSION = "todos.git_traceability.v1";
|
|
29015
29356
|
function runGit(args, cwd) {
|
|
29016
29357
|
const result = spawnSync2("git", args, { cwd, encoding: "utf8" });
|
|
@@ -29053,8 +29394,8 @@ function inspectGitCommit(sha, cwd) {
|
|
|
29053
29394
|
};
|
|
29054
29395
|
}
|
|
29055
29396
|
function loadCiSnapshot(path) {
|
|
29056
|
-
const target = path ?
|
|
29057
|
-
if (!
|
|
29397
|
+
const target = path ? resolve14(path) : resolve14(process.cwd(), ".todos", "ci-snapshot.json");
|
|
29398
|
+
if (!existsSync15(target))
|
|
29058
29399
|
return null;
|
|
29059
29400
|
try {
|
|
29060
29401
|
const parsed = JSON.parse(readFileSync12(target, "utf8"));
|
|
@@ -29150,8 +29491,8 @@ function formatTraceabilityReport(report) {
|
|
|
29150
29491
|
`);
|
|
29151
29492
|
}
|
|
29152
29493
|
// src/lib/mention-resolver.ts
|
|
29153
|
-
import { existsSync as
|
|
29154
|
-
import { basename as
|
|
29494
|
+
import { existsSync as existsSync16, readdirSync as readdirSync3, readFileSync as readFileSync13, statSync as statSync5 } from "fs";
|
|
29495
|
+
import { basename as basename4, isAbsolute, join as join14, relative as relative4, resolve as resolve15, sep as sep2 } from "path";
|
|
29155
29496
|
init_database();
|
|
29156
29497
|
var PREFIXES = {
|
|
29157
29498
|
file: "file",
|
|
@@ -29227,7 +29568,7 @@ function backlink(kind, key, label, target = key) {
|
|
|
29227
29568
|
return { kind, key, label, target };
|
|
29228
29569
|
}
|
|
29229
29570
|
function normalizeWorkspace(workspace) {
|
|
29230
|
-
return
|
|
29571
|
+
return resolve15(workspace || process.cwd());
|
|
29231
29572
|
}
|
|
29232
29573
|
function isInside(root, absolutePath) {
|
|
29233
29574
|
const rel = relative4(root, absolutePath);
|
|
@@ -29295,18 +29636,18 @@ function resolveFile(parsed, workspace) {
|
|
|
29295
29636
|
resolution.warnings.push("path is empty or escapes the workspace");
|
|
29296
29637
|
return resolution;
|
|
29297
29638
|
}
|
|
29298
|
-
const absolutePath =
|
|
29639
|
+
const absolutePath = resolve15(workspace, relPath);
|
|
29299
29640
|
if (!isInside(workspace, absolutePath)) {
|
|
29300
29641
|
resolution.path = relPath;
|
|
29301
29642
|
resolution.warnings.push("path escapes the workspace");
|
|
29302
29643
|
return resolution;
|
|
29303
29644
|
}
|
|
29304
29645
|
resolution.path = relPath;
|
|
29305
|
-
if (!
|
|
29646
|
+
if (!existsSync16(absolutePath)) {
|
|
29306
29647
|
resolution.warnings.push("file does not exist in the local workspace");
|
|
29307
29648
|
return resolution;
|
|
29308
29649
|
}
|
|
29309
|
-
const stats2 =
|
|
29650
|
+
const stats2 = statSync5(absolutePath);
|
|
29310
29651
|
if (!stats2.isFile()) {
|
|
29311
29652
|
resolution.warnings.push("path exists but is not a file");
|
|
29312
29653
|
return resolution;
|
|
@@ -29330,12 +29671,12 @@ function resolveFile(parsed, workspace) {
|
|
|
29330
29671
|
function walkSourceFiles(root, current = root, files = []) {
|
|
29331
29672
|
if (files.length >= 5000)
|
|
29332
29673
|
return files;
|
|
29333
|
-
for (const entry2 of
|
|
29674
|
+
for (const entry2 of readdirSync3(current, { withFileTypes: true })) {
|
|
29334
29675
|
if (entry2.name.startsWith(".") && ![".github"].includes(entry2.name)) {
|
|
29335
29676
|
if (SKIP_DIRS.has(entry2.name))
|
|
29336
29677
|
continue;
|
|
29337
29678
|
}
|
|
29338
|
-
const absolutePath =
|
|
29679
|
+
const absolutePath = join14(current, entry2.name);
|
|
29339
29680
|
if (entry2.isDirectory()) {
|
|
29340
29681
|
if (!SKIP_DIRS.has(entry2.name))
|
|
29341
29682
|
walkSourceFiles(root, absolutePath, files);
|
|
@@ -29343,8 +29684,8 @@ function walkSourceFiles(root, current = root, files = []) {
|
|
|
29343
29684
|
}
|
|
29344
29685
|
if (!entry2.isFile())
|
|
29345
29686
|
continue;
|
|
29346
|
-
const extension = `.${
|
|
29347
|
-
if (SOURCE_EXTENSIONS.has(extension) &&
|
|
29687
|
+
const extension = `.${basename4(entry2.name).split(".").pop() || ""}`;
|
|
29688
|
+
if (SOURCE_EXTENSIONS.has(extension) && statSync5(absolutePath).size <= 512 * 1024) {
|
|
29348
29689
|
files.push(absolutePath);
|
|
29349
29690
|
}
|
|
29350
29691
|
}
|
|
@@ -31215,9 +31556,9 @@ function getAdapterDocsFingerprint() {
|
|
|
31215
31556
|
}
|
|
31216
31557
|
// src/lib/inbox-intake.ts
|
|
31217
31558
|
init_database();
|
|
31218
|
-
import { existsSync as
|
|
31219
|
-
import { basename as
|
|
31220
|
-
import { createHash as
|
|
31559
|
+
import { existsSync as existsSync17, readFileSync as readFileSync14 } from "fs";
|
|
31560
|
+
import { basename as basename5 } from "path";
|
|
31561
|
+
import { createHash as createHash12 } from "crypto";
|
|
31221
31562
|
init_secret_redaction();
|
|
31222
31563
|
var INBOX_INTAKE_SCHEMA = "todos.inbox_intake.v1";
|
|
31223
31564
|
var INTAKE_SOURCE_TYPES = [
|
|
@@ -31230,7 +31571,7 @@ var INTAKE_SOURCE_TYPES = [
|
|
|
31230
31571
|
];
|
|
31231
31572
|
var INTAKE_TRIAGE_STATUSES = ["preview", "triaged", "duplicate", "created"];
|
|
31232
31573
|
function fingerprint2(text) {
|
|
31233
|
-
return
|
|
31574
|
+
return createHash12("sha256").update(text).digest("hex").slice(0, 16);
|
|
31234
31575
|
}
|
|
31235
31576
|
function loadRawContent(input) {
|
|
31236
31577
|
if (input.github_url) {
|
|
@@ -31261,15 +31602,15 @@ function loadRawContent(input) {
|
|
|
31261
31602
|
}
|
|
31262
31603
|
}
|
|
31263
31604
|
if (input.file_path) {
|
|
31264
|
-
if (!
|
|
31605
|
+
if (!existsSync17(input.file_path))
|
|
31265
31606
|
throw new Error(`File not found: ${input.file_path}`);
|
|
31266
31607
|
const raw = readFileSync14(input.file_path, "utf8");
|
|
31267
|
-
const name =
|
|
31608
|
+
const name = basename5(input.file_path).toLowerCase();
|
|
31268
31609
|
const source_type2 = input.source_type ?? (name.includes("ci") || name.endsWith(".log") ? "ci_log" : "file");
|
|
31269
31610
|
return {
|
|
31270
31611
|
raw,
|
|
31271
31612
|
source_type: source_type2,
|
|
31272
|
-
metadata: { file_path: input.file_path, file_name:
|
|
31613
|
+
metadata: { file_path: input.file_path, file_name: basename5(input.file_path) }
|
|
31273
31614
|
};
|
|
31274
31615
|
}
|
|
31275
31616
|
const text = input.text?.trim();
|
|
@@ -31879,7 +32220,7 @@ function formatNlIntakePreviewText(preview) {
|
|
|
31879
32220
|
}
|
|
31880
32221
|
// src/lib/issue-importers.ts
|
|
31881
32222
|
init_database();
|
|
31882
|
-
import { existsSync as
|
|
32223
|
+
import { existsSync as existsSync18, readFileSync as readFileSync15 } from "fs";
|
|
31883
32224
|
var ISSUE_IMPORT_SCHEMA = "todos.issue_import.v1";
|
|
31884
32225
|
var ISSUE_SOURCES = ["github", "linear", "jira", "auto"];
|
|
31885
32226
|
var GITHUB_LABEL_PRIORITY = {
|
|
@@ -32098,7 +32439,7 @@ function parseIssueExport(data, source9 = "auto") {
|
|
|
32098
32439
|
return normalized;
|
|
32099
32440
|
}
|
|
32100
32441
|
function loadIssueExportFromFile(path) {
|
|
32101
|
-
if (!
|
|
32442
|
+
if (!existsSync18(path))
|
|
32102
32443
|
throw new Error(`File not found: ${path}`);
|
|
32103
32444
|
return JSON.parse(readFileSync15(path, "utf8"));
|
|
32104
32445
|
}
|
|
@@ -32258,8 +32599,8 @@ todos import issues ./linear.json --source linear --dry-run
|
|
|
32258
32599
|
// src/lib/run-records.ts
|
|
32259
32600
|
init_database();
|
|
32260
32601
|
init_secret_redaction();
|
|
32261
|
-
import { existsSync as
|
|
32262
|
-
import { join as
|
|
32602
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
32603
|
+
import { join as join15, dirname as dirname11 } from "path";
|
|
32263
32604
|
var RUN_RECORD_SCHEMA = "todos.run_record.v1";
|
|
32264
32605
|
var RUN_RECORD_STATUSES = ["active", "completed", "failed", "archived"];
|
|
32265
32606
|
function parseJsonArray3(raw, fallback = []) {
|
|
@@ -32452,8 +32793,8 @@ function buildRunReplayBundle(id, db) {
|
|
|
32452
32793
|
}
|
|
32453
32794
|
function exportRunReplay(id, outputPath, db) {
|
|
32454
32795
|
const bundle = buildRunReplayBundle(id, db);
|
|
32455
|
-
const path = outputPath ??
|
|
32456
|
-
mkdirSync12(
|
|
32796
|
+
const path = outputPath ?? join15(process.cwd(), ".todos", "replays", `${id.slice(0, 8)}.json`);
|
|
32797
|
+
mkdirSync12(dirname11(path), { recursive: true });
|
|
32457
32798
|
writeFileSync10(path, JSON.stringify(bundle, null, 2));
|
|
32458
32799
|
const d = db || getDatabase();
|
|
32459
32800
|
d.run(`UPDATE run_records SET replay_bundle = ?, updated_at = ? WHERE id = ?`, [path, now(), id]);
|
|
@@ -32492,16 +32833,16 @@ function formatRunRecordMarkdown(record) {
|
|
|
32492
32833
|
`;
|
|
32493
32834
|
}
|
|
32494
32835
|
function getDefaultReplayDir() {
|
|
32495
|
-
const local =
|
|
32496
|
-
if (
|
|
32836
|
+
const local = join15(process.cwd(), ".todos", "replays");
|
|
32837
|
+
if (existsSync19(join15(process.cwd(), ".todos")))
|
|
32497
32838
|
return local;
|
|
32498
32839
|
const home = process.env["HOME"] || "~";
|
|
32499
|
-
return
|
|
32840
|
+
return join15(home, ".hasna", "todos", "replays");
|
|
32500
32841
|
}
|
|
32501
32842
|
// src/lib/release-checks.ts
|
|
32502
32843
|
init_secret_redaction();
|
|
32503
|
-
import { existsSync as
|
|
32504
|
-
import { join as
|
|
32844
|
+
import { existsSync as existsSync20, readFileSync as readFileSync16, readdirSync as readdirSync4, statSync as statSync6 } from "fs";
|
|
32845
|
+
import { join as join16, relative as relative5 } from "path";
|
|
32505
32846
|
var RELEASE_CHECK_SCHEMA = "todos.release_check.v1";
|
|
32506
32847
|
var FORBIDDEN_DIST_PATTERNS = [
|
|
32507
32848
|
{
|
|
@@ -32514,17 +32855,17 @@ var FORBIDDEN_DIST_PATTERNS = [
|
|
|
32514
32855
|
];
|
|
32515
32856
|
var REQUIRED_BINS = ["todos", "todos-mcp", "todos-serve"];
|
|
32516
32857
|
function readPackageJson3(root) {
|
|
32517
|
-
const path =
|
|
32518
|
-
if (!
|
|
32858
|
+
const path = join16(root, "package.json");
|
|
32859
|
+
if (!existsSync20(path))
|
|
32519
32860
|
throw new Error(`package.json not found in ${root}`);
|
|
32520
32861
|
return JSON.parse(readFileSync16(path, "utf8"));
|
|
32521
32862
|
}
|
|
32522
32863
|
function walkFiles(dir, acc = []) {
|
|
32523
|
-
if (!
|
|
32864
|
+
if (!existsSync20(dir))
|
|
32524
32865
|
return acc;
|
|
32525
|
-
for (const entry2 of
|
|
32526
|
-
const full =
|
|
32527
|
-
const st =
|
|
32866
|
+
for (const entry2 of readdirSync4(dir)) {
|
|
32867
|
+
const full = join16(dir, entry2);
|
|
32868
|
+
const st = statSync6(full);
|
|
32528
32869
|
if (st.isDirectory())
|
|
32529
32870
|
walkFiles(full, acc);
|
|
32530
32871
|
else if (/\.(js|mjs|cjs|json|d\.ts)$/.test(entry2))
|
|
@@ -32541,8 +32882,8 @@ function auditPackageContents(root) {
|
|
|
32541
32882
|
checks.push({ id: "files_dist", severity: "error", message: "package.json files must include dist" });
|
|
32542
32883
|
}
|
|
32543
32884
|
for (const pattern of files) {
|
|
32544
|
-
const target =
|
|
32545
|
-
if (!
|
|
32885
|
+
const target = join16(root, pattern);
|
|
32886
|
+
if (!existsSync20(target)) {
|
|
32546
32887
|
checks.push({ id: `files_missing_${pattern}`, severity: "error", message: `Published file path missing: ${pattern}` });
|
|
32547
32888
|
}
|
|
32548
32889
|
}
|
|
@@ -32556,8 +32897,8 @@ function auditPackageContents(root) {
|
|
|
32556
32897
|
checks.push({ id: `bin_${name}`, severity: "error", message: `Missing bin entry: ${name}` });
|
|
32557
32898
|
continue;
|
|
32558
32899
|
}
|
|
32559
|
-
const binPath =
|
|
32560
|
-
if (!
|
|
32900
|
+
const binPath = join16(root, rel);
|
|
32901
|
+
if (!existsSync20(binPath)) {
|
|
32561
32902
|
checks.push({ id: `bin_path_${name}`, severity: "error", message: `Bin file missing: ${rel}` });
|
|
32562
32903
|
} else {
|
|
32563
32904
|
checks.push({ id: `bin_ok_${name}`, severity: "info", message: `Bin present: ${name} \u2192 ${rel}` });
|
|
@@ -32574,8 +32915,8 @@ function auditPackageContents(root) {
|
|
|
32574
32915
|
}
|
|
32575
32916
|
function scanDistArtifacts(root) {
|
|
32576
32917
|
const checks = [];
|
|
32577
|
-
const distDir =
|
|
32578
|
-
if (!
|
|
32918
|
+
const distDir = join16(root, "dist");
|
|
32919
|
+
if (!existsSync20(distDir)) {
|
|
32579
32920
|
checks.push({ id: "dist_missing", severity: "error", message: "dist/ directory not found \u2014 run bun run build" });
|
|
32580
32921
|
return checks;
|
|
32581
32922
|
}
|
|
@@ -32881,15 +33222,15 @@ function renderReleaseNotesMarkdown(document) {
|
|
|
32881
33222
|
// src/lib/db-backup.ts
|
|
32882
33223
|
init_database();
|
|
32883
33224
|
init_migrations();
|
|
32884
|
-
import { existsSync as
|
|
32885
|
-
import { dirname as
|
|
32886
|
-
import { Database as
|
|
33225
|
+
import { existsSync as existsSync21, copyFileSync, mkdirSync as mkdirSync13, readFileSync as readFileSync17, statSync as statSync7, writeFileSync as writeFileSync11, unlinkSync } from "fs";
|
|
33226
|
+
import { dirname as dirname12, join as join17, resolve as resolve16 } from "path";
|
|
33227
|
+
import { Database as Database4 } from "bun:sqlite";
|
|
32887
33228
|
var DB_BACKUP_SCHEMA = "todos.db_backup.v1";
|
|
32888
33229
|
function resolveDbPath(dbPath) {
|
|
32889
33230
|
if (dbPath)
|
|
32890
|
-
return
|
|
33231
|
+
return resolve16(dbPath);
|
|
32891
33232
|
if (process.env["TODOS_DB_PATH"] && process.env["TODOS_DB_PATH"] !== ":memory:") {
|
|
32892
|
-
return
|
|
33233
|
+
return resolve16(process.env["TODOS_DB_PATH"]);
|
|
32893
33234
|
}
|
|
32894
33235
|
const db = getDatabase();
|
|
32895
33236
|
const filename = db.filename;
|
|
@@ -32899,11 +33240,11 @@ function resolveDbPath(dbPath) {
|
|
|
32899
33240
|
}
|
|
32900
33241
|
function backupDatabase(outputPath, sourcePath) {
|
|
32901
33242
|
const source9 = resolveDbPath(sourcePath);
|
|
32902
|
-
if (!
|
|
33243
|
+
if (!existsSync21(source9))
|
|
32903
33244
|
throw new Error(`Database not found: ${source9}`);
|
|
32904
|
-
mkdirSync13(
|
|
33245
|
+
mkdirSync13(dirname12(outputPath), { recursive: true });
|
|
32905
33246
|
closeDatabase();
|
|
32906
|
-
const src = new
|
|
33247
|
+
const src = new Database4(source9);
|
|
32907
33248
|
try {
|
|
32908
33249
|
src.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
32909
33250
|
} catch {}
|
|
@@ -32911,7 +33252,7 @@ function backupDatabase(outputPath, sourcePath) {
|
|
|
32911
33252
|
src.close();
|
|
32912
33253
|
writeFileSync11(outputPath, image);
|
|
32913
33254
|
const method = "file_copy";
|
|
32914
|
-
const bytes =
|
|
33255
|
+
const bytes = statSync7(outputPath).size;
|
|
32915
33256
|
return {
|
|
32916
33257
|
schema_version: DB_BACKUP_SCHEMA,
|
|
32917
33258
|
source_path: source9,
|
|
@@ -32922,14 +33263,14 @@ function backupDatabase(outputPath, sourcePath) {
|
|
|
32922
33263
|
};
|
|
32923
33264
|
}
|
|
32924
33265
|
function restoreDatabase(backupPath, targetPath) {
|
|
32925
|
-
if (!
|
|
33266
|
+
if (!existsSync21(backupPath))
|
|
32926
33267
|
throw new Error(`Backup not found: ${backupPath}`);
|
|
32927
33268
|
const integrity = checkDatabaseIntegrity(backupPath);
|
|
32928
33269
|
if (!integrity.ok) {
|
|
32929
33270
|
throw new Error(`Backup failed integrity check: ${integrity.errors.join("; ")}`);
|
|
32930
33271
|
}
|
|
32931
|
-
const target = targetPath ?
|
|
32932
|
-
mkdirSync13(
|
|
33272
|
+
const target = targetPath ? resolve16(targetPath) : resolveDbPath();
|
|
33273
|
+
mkdirSync13(dirname12(target), { recursive: true });
|
|
32933
33274
|
const staging = `${target}.restore.tmp`;
|
|
32934
33275
|
copyFileSync(backupPath, staging);
|
|
32935
33276
|
copyFileSync(staging, target);
|
|
@@ -32941,15 +33282,15 @@ function restoreDatabase(backupPath, targetPath) {
|
|
|
32941
33282
|
schema_version: DB_BACKUP_SCHEMA,
|
|
32942
33283
|
source_path: backupPath,
|
|
32943
33284
|
backup_path: target,
|
|
32944
|
-
bytes:
|
|
33285
|
+
bytes: statSync7(target).size,
|
|
32945
33286
|
method: "file_copy",
|
|
32946
33287
|
created_at: new Date().toISOString()
|
|
32947
33288
|
};
|
|
32948
33289
|
}
|
|
32949
33290
|
function checkDatabaseIntegrity(dbPath) {
|
|
32950
|
-
const path = dbPath ?
|
|
33291
|
+
const path = dbPath ? resolve16(dbPath) : resolveDbPath();
|
|
32951
33292
|
const errors = [];
|
|
32952
|
-
if (!
|
|
33293
|
+
if (!existsSync21(path)) {
|
|
32953
33294
|
return {
|
|
32954
33295
|
schema_version: DB_BACKUP_SCHEMA,
|
|
32955
33296
|
path,
|
|
@@ -32962,7 +33303,7 @@ function checkDatabaseIntegrity(dbPath) {
|
|
|
32962
33303
|
}
|
|
32963
33304
|
let db;
|
|
32964
33305
|
try {
|
|
32965
|
-
db = new
|
|
33306
|
+
db = new Database4(path, { readonly: true });
|
|
32966
33307
|
} catch (e) {
|
|
32967
33308
|
return {
|
|
32968
33309
|
schema_version: DB_BACKUP_SCHEMA,
|
|
@@ -33012,18 +33353,18 @@ function checkDatabaseIntegrity(dbPath) {
|
|
|
33012
33353
|
};
|
|
33013
33354
|
}
|
|
33014
33355
|
function compactDatabase(dbPath) {
|
|
33015
|
-
const path = dbPath ?
|
|
33016
|
-
const before =
|
|
33017
|
-
const db = new
|
|
33356
|
+
const path = dbPath ? resolve16(dbPath) : resolveDbPath();
|
|
33357
|
+
const before = statSync7(path).size;
|
|
33358
|
+
const db = new Database4(path);
|
|
33018
33359
|
db.exec("VACUUM");
|
|
33019
33360
|
db.close();
|
|
33020
|
-
const after =
|
|
33361
|
+
const after = statSync7(path).size;
|
|
33021
33362
|
closeDatabase();
|
|
33022
33363
|
return { path, bytes_before: before, bytes_after: after };
|
|
33023
33364
|
}
|
|
33024
33365
|
function migrationDryRun(dbPath) {
|
|
33025
|
-
const path = dbPath ?
|
|
33026
|
-
const db = new
|
|
33366
|
+
const path = dbPath ? resolve16(dbPath) : resolveDbPath();
|
|
33367
|
+
const db = new Database4(path, { readonly: true });
|
|
33027
33368
|
let current = 0;
|
|
33028
33369
|
try {
|
|
33029
33370
|
const row = db.query("SELECT MAX(id) as id FROM _migrations").get();
|
|
@@ -33046,13 +33387,13 @@ function migrationDryRun(dbPath) {
|
|
|
33046
33387
|
};
|
|
33047
33388
|
}
|
|
33048
33389
|
function defaultBackupPath(dbPath) {
|
|
33049
|
-
const base = dbPath ?
|
|
33390
|
+
const base = dbPath ? dirname12(resolve16(dbPath)) : dirname12(resolveDbPath());
|
|
33050
33391
|
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
33051
|
-
return
|
|
33392
|
+
return join17(base, "backups", `todos-${stamp}.db`);
|
|
33052
33393
|
}
|
|
33053
33394
|
function readBackupManifest(backupPath) {
|
|
33054
33395
|
const manifestPath = `${backupPath}.json`;
|
|
33055
|
-
if (!
|
|
33396
|
+
if (!existsSync21(manifestPath))
|
|
33056
33397
|
return null;
|
|
33057
33398
|
try {
|
|
33058
33399
|
return JSON.parse(readFileSync17(manifestPath, "utf8"));
|
|
@@ -33065,7 +33406,7 @@ function writeBackupManifest(backupPath, result) {
|
|
|
33065
33406
|
writeFileSyncSafe(manifestPath, JSON.stringify(result, null, 2));
|
|
33066
33407
|
}
|
|
33067
33408
|
function writeFileSyncSafe(path, content) {
|
|
33068
|
-
mkdirSync13(
|
|
33409
|
+
mkdirSync13(dirname12(path), { recursive: true });
|
|
33069
33410
|
writeFileSync11(path, content);
|
|
33070
33411
|
}
|
|
33071
33412
|
// src/lib/json-schemas.ts
|
|
@@ -33573,15 +33914,15 @@ ${SCHEMA_ENTITIES.map((e) => `- **${e}**: \`${JSON_SCHEMAS[e].schema_version}\``
|
|
|
33573
33914
|
}
|
|
33574
33915
|
function exportSchemasToDirectory(dir) {
|
|
33575
33916
|
const { mkdirSync: mkdirSync14, writeFileSync: writeFileSync12 } = __require("fs");
|
|
33576
|
-
const { join:
|
|
33917
|
+
const { join: join18 } = __require("path");
|
|
33577
33918
|
mkdirSync14(dir, { recursive: true });
|
|
33578
33919
|
const written = [];
|
|
33579
33920
|
for (const entity of SCHEMA_ENTITIES) {
|
|
33580
|
-
const path =
|
|
33921
|
+
const path = join18(dir, `${entity}.${JSON_SCHEMAS[entity].schema_version.replace(/\./g, "-")}.json`);
|
|
33581
33922
|
writeFileSync12(path, JSON.stringify(JSON_SCHEMAS[entity], null, 2));
|
|
33582
33923
|
written.push(path);
|
|
33583
33924
|
}
|
|
33584
|
-
const catalogPath =
|
|
33925
|
+
const catalogPath = join18(dir, "catalog.json");
|
|
33585
33926
|
writeFileSync12(catalogPath, JSON.stringify({
|
|
33586
33927
|
catalog_version: JSON_SCHEMA_CATALOG_VERSION,
|
|
33587
33928
|
semver: SCHEMA_SEMVER,
|
|
@@ -34482,7 +34823,7 @@ function getReminderDocs() {
|
|
|
34482
34823
|
// src/lib/import-export-bridge.ts
|
|
34483
34824
|
init_database();
|
|
34484
34825
|
import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, mkdirSync as mkdirSync14 } from "fs";
|
|
34485
|
-
import { dirname as
|
|
34826
|
+
import { dirname as dirname13 } from "path";
|
|
34486
34827
|
init_secret_redaction();
|
|
34487
34828
|
var BUNDLE_SCHEMA = "todos.bundle.v1";
|
|
34488
34829
|
var BUNDLE_TYPES = ["full_export", "tasks", "partial"];
|
|
@@ -34890,7 +35231,7 @@ function importBundle(bundle, options = {}, db) {
|
|
|
34890
35231
|
return result;
|
|
34891
35232
|
}
|
|
34892
35233
|
function writeBundleFile(bundle, path) {
|
|
34893
|
-
mkdirSync14(
|
|
35234
|
+
mkdirSync14(dirname13(path), { recursive: true });
|
|
34894
35235
|
writeFileSync12(path, JSON.stringify(bundle, null, 2), "utf8");
|
|
34895
35236
|
}
|
|
34896
35237
|
function readBundleFile(path) {
|
|
@@ -35371,7 +35712,7 @@ function createPlanWithSteps(name, steps, opts = {}, db) {
|
|
|
35371
35712
|
// src/lib/handoff-packets.ts
|
|
35372
35713
|
init_database();
|
|
35373
35714
|
import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync15 } from "fs";
|
|
35374
|
-
import { dirname as
|
|
35715
|
+
import { dirname as dirname14 } from "path";
|
|
35375
35716
|
var HANDOFF_PACKET_SCHEMA = "todos.handoff_packet.v1";
|
|
35376
35717
|
function summarizeTask2(t) {
|
|
35377
35718
|
return {
|
|
@@ -35545,7 +35886,7 @@ function formatHandoffPacket(packet, format = "json") {
|
|
|
35545
35886
|
function exportHandoffPacket(input = {}, path, db) {
|
|
35546
35887
|
const packet = createHandoffPacket(input, db);
|
|
35547
35888
|
if (path) {
|
|
35548
|
-
mkdirSync15(
|
|
35889
|
+
mkdirSync15(dirname14(path), { recursive: true });
|
|
35549
35890
|
writeFileSync13(path, formatHandoffPacket(packet, "json"), "utf8");
|
|
35550
35891
|
}
|
|
35551
35892
|
return packet;
|
|
@@ -36150,7 +36491,7 @@ function generateCliReferenceMarkdown() {
|
|
|
36150
36491
|
// src/db/builtin-templates.ts
|
|
36151
36492
|
init_database();
|
|
36152
36493
|
import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
|
|
36153
|
-
import { join as
|
|
36494
|
+
import { join as join18 } from "path";
|
|
36154
36495
|
var BUILTIN_TEMPLATE_LIBRARY_VERSION = "2026-05-21";
|
|
36155
36496
|
var BUILTIN_TEMPLATE_LIBRARY_SOURCE = "bundled-local-template-library";
|
|
36156
36497
|
var TEMPLATE_LIBRARY_SCHEMA = "todos.template_library.v1";
|
|
@@ -36426,7 +36767,7 @@ function writeBuiltinTemplateFiles(directory) {
|
|
|
36426
36767
|
mkdirSync16(directory, { recursive: true });
|
|
36427
36768
|
const files = [];
|
|
36428
36769
|
for (const entry2 of exportBuiltinTemplateFiles()) {
|
|
36429
|
-
const path =
|
|
36770
|
+
const path = join18(directory, entry2.filename);
|
|
36430
36771
|
writeFileSync14(path, `${JSON.stringify(entry2.template, null, 2)}
|
|
36431
36772
|
`, "utf-8");
|
|
36432
36773
|
files.push(path);
|
|
@@ -36471,7 +36812,7 @@ function initBuiltinTemplates(db) {
|
|
|
36471
36812
|
// src/lib/template-library.ts
|
|
36472
36813
|
init_database();
|
|
36473
36814
|
import { writeFileSync as writeFileSync15, readFileSync as readFileSync19, mkdirSync as mkdirSync17 } from "fs";
|
|
36474
|
-
import { dirname as
|
|
36815
|
+
import { dirname as dirname15 } from "path";
|
|
36475
36816
|
function listTemplateLibrary(db) {
|
|
36476
36817
|
const d = db || getDatabase();
|
|
36477
36818
|
const installed = new Set(listTemplates(d).map((t) => t.name));
|
|
@@ -36513,7 +36854,7 @@ function exportTemplateLibraryCatalog(path, db) {
|
|
|
36513
36854
|
templates: listTemplateLibrary(db)
|
|
36514
36855
|
};
|
|
36515
36856
|
if (path) {
|
|
36516
|
-
mkdirSync17(
|
|
36857
|
+
mkdirSync17(dirname15(path), { recursive: true });
|
|
36517
36858
|
writeFileSync15(path, JSON.stringify(catalog, null, 2), "utf8");
|
|
36518
36859
|
}
|
|
36519
36860
|
return catalog;
|
|
@@ -36573,11 +36914,11 @@ init_database();
|
|
|
36573
36914
|
init_machines();
|
|
36574
36915
|
import { hostname as hostname2 } from "os";
|
|
36575
36916
|
var MACHINE_TOPOLOGY_SCHEMA = "todos.machine_topology.v1";
|
|
36576
|
-
function
|
|
36917
|
+
function normalizePath5(p) {
|
|
36577
36918
|
return p.replace(/\\/g, "/").replace(/\/+$/, "").toLowerCase();
|
|
36578
36919
|
}
|
|
36579
36920
|
function detectCasingMismatch(stored, canonical) {
|
|
36580
|
-
return
|
|
36921
|
+
return normalizePath5(stored) === normalizePath5(canonical) && stored !== canonical;
|
|
36581
36922
|
}
|
|
36582
36923
|
function registerLocalMachine(db) {
|
|
36583
36924
|
resetMachineId();
|
|
@@ -36596,7 +36937,7 @@ function getPathOverrides(db) {
|
|
|
36596
36937
|
project_name: project?.name ?? row.project_id.slice(0, 8),
|
|
36597
36938
|
machine_id: row.machine_id,
|
|
36598
36939
|
path: row.path,
|
|
36599
|
-
path_normalized:
|
|
36940
|
+
path_normalized: normalizePath5(row.path),
|
|
36600
36941
|
casing_mismatch: detectCasingMismatch(row.path, canonical)
|
|
36601
36942
|
};
|
|
36602
36943
|
});
|
|
@@ -36708,10 +37049,10 @@ todos machines topology # full diagnostic report
|
|
|
36708
37049
|
`;
|
|
36709
37050
|
}
|
|
36710
37051
|
// src/lib/environment-snapshots.ts
|
|
36711
|
-
import { createHash as
|
|
36712
|
-
import { existsSync as
|
|
37052
|
+
import { createHash as createHash13 } from "crypto";
|
|
37053
|
+
import { existsSync as existsSync22, readFileSync as readFileSync20, statSync as statSync8 } from "fs";
|
|
36713
37054
|
import { hostname as hostname3, platform, arch } from "os";
|
|
36714
|
-
import { dirname as
|
|
37055
|
+
import { dirname as dirname16, join as join19, resolve as resolve17 } from "path";
|
|
36715
37056
|
import { tmpdir as tmpdir3 } from "os";
|
|
36716
37057
|
init_database();
|
|
36717
37058
|
init_redaction();
|
|
@@ -36733,13 +37074,13 @@ var CONFIG_FILES = [
|
|
|
36733
37074
|
"dashboard/vite.config.ts"
|
|
36734
37075
|
];
|
|
36735
37076
|
function sha2565(value) {
|
|
36736
|
-
return
|
|
37077
|
+
return createHash13("sha256").update(value).digest("hex");
|
|
36737
37078
|
}
|
|
36738
37079
|
function fileRecord(root, relativePath) {
|
|
36739
|
-
const path =
|
|
36740
|
-
if (!
|
|
37080
|
+
const path = join19(root, relativePath);
|
|
37081
|
+
if (!existsSync22(path))
|
|
36741
37082
|
return null;
|
|
36742
|
-
const stat =
|
|
37083
|
+
const stat = statSync8(path);
|
|
36743
37084
|
if (!stat.isFile())
|
|
36744
37085
|
return null;
|
|
36745
37086
|
const content = readFileSync20(path);
|
|
@@ -36749,7 +37090,7 @@ function manifestRecord(root, relativePath) {
|
|
|
36749
37090
|
const base = fileRecord(root, relativePath);
|
|
36750
37091
|
if (!base)
|
|
36751
37092
|
return null;
|
|
36752
|
-
const parsed = readJsonFile(
|
|
37093
|
+
const parsed = readJsonFile(join19(root, relativePath));
|
|
36753
37094
|
if (!parsed)
|
|
36754
37095
|
return { ...base, redacted: {} };
|
|
36755
37096
|
const redacted = redactValue({
|
|
@@ -36844,15 +37185,15 @@ function commandEnv(env, includeValues) {
|
|
|
36844
37185
|
function defaultSnapshotDir() {
|
|
36845
37186
|
const dbPath = getDatabasePath();
|
|
36846
37187
|
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
36847
|
-
return
|
|
36848
|
-
return
|
|
37188
|
+
return join19(tmpdir3(), "hasna-todos", "environment-snapshots");
|
|
37189
|
+
return join19(dirname16(resolve17(dbPath)), "environment-snapshots");
|
|
36849
37190
|
}
|
|
36850
37191
|
function snapshotWithId(snapshot) {
|
|
36851
37192
|
const digest = sha2565(JSON.stringify(snapshot)).slice(0, 24);
|
|
36852
37193
|
return { id: `env_${digest}`, ...snapshot };
|
|
36853
37194
|
}
|
|
36854
37195
|
function captureEnvironmentSnapshot(input = {}) {
|
|
36855
|
-
const root =
|
|
37196
|
+
const root = resolve17(input.root || process.cwd());
|
|
36856
37197
|
const env = input.env || process.env;
|
|
36857
37198
|
const warnings = [];
|
|
36858
37199
|
const manifests = MANIFEST_FILES.map((file) => manifestRecord(root, file)).filter((file) => Boolean(file));
|
|
@@ -36892,13 +37233,13 @@ function captureEnvironmentSnapshot(input = {}) {
|
|
|
36892
37233
|
});
|
|
36893
37234
|
}
|
|
36894
37235
|
function writeEnvironmentSnapshot(snapshot, outputPath) {
|
|
36895
|
-
const path = outputPath ?
|
|
36896
|
-
ensureDir2(
|
|
37236
|
+
const path = outputPath ? resolve17(outputPath) : join19(defaultSnapshotDir(), `${snapshot.id}.json`);
|
|
37237
|
+
ensureDir2(dirname16(path));
|
|
36897
37238
|
writeJsonFile(path, snapshot);
|
|
36898
37239
|
return path;
|
|
36899
37240
|
}
|
|
36900
37241
|
function readEnvironmentSnapshot(path) {
|
|
36901
|
-
const snapshot = readJsonFile(
|
|
37242
|
+
const snapshot = readJsonFile(resolve17(path));
|
|
36902
37243
|
if (!snapshot || snapshot.schema_version !== 1 || typeof snapshot.id !== "string") {
|
|
36903
37244
|
throw new Error(`Invalid environment snapshot: ${path}`);
|
|
36904
37245
|
}
|
|
@@ -36983,9 +37324,9 @@ function compareEnvironmentSnapshotFiles(leftPath, rightPath) {
|
|
|
36983
37324
|
}
|
|
36984
37325
|
// src/lib/decision-records.ts
|
|
36985
37326
|
init_database();
|
|
36986
|
-
import { createHash as
|
|
37327
|
+
import { createHash as createHash14 } from "crypto";
|
|
36987
37328
|
import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
|
|
36988
|
-
import { dirname as
|
|
37329
|
+
import { dirname as dirname17, join as join20 } from "path";
|
|
36989
37330
|
var DECISION_RECORD_SCHEMA = "todos.decision_record.v1";
|
|
36990
37331
|
var KNOWLEDGE_SNAPSHOT_SCHEMA = "todos.knowledge_snapshot.v1";
|
|
36991
37332
|
var DECISION_STATUSES = ["proposed", "accepted", "deprecated", "superseded", "rejected"];
|
|
@@ -37039,7 +37380,7 @@ function rowToDecisionRecord(row) {
|
|
|
37039
37380
|
}
|
|
37040
37381
|
function stableSnapshotHash(payload) {
|
|
37041
37382
|
const { captured_at: _capturedAt, ...rest } = payload;
|
|
37042
|
-
return
|
|
37383
|
+
return createHash14("sha256").update(JSON.stringify(rest)).digest("hex");
|
|
37043
37384
|
}
|
|
37044
37385
|
function createDecisionRecord(input, db) {
|
|
37045
37386
|
const d = db || getDatabase();
|
|
@@ -37215,8 +37556,8 @@ function exportDecisionRecord(id, outputPath, format = "markdown", db) {
|
|
|
37215
37556
|
if (!record)
|
|
37216
37557
|
throw new Error(`Decision record not found: ${id}`);
|
|
37217
37558
|
const content = format === "markdown" ? formatDecisionRecordMarkdown(record) : JSON.stringify(record, null, 2);
|
|
37218
|
-
const path = outputPath ??
|
|
37219
|
-
mkdirSync18(
|
|
37559
|
+
const path = outputPath ?? join20(process.cwd(), ".todos", "decisions", `${record.short_ref}.${format === "markdown" ? "md" : "json"}`);
|
|
37560
|
+
mkdirSync18(dirname17(path), { recursive: true });
|
|
37220
37561
|
writeFileSync16(path, content, "utf8");
|
|
37221
37562
|
return { path, content };
|
|
37222
37563
|
}
|
|
@@ -37363,8 +37704,8 @@ function exportKnowledgeSnapshot(id, outputPath, format = "markdown", db) {
|
|
|
37363
37704
|
throw new Error(`Knowledge snapshot not found: ${id}`);
|
|
37364
37705
|
const content = format === "markdown" ? formatKnowledgeSnapshotMarkdown(record) : JSON.stringify(record, null, 2);
|
|
37365
37706
|
const slug = record.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
37366
|
-
const path = outputPath ??
|
|
37367
|
-
mkdirSync18(
|
|
37707
|
+
const path = outputPath ?? join20(process.cwd(), ".todos", "knowledge", `${slug || record.id.slice(0, 8)}.${format === "markdown" ? "md" : "json"}`);
|
|
37708
|
+
mkdirSync18(dirname17(path), { recursive: true });
|
|
37368
37709
|
writeFileSync16(path, content, "utf8");
|
|
37369
37710
|
return { path, content };
|
|
37370
37711
|
}
|
|
@@ -37392,7 +37733,7 @@ Schema versions:
|
|
|
37392
37733
|
// src/lib/report-exports.ts
|
|
37393
37734
|
init_database();
|
|
37394
37735
|
import { writeFileSync as writeFileSync17, mkdirSync as mkdirSync19 } from "fs";
|
|
37395
|
-
import { dirname as
|
|
37736
|
+
import { dirname as dirname18 } from "path";
|
|
37396
37737
|
init_secret_redaction();
|
|
37397
37738
|
var REPORT_EXPORT_SCHEMA = "todos.report_export.v1";
|
|
37398
37739
|
var REPORT_KINDS = ["project", "plan", "run", "evidence", "roadmap", "retrospective"];
|
|
@@ -37624,7 +37965,7 @@ function formatReportExport(data, format) {
|
|
|
37624
37965
|
return format === "html" ? formatReportHtml(data) : formatReportMarkdown(data);
|
|
37625
37966
|
}
|
|
37626
37967
|
function writeReportExport(data, format, path) {
|
|
37627
|
-
mkdirSync19(
|
|
37968
|
+
mkdirSync19(dirname18(path), { recursive: true });
|
|
37628
37969
|
writeFileSync17(path, formatReportExport(data, format), "utf8");
|
|
37629
37970
|
}
|
|
37630
37971
|
function exportReport(input, db) {
|
|
@@ -37658,8 +37999,8 @@ todos report export --kind retrospective --days 14 --format markdown --out retro
|
|
|
37658
37999
|
`;
|
|
37659
38000
|
}
|
|
37660
38001
|
// src/lib/command-aliases.ts
|
|
37661
|
-
import { existsSync as
|
|
37662
|
-
import { join as
|
|
38002
|
+
import { existsSync as existsSync23, readFileSync as readFileSync21, writeFileSync as writeFileSync18, mkdirSync as mkdirSync20 } from "fs";
|
|
38003
|
+
import { join as join21 } from "path";
|
|
37663
38004
|
var COMMAND_ALIASES_SCHEMA = "todos.command_aliases.v1";
|
|
37664
38005
|
var RESERVED = new Set([...listTopLevelCommands(), "help", "version", "alias", "shortcuts"]);
|
|
37665
38006
|
var BUILTIN_SHORTCUTS = [
|
|
@@ -37678,7 +38019,7 @@ var BUILTIN_SHORTCUTS = [
|
|
|
37678
38019
|
{ pattern: /^reports?$/, argv: ["report", "docs"], explain: "Report export documentation" }
|
|
37679
38020
|
];
|
|
37680
38021
|
function aliasesPath(cwd = process.cwd()) {
|
|
37681
|
-
return
|
|
38022
|
+
return join21(cwd, ".todos", "aliases.json");
|
|
37682
38023
|
}
|
|
37683
38024
|
function emptyStore() {
|
|
37684
38025
|
return { schema_version: COMMAND_ALIASES_SCHEMA, aliases: {}, updated_at: new Date(0).toISOString() };
|
|
@@ -37695,7 +38036,7 @@ function validateAliasName(name) {
|
|
|
37695
38036
|
}
|
|
37696
38037
|
function loadAliasStore(cwd) {
|
|
37697
38038
|
const path = aliasesPath(cwd);
|
|
37698
|
-
if (!
|
|
38039
|
+
if (!existsSync23(path))
|
|
37699
38040
|
return emptyStore();
|
|
37700
38041
|
const parsed = JSON.parse(readFileSync21(path, "utf8"));
|
|
37701
38042
|
if (parsed.schema_version !== COMMAND_ALIASES_SCHEMA) {
|
|
@@ -37705,7 +38046,7 @@ function loadAliasStore(cwd) {
|
|
|
37705
38046
|
}
|
|
37706
38047
|
function saveAliasStore(store, cwd) {
|
|
37707
38048
|
const path = aliasesPath(cwd);
|
|
37708
|
-
mkdirSync20(
|
|
38049
|
+
mkdirSync20(join21(path, ".."), { recursive: true });
|
|
37709
38050
|
store.updated_at = new Date().toISOString();
|
|
37710
38051
|
writeFileSync18(path, JSON.stringify(store, null, 2), "utf8");
|
|
37711
38052
|
}
|
|
@@ -38220,7 +38561,7 @@ function normalizeBranch(value) {
|
|
|
38220
38561
|
}
|
|
38221
38562
|
return branch;
|
|
38222
38563
|
}
|
|
38223
|
-
function
|
|
38564
|
+
function normalizePath6(value) {
|
|
38224
38565
|
const path = value.trim().replace(/\\/g, "/");
|
|
38225
38566
|
if (!path || path.startsWith("/") || path.includes("\x00"))
|
|
38226
38567
|
return null;
|
|
@@ -38252,7 +38593,7 @@ function getGitStatus(root, branch, includeGitStatus) {
|
|
|
38252
38593
|
const branchExists = runGit3(root, ["show-ref", "--verify", `refs/heads/${branch}`]) !== null;
|
|
38253
38594
|
const status = runGit3(root, ["status", "--short"]) || "";
|
|
38254
38595
|
const dirtyFiles = status.split(`
|
|
38255
|
-
`).map((line) => line.trim()).filter(Boolean).map((line) =>
|
|
38596
|
+
`).map((line) => line.trim()).filter(Boolean).map((line) => normalizePath6(line.replace(/^.. /, "").replace(/^.* -> /, ""))).filter((path) => Boolean(path));
|
|
38256
38597
|
return { has_git: true, current_branch: currentBranch || null, branch_exists: branchExists, dirty_files: dirtyFiles };
|
|
38257
38598
|
}
|
|
38258
38599
|
function resolveScope2(input, db) {
|
|
@@ -38267,8 +38608,8 @@ function resolveScope2(input, db) {
|
|
|
38267
38608
|
throw new Error("task_id or plan_id is required");
|
|
38268
38609
|
}
|
|
38269
38610
|
function collectPlannedFiles(tasks, explicitPaths, db) {
|
|
38270
|
-
const fromTasks = tasks.flatMap((task2) => listTaskFiles(task2.id, db).map((file) =>
|
|
38271
|
-
const fromInput = (explicitPaths || []).map(
|
|
38611
|
+
const fromTasks = tasks.flatMap((task2) => listTaskFiles(task2.id, db).map((file) => normalizePath6(file.path)));
|
|
38612
|
+
const fromInput = (explicitPaths || []).map(normalizePath6);
|
|
38272
38613
|
return uniqueSorted2([...fromTasks, ...fromInput]);
|
|
38273
38614
|
}
|
|
38274
38615
|
function detectBranchPlanConflicts(taskIds, files, db) {
|
|
@@ -38353,18 +38694,18 @@ function createBranchWorkPlan(input, db) {
|
|
|
38353
38694
|
}
|
|
38354
38695
|
// src/lib/user-scaffolds.ts
|
|
38355
38696
|
init_database();
|
|
38356
|
-
import { existsSync as
|
|
38357
|
-
import { join as
|
|
38697
|
+
import { existsSync as existsSync24, readFileSync as readFileSync22, writeFileSync as writeFileSync19, mkdirSync as mkdirSync21 } from "fs";
|
|
38698
|
+
import { join as join22 } from "path";
|
|
38358
38699
|
var USER_SCAFFOLD_SCHEMA = "todos.user_scaffold.v1";
|
|
38359
38700
|
var SCAFFOLD_KINDS = ["task", "project", "plan", "checklist", "contract", "verification_policy"];
|
|
38360
38701
|
function storeDir(cwd = process.cwd()) {
|
|
38361
|
-
return
|
|
38702
|
+
return join22(cwd, ".todos", "scaffolds");
|
|
38362
38703
|
}
|
|
38363
38704
|
function storePath(cwd) {
|
|
38364
|
-
return
|
|
38705
|
+
return join22(storeDir(cwd), "store.json");
|
|
38365
38706
|
}
|
|
38366
38707
|
function versionsDir(cwd) {
|
|
38367
|
-
return
|
|
38708
|
+
return join22(storeDir(cwd), "versions");
|
|
38368
38709
|
}
|
|
38369
38710
|
function slugify5(name) {
|
|
38370
38711
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -38374,7 +38715,7 @@ function emptyStore2() {
|
|
|
38374
38715
|
}
|
|
38375
38716
|
function loadUserScaffoldStore(cwd) {
|
|
38376
38717
|
const path = storePath(cwd);
|
|
38377
|
-
if (!
|
|
38718
|
+
if (!existsSync24(path))
|
|
38378
38719
|
return emptyStore2();
|
|
38379
38720
|
const parsed = JSON.parse(readFileSync22(path, "utf8"));
|
|
38380
38721
|
if (parsed.schema_version !== USER_SCAFFOLD_SCHEMA) {
|
|
@@ -38389,7 +38730,7 @@ function saveUserScaffoldStore(store, cwd) {
|
|
|
38389
38730
|
}
|
|
38390
38731
|
function snapshotVersion(scaffold, cwd) {
|
|
38391
38732
|
mkdirSync21(versionsDir(cwd), { recursive: true });
|
|
38392
|
-
const path =
|
|
38733
|
+
const path = join22(versionsDir(cwd), `${scaffold.id}-v${scaffold.version}.json`);
|
|
38393
38734
|
writeFileSync19(path, JSON.stringify(scaffold, null, 2), "utf8");
|
|
38394
38735
|
}
|
|
38395
38736
|
function listUserScaffolds(kind, cwd) {
|
|
@@ -38636,7 +38977,7 @@ function listLinkedTemplates(db, cwd) {
|
|
|
38636
38977
|
// src/lib/agent-workflow-demo.ts
|
|
38637
38978
|
init_database();
|
|
38638
38979
|
import { mkdtempSync } from "fs";
|
|
38639
|
-
import { join as
|
|
38980
|
+
import { join as join23 } from "path";
|
|
38640
38981
|
import { tmpdir as tmpdir4 } from "os";
|
|
38641
38982
|
var AGENT_WORKFLOW_DEMO_SCHEMA = "todos.agent_workflow_demo.v1";
|
|
38642
38983
|
var DEMO_DEFAULT_AGENT = "demoagent";
|
|
@@ -38652,7 +38993,7 @@ function setupEphemeralDemoDb(options = {}) {
|
|
|
38652
38993
|
if (options.db_path) {
|
|
38653
38994
|
db_path = options.db_path;
|
|
38654
38995
|
} else if (options.persist) {
|
|
38655
|
-
db_path =
|
|
38996
|
+
db_path = join23(mkdtempSync(join23(tmpdir4(), "todos-demo-")), "todos.db");
|
|
38656
38997
|
} else {
|
|
38657
38998
|
db_path = ":memory:";
|
|
38658
38999
|
}
|
|
@@ -40977,18 +41318,18 @@ function runSearchView(idOrName, db) {
|
|
|
40977
41318
|
return { ...runSavedSearch(view.filters, view.scope, d), view };
|
|
40978
41319
|
}
|
|
40979
41320
|
// src/lib/claude-tasks.ts
|
|
40980
|
-
import { existsSync as
|
|
40981
|
-
import { join as
|
|
41321
|
+
import { existsSync as existsSync25, readFileSync as readFileSync23, readdirSync as readdirSync5, writeFileSync as writeFileSync20 } from "fs";
|
|
41322
|
+
import { join as join24 } from "path";
|
|
40982
41323
|
init_config();
|
|
40983
41324
|
init_sync_utils();
|
|
40984
41325
|
function getTaskListDir(taskListId) {
|
|
40985
|
-
return
|
|
41326
|
+
return join24(HOME, ".claude", "tasks", taskListId);
|
|
40986
41327
|
}
|
|
40987
41328
|
function readClaudeTask(dir, filename) {
|
|
40988
|
-
return readJsonFile(
|
|
41329
|
+
return readJsonFile(join24(dir, filename));
|
|
40989
41330
|
}
|
|
40990
41331
|
function writeClaudeTask(dir, task2) {
|
|
40991
|
-
writeJsonFile(
|
|
41332
|
+
writeJsonFile(join24(dir, `${task2.id}.json`), task2);
|
|
40992
41333
|
}
|
|
40993
41334
|
function toClaudeStatus(status) {
|
|
40994
41335
|
if (status === "pending" || status === "in_progress" || status === "completed") {
|
|
@@ -41000,14 +41341,14 @@ function toSqliteStatus(status) {
|
|
|
41000
41341
|
return status;
|
|
41001
41342
|
}
|
|
41002
41343
|
function readPrefixCounter(dir) {
|
|
41003
|
-
const path =
|
|
41004
|
-
if (!
|
|
41344
|
+
const path = join24(dir, ".prefix-counter");
|
|
41345
|
+
if (!existsSync25(path))
|
|
41005
41346
|
return 0;
|
|
41006
41347
|
const val = parseInt(readFileSync23(path, "utf-8").trim(), 10);
|
|
41007
41348
|
return isNaN(val) ? 0 : val;
|
|
41008
41349
|
}
|
|
41009
41350
|
function writePrefixCounter(dir, value) {
|
|
41010
|
-
writeFileSync20(
|
|
41351
|
+
writeFileSync20(join24(dir, ".prefix-counter"), String(value));
|
|
41011
41352
|
}
|
|
41012
41353
|
function formatPrefixedSubject(title, prefix, counter) {
|
|
41013
41354
|
const padded = String(counter).padStart(5, "0");
|
|
@@ -41034,7 +41375,7 @@ function taskToClaudeTask(task2, claudeTaskId, existingMeta) {
|
|
|
41034
41375
|
}
|
|
41035
41376
|
function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
41036
41377
|
const dir = getTaskListDir(taskListId);
|
|
41037
|
-
if (!
|
|
41378
|
+
if (!existsSync25(dir))
|
|
41038
41379
|
ensureDir2(dir);
|
|
41039
41380
|
const filter = {};
|
|
41040
41381
|
if (projectId)
|
|
@@ -41043,7 +41384,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
41043
41384
|
const existingByTodosId = new Map;
|
|
41044
41385
|
const files = listJsonFiles(dir);
|
|
41045
41386
|
for (const f of files) {
|
|
41046
|
-
const path =
|
|
41387
|
+
const path = join24(dir, f);
|
|
41047
41388
|
const ct = readClaudeTask(dir, f);
|
|
41048
41389
|
if (ct?.metadata?.["todos_id"]) {
|
|
41049
41390
|
existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -41130,10 +41471,10 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
41130
41471
|
}
|
|
41131
41472
|
function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
|
|
41132
41473
|
const dir = getTaskListDir(taskListId);
|
|
41133
|
-
if (!
|
|
41474
|
+
if (!existsSync25(dir)) {
|
|
41134
41475
|
return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
|
|
41135
41476
|
}
|
|
41136
|
-
const files =
|
|
41477
|
+
const files = readdirSync5(dir).filter((f) => f.endsWith(".json"));
|
|
41137
41478
|
let pulled = 0;
|
|
41138
41479
|
const errors = [];
|
|
41139
41480
|
const prefer = options.prefer || "remote";
|
|
@@ -41150,7 +41491,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
41150
41491
|
}
|
|
41151
41492
|
for (const f of files) {
|
|
41152
41493
|
try {
|
|
41153
|
-
const filePath =
|
|
41494
|
+
const filePath = join24(dir, f);
|
|
41154
41495
|
const ct = readClaudeTask(dir, f);
|
|
41155
41496
|
if (!ct)
|
|
41156
41497
|
continue;
|
|
@@ -41218,22 +41559,22 @@ function syncClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
41218
41559
|
}
|
|
41219
41560
|
|
|
41220
41561
|
// src/lib/agent-tasks.ts
|
|
41221
|
-
import { existsSync as
|
|
41222
|
-
import { join as
|
|
41562
|
+
import { existsSync as existsSync26 } from "fs";
|
|
41563
|
+
import { join as join25 } from "path";
|
|
41223
41564
|
init_sync_utils();
|
|
41224
41565
|
init_config();
|
|
41225
41566
|
function agentBaseDir(agent) {
|
|
41226
41567
|
const key = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
|
|
41227
|
-
return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] ||
|
|
41568
|
+
return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join25(getTodosGlobalDir(), "agents");
|
|
41228
41569
|
}
|
|
41229
41570
|
function getTaskListDir2(agent, taskListId) {
|
|
41230
|
-
return
|
|
41571
|
+
return join25(agentBaseDir(agent), agent, taskListId);
|
|
41231
41572
|
}
|
|
41232
41573
|
function readAgentTask(dir, filename) {
|
|
41233
|
-
return readJsonFile(
|
|
41574
|
+
return readJsonFile(join25(dir, filename));
|
|
41234
41575
|
}
|
|
41235
41576
|
function writeAgentTask(dir, task2) {
|
|
41236
|
-
writeJsonFile(
|
|
41577
|
+
writeJsonFile(join25(dir, `${task2.id}.json`), task2);
|
|
41237
41578
|
}
|
|
41238
41579
|
function taskToAgentTask(task2, externalId, existingMeta) {
|
|
41239
41580
|
return {
|
|
@@ -41258,7 +41599,7 @@ function metadataKey(agent) {
|
|
|
41258
41599
|
}
|
|
41259
41600
|
function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
41260
41601
|
const dir = getTaskListDir2(agent, taskListId);
|
|
41261
|
-
if (!
|
|
41602
|
+
if (!existsSync26(dir))
|
|
41262
41603
|
ensureDir2(dir);
|
|
41263
41604
|
const filter = {};
|
|
41264
41605
|
if (projectId)
|
|
@@ -41267,7 +41608,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
41267
41608
|
const existingByTodosId = new Map;
|
|
41268
41609
|
const files = listJsonFiles(dir);
|
|
41269
41610
|
for (const f of files) {
|
|
41270
|
-
const path =
|
|
41611
|
+
const path = join25(dir, f);
|
|
41271
41612
|
const at = readAgentTask(dir, f);
|
|
41272
41613
|
if (at?.metadata?.["todos_id"]) {
|
|
41273
41614
|
existingByTodosId.set(at.metadata["todos_id"], { task: at, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -41341,7 +41682,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
41341
41682
|
}
|
|
41342
41683
|
function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
41343
41684
|
const dir = getTaskListDir2(agent, taskListId);
|
|
41344
|
-
if (!
|
|
41685
|
+
if (!existsSync26(dir)) {
|
|
41345
41686
|
return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
|
|
41346
41687
|
}
|
|
41347
41688
|
const files = listJsonFiles(dir);
|
|
@@ -41360,7 +41701,7 @@ function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
41360
41701
|
}
|
|
41361
41702
|
for (const f of files) {
|
|
41362
41703
|
try {
|
|
41363
|
-
const filePath =
|
|
41704
|
+
const filePath = join25(dir, f);
|
|
41364
41705
|
const at = readAgentTask(dir, f);
|
|
41365
41706
|
if (!at)
|
|
41366
41707
|
continue;
|
|
@@ -41498,9 +41839,9 @@ function syncWithAgents(agents, taskListIdByAgent, projectId, direction = "both"
|
|
|
41498
41839
|
return { pushed, pulled, errors };
|
|
41499
41840
|
}
|
|
41500
41841
|
// src/lib/extract.ts
|
|
41501
|
-
import { existsSync as
|
|
41502
|
-
import { createHash as
|
|
41503
|
-
import { relative as relative6, resolve as
|
|
41842
|
+
import { existsSync as existsSync27, readFileSync as readFileSync24, statSync as statSync9 } from "fs";
|
|
41843
|
+
import { createHash as createHash15 } from "crypto";
|
|
41844
|
+
import { relative as relative6, resolve as resolve18, join as join26 } from "path";
|
|
41504
41845
|
var EXTRACT_TAGS = ["TODO", "FIXME", "HACK", "XXX", "BUG", "NOTE"];
|
|
41505
41846
|
var DEFAULT_EXTENSIONS = new Set([
|
|
41506
41847
|
".ts",
|
|
@@ -41564,15 +41905,15 @@ var SKIP_DIRS2 = new Set([
|
|
|
41564
41905
|
".parcel-cache"
|
|
41565
41906
|
]);
|
|
41566
41907
|
function stableHash(value) {
|
|
41567
|
-
return
|
|
41908
|
+
return createHash15("sha256").update(value).digest("hex");
|
|
41568
41909
|
}
|
|
41569
41910
|
function normalizePathForMatch(value) {
|
|
41570
41911
|
return value.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
41571
41912
|
}
|
|
41572
41913
|
function readGitignorePatterns(basePath) {
|
|
41573
|
-
const root =
|
|
41574
|
-
const gitignorePath =
|
|
41575
|
-
if (!
|
|
41914
|
+
const root = statSync9(basePath).isFile() ? resolve18(basePath, "..") : basePath;
|
|
41915
|
+
const gitignorePath = join26(root, ".gitignore");
|
|
41916
|
+
if (!existsSync27(gitignorePath))
|
|
41576
41917
|
return [];
|
|
41577
41918
|
try {
|
|
41578
41919
|
return readFileSync24(gitignorePath, "utf-8").split(`
|
|
@@ -41681,7 +42022,7 @@ function extractFromSource(source9, filePath, tags = [...EXTRACT_TAGS]) {
|
|
|
41681
42022
|
return results;
|
|
41682
42023
|
}
|
|
41683
42024
|
function collectFiles(basePath, extensions, excludes, respectGitignore) {
|
|
41684
|
-
const stat =
|
|
42025
|
+
const stat = statSync9(basePath);
|
|
41685
42026
|
if (stat.isFile()) {
|
|
41686
42027
|
return [basePath];
|
|
41687
42028
|
}
|
|
@@ -41706,7 +42047,7 @@ function collectFiles(basePath, extensions, excludes, respectGitignore) {
|
|
|
41706
42047
|
return files.sort();
|
|
41707
42048
|
}
|
|
41708
42049
|
function buildCodebaseIndex(options) {
|
|
41709
|
-
const basePath =
|
|
42050
|
+
const basePath = resolve18(options.path);
|
|
41710
42051
|
const tags = options.patterns || [...EXTRACT_TAGS];
|
|
41711
42052
|
const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
|
|
41712
42053
|
const excludes = options.exclude || [];
|
|
@@ -41714,10 +42055,10 @@ function buildCodebaseIndex(options) {
|
|
|
41714
42055
|
const files = collectFiles(basePath, extensions, excludes, respectGitignore);
|
|
41715
42056
|
const indexed = [];
|
|
41716
42057
|
for (const file of files) {
|
|
41717
|
-
const fullPath =
|
|
42058
|
+
const fullPath = statSync9(basePath).isFile() ? basePath : join26(basePath, file);
|
|
41718
42059
|
try {
|
|
41719
42060
|
const source9 = readFileSync24(fullPath, "utf-8");
|
|
41720
|
-
const relPath =
|
|
42061
|
+
const relPath = statSync9(basePath).isFile() ? relative6(resolve18(basePath, ".."), fullPath) : file;
|
|
41721
42062
|
indexed.push({
|
|
41722
42063
|
file: relPath,
|
|
41723
42064
|
checksum: stableHash(source9).slice(0, 24),
|
|
@@ -41737,7 +42078,7 @@ function buildCodebaseIndex(options) {
|
|
|
41737
42078
|
};
|
|
41738
42079
|
}
|
|
41739
42080
|
function extractTodos(options, db) {
|
|
41740
|
-
const basePath =
|
|
42081
|
+
const basePath = resolve18(options.path);
|
|
41741
42082
|
const tags = options.patterns || [...EXTRACT_TAGS];
|
|
41742
42083
|
const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
|
|
41743
42084
|
const excludes = options.exclude || [];
|
|
@@ -41745,10 +42086,10 @@ function extractTodos(options, db) {
|
|
|
41745
42086
|
const files = collectFiles(basePath, extensions, excludes, respectGitignore);
|
|
41746
42087
|
const allComments = [];
|
|
41747
42088
|
for (const file of files) {
|
|
41748
|
-
const fullPath =
|
|
42089
|
+
const fullPath = statSync9(basePath).isFile() ? basePath : join26(basePath, file);
|
|
41749
42090
|
try {
|
|
41750
42091
|
const source9 = readFileSync24(fullPath, "utf-8");
|
|
41751
|
-
const relPath =
|
|
42092
|
+
const relPath = statSync9(basePath).isFile() ? relative6(resolve18(basePath, ".."), fullPath) : file;
|
|
41752
42093
|
const comments = extractFromSource(source9, relPath, tags);
|
|
41753
42094
|
allComments.push(...comments);
|
|
41754
42095
|
} catch {}
|
|
@@ -41842,7 +42183,7 @@ async function watchSourceTodos(options, onRun) {
|
|
|
41842
42183
|
const interval = Math.max(100, options.interval_ms || 2000);
|
|
41843
42184
|
const once = options.once !== false && (!options.max_runs || options.max_runs <= 1);
|
|
41844
42185
|
const maxRuns = options.max_runs ?? (once ? 1 : Number.POSITIVE_INFINITY);
|
|
41845
|
-
const root =
|
|
42186
|
+
const root = resolve18(options.path);
|
|
41846
42187
|
const runs = [];
|
|
41847
42188
|
let previous = new Map;
|
|
41848
42189
|
for (let runNumber = 1;runNumber <= maxRuns; runNumber++) {
|
|
@@ -42452,7 +42793,7 @@ function renderWorkflowStatesMarkdown(states = listWorkflowStates()) {
|
|
|
42452
42793
|
}
|
|
42453
42794
|
// src/lib/agent-replay-simulator.ts
|
|
42454
42795
|
init_redaction();
|
|
42455
|
-
import { createHash as
|
|
42796
|
+
import { createHash as createHash16 } from "crypto";
|
|
42456
42797
|
import { readFileSync as readFileSync25 } from "fs";
|
|
42457
42798
|
function isObject(value) {
|
|
42458
42799
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
@@ -42474,7 +42815,7 @@ function stable2(value) {
|
|
|
42474
42815
|
return Object.fromEntries(Object.keys(value).sort().map((key) => [key, stable2(value[key])]));
|
|
42475
42816
|
}
|
|
42476
42817
|
function fingerprint3(value) {
|
|
42477
|
-
return
|
|
42818
|
+
return createHash16("sha256").update(JSON.stringify(stable2(value))).digest("hex");
|
|
42478
42819
|
}
|
|
42479
42820
|
function unpackFixture(input) {
|
|
42480
42821
|
if (!isObject(input))
|
|
@@ -42713,9 +43054,9 @@ function renderAgentReplaySimulationMarkdown(simulation) {
|
|
|
42713
43054
|
}
|
|
42714
43055
|
// src/lib/local-extensions.ts
|
|
42715
43056
|
init_config();
|
|
42716
|
-
import { createHash as
|
|
42717
|
-
import { existsSync as
|
|
42718
|
-
import { basename as
|
|
43057
|
+
import { createHash as createHash17, createVerify } from "crypto";
|
|
43058
|
+
import { existsSync as existsSync28, readdirSync as readdirSync6, readFileSync as readFileSync26, statSync as statSync10 } from "fs";
|
|
43059
|
+
import { basename as basename6, join as join27, resolve as resolve19 } from "path";
|
|
42719
43060
|
init_redaction();
|
|
42720
43061
|
function isObject2(value) {
|
|
42721
43062
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
@@ -42800,7 +43141,7 @@ function parseJson(path) {
|
|
|
42800
43141
|
return JSON.parse(readFileSync26(path, "utf8"));
|
|
42801
43142
|
}
|
|
42802
43143
|
function sha2566(bytes) {
|
|
42803
|
-
return `sha256:${
|
|
43144
|
+
return `sha256:${createHash17("sha256").update(bytes).digest("hex")}`;
|
|
42804
43145
|
}
|
|
42805
43146
|
function compareVersions(a, b) {
|
|
42806
43147
|
const left = a.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
@@ -42995,11 +43336,11 @@ function verifyExtensionSignature(input) {
|
|
|
42995
43336
|
return verifier.verify(input.public_key, decodeSignature(input.signature));
|
|
42996
43337
|
}
|
|
42997
43338
|
function inspectExtensionSource(source9) {
|
|
42998
|
-
const resolved =
|
|
42999
|
-
if (!
|
|
43339
|
+
const resolved = resolve19(source9);
|
|
43340
|
+
if (!existsSync28(resolved))
|
|
43000
43341
|
throw new Error(`extension source not found: ${source9}`);
|
|
43001
|
-
const stat =
|
|
43002
|
-
const manifestPath = stat.isDirectory() ? [
|
|
43342
|
+
const stat = statSync10(resolved);
|
|
43343
|
+
const manifestPath = stat.isDirectory() ? [join27(resolved, "todos.extension.json"), join27(resolved, "extension.json")].find(existsSync28) : resolved;
|
|
43003
43344
|
if (!manifestPath)
|
|
43004
43345
|
throw new Error(`extension directory ${source9} is missing todos.extension.json`);
|
|
43005
43346
|
const raw = readFileSync26(manifestPath);
|
|
@@ -43093,26 +43434,26 @@ function testExtensionCompatibility(sourceOrManifest) {
|
|
|
43093
43434
|
function projectExtensionSources(projectPath) {
|
|
43094
43435
|
if (!projectPath)
|
|
43095
43436
|
return [];
|
|
43096
|
-
const root =
|
|
43437
|
+
const root = resolve19(projectPath);
|
|
43097
43438
|
const candidates = [
|
|
43098
|
-
|
|
43099
|
-
|
|
43439
|
+
join27(root, "todos.extension.json"),
|
|
43440
|
+
join27(root, ".todos", "todos.extension.json")
|
|
43100
43441
|
];
|
|
43101
|
-
const extensionDir =
|
|
43102
|
-
if (
|
|
43103
|
-
for (const entry2 of
|
|
43442
|
+
const extensionDir = join27(root, ".todos", "extensions");
|
|
43443
|
+
if (existsSync28(extensionDir)) {
|
|
43444
|
+
for (const entry2 of readdirSync6(extensionDir)) {
|
|
43104
43445
|
if (entry2.startsWith("."))
|
|
43105
43446
|
continue;
|
|
43106
|
-
const full =
|
|
43107
|
-
if (
|
|
43447
|
+
const full = join27(extensionDir, entry2);
|
|
43448
|
+
if (statSync10(full).isDirectory() || entry2.endsWith(".json"))
|
|
43108
43449
|
candidates.push(full);
|
|
43109
43450
|
}
|
|
43110
43451
|
}
|
|
43111
|
-
return candidates.filter(
|
|
43452
|
+
return candidates.filter(existsSync28);
|
|
43112
43453
|
}
|
|
43113
43454
|
function discoverLocalExtensions(options = {}) {
|
|
43114
43455
|
const config = loadConfig();
|
|
43115
|
-
const projectPath = options.project_path ?
|
|
43456
|
+
const projectPath = options.project_path ? resolve19(options.project_path) : null;
|
|
43116
43457
|
const configuredSources = [
|
|
43117
43458
|
...config.extension_sources || [],
|
|
43118
43459
|
...projectPath ? config.project_overrides?.[projectPath]?.extension_sources || [] : []
|
|
@@ -43120,7 +43461,7 @@ function discoverLocalExtensions(options = {}) {
|
|
|
43120
43461
|
const sources = Array.from(new Set([
|
|
43121
43462
|
...configuredSources,
|
|
43122
43463
|
...projectExtensionSources(projectPath || undefined)
|
|
43123
|
-
])).map((source9) => projectPath && !source9.startsWith("/") ?
|
|
43464
|
+
])).map((source9) => projectPath && !source9.startsWith("/") ? resolve19(projectPath, source9) : resolve19(source9));
|
|
43124
43465
|
const warnings = [];
|
|
43125
43466
|
const discovered = [];
|
|
43126
43467
|
for (const source9 of sources) {
|
|
@@ -43202,7 +43543,7 @@ function removeLocalExtension(name) {
|
|
|
43202
43543
|
return true;
|
|
43203
43544
|
}
|
|
43204
43545
|
function renderExtensionSummary(record) {
|
|
43205
|
-
return `${record.name}@${record.version} ${record.status} ${
|
|
43546
|
+
return `${record.name}@${record.version} ${record.status} ${basename6(record.source)} ${record.signature_verified ? "signed" : "unsigned"}`;
|
|
43206
43547
|
}
|
|
43207
43548
|
// src/lib/workflow-prompts.ts
|
|
43208
43549
|
var COMMON_ARGUMENTS = [
|
|
@@ -43969,7 +44310,7 @@ function resolveMissingTaskFindings(input, db) {
|
|
|
43969
44310
|
init_redaction();
|
|
43970
44311
|
|
|
43971
44312
|
// src/lib/retention-cleanup.ts
|
|
43972
|
-
import { existsSync as
|
|
44313
|
+
import { existsSync as existsSync29, unlinkSync as unlinkSync2 } from "fs";
|
|
43973
44314
|
init_database();
|
|
43974
44315
|
var RETENTION_CLEANUP_CONFIRMATION = "delete-local-retention-data";
|
|
43975
44316
|
var ALL_SCOPES = ["comments", "runs", "verifications", "expired_artifacts"];
|
|
@@ -44181,7 +44522,7 @@ function applyRetentionCleanup(input, db) {
|
|
|
44181
44522
|
for (const artifact of report.candidates.artifact_files) {
|
|
44182
44523
|
try {
|
|
44183
44524
|
const path = artifactStorePath(artifact.relative_path);
|
|
44184
|
-
if (!
|
|
44525
|
+
if (!existsSync29(path)) {
|
|
44185
44526
|
report.warnings.push(`stored artifact already missing: ${artifact.relative_path}`);
|
|
44186
44527
|
continue;
|
|
44187
44528
|
}
|
|
@@ -44419,8 +44760,8 @@ function renderScalePerformanceReportMarkdown(report) {
|
|
|
44419
44760
|
init_database();
|
|
44420
44761
|
init_migrations();
|
|
44421
44762
|
init_schema();
|
|
44422
|
-
import { chmodSync, copyFileSync as copyFileSync2, existsSync as
|
|
44423
|
-
import { basename as
|
|
44763
|
+
import { chmodSync, copyFileSync as copyFileSync2, existsSync as existsSync30, mkdirSync as mkdirSync22, statSync as statSync11 } from "fs";
|
|
44764
|
+
import { basename as basename7, dirname as dirname19, join as join28 } from "path";
|
|
44424
44765
|
var REQUIRED_TABLES2 = [
|
|
44425
44766
|
"_migrations",
|
|
44426
44767
|
"projects",
|
|
@@ -44527,7 +44868,7 @@ function findMissingProjectRoots(db) {
|
|
|
44527
44868
|
continue;
|
|
44528
44869
|
if (!row.path.startsWith("/"))
|
|
44529
44870
|
continue;
|
|
44530
|
-
if (!
|
|
44871
|
+
if (!existsSync30(row.path))
|
|
44531
44872
|
missing++;
|
|
44532
44873
|
}
|
|
44533
44874
|
return missing;
|
|
@@ -44579,7 +44920,7 @@ function databasePermissionsAreUnsafe(dbPath) {
|
|
|
44579
44920
|
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
44580
44921
|
return false;
|
|
44581
44922
|
try {
|
|
44582
|
-
return (
|
|
44923
|
+
return (statSync11(dbPath).mode & 63) !== 0;
|
|
44583
44924
|
} catch {
|
|
44584
44925
|
return false;
|
|
44585
44926
|
}
|
|
@@ -44587,16 +44928,16 @@ function databasePermissionsAreUnsafe(dbPath) {
|
|
|
44587
44928
|
function createBackup(dbPath) {
|
|
44588
44929
|
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
44589
44930
|
return;
|
|
44590
|
-
if (!
|
|
44931
|
+
if (!existsSync30(dbPath))
|
|
44591
44932
|
return;
|
|
44592
44933
|
const stamp = now().replace(/[:.]/g, "-");
|
|
44593
|
-
const backupDir =
|
|
44934
|
+
const backupDir = join28(dirname19(dbPath), `${basename7(dbPath)}.backup-${stamp}`);
|
|
44594
44935
|
const files = [];
|
|
44595
44936
|
mkdirSync22(backupDir, { recursive: true });
|
|
44596
44937
|
for (const source9 of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
|
|
44597
|
-
if (!
|
|
44938
|
+
if (!existsSync30(source9))
|
|
44598
44939
|
continue;
|
|
44599
|
-
const target =
|
|
44940
|
+
const target = join28(backupDir, basename7(source9));
|
|
44600
44941
|
copyFileSync2(source9, target);
|
|
44601
44942
|
files.push(target);
|
|
44602
44943
|
}
|
|
@@ -46020,6 +46361,7 @@ export {
|
|
|
46020
46361
|
dispatchToMultiple,
|
|
46021
46362
|
dismissReminder,
|
|
46022
46363
|
discoverVerificationProviderCapabilities,
|
|
46364
|
+
discoverTaskRouteSources,
|
|
46023
46365
|
discoverProjectWorkspace,
|
|
46024
46366
|
discoverLocalExtensions,
|
|
46025
46367
|
detectSourceType,
|
|
@@ -46271,6 +46613,7 @@ export {
|
|
|
46271
46613
|
TASK_WORKFLOW_POINTER_SCHEMA_VERSION,
|
|
46272
46614
|
TASK_STATUSES,
|
|
46273
46615
|
TASK_SCHEDULING_SCHEMA,
|
|
46616
|
+
TASK_ROUTE_SOURCE_DISCOVERY_SCHEMA_VERSION,
|
|
46274
46617
|
TASK_PRIORITIES,
|
|
46275
46618
|
TASK_FINDING_UPSERT_SCHEMA_VERSION,
|
|
46276
46619
|
TASK_FINDING_SCHEMA_VERSION,
|