@hasna/todos 0.11.68 → 0.11.70
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/plan-template-commands.d.ts.map +1 -1
- package/dist/cli/helpers.d.ts +1 -0
- package/dist/cli/helpers.d.ts.map +1 -1
- package/dist/cli/index.js +639 -240
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +527 -230
- package/dist/lib/plan-artifacts.d.ts +69 -0
- package/dist/lib/plan-artifacts.d.ts.map +1 -0
- package/dist/release-provenance.json +3 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -24961,6 +24961,294 @@ function listCyclesWithStats(options = {}, db) {
|
|
|
24961
24961
|
};
|
|
24962
24962
|
});
|
|
24963
24963
|
}
|
|
24964
|
+
// src/lib/plan-artifacts.ts
|
|
24965
|
+
init_database();
|
|
24966
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync8, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
|
|
24967
|
+
import { join as join10, resolve as resolve10 } from "path";
|
|
24968
|
+
var PLAN_MARKDOWN_SCHEMA = "hasna.todos.plan/v1";
|
|
24969
|
+
function assertSafePathSegment(value, label) {
|
|
24970
|
+
const trimmed = value.trim();
|
|
24971
|
+
if (!trimmed || trimmed === "." || trimmed === ".." || trimmed.includes("/") || trimmed.includes("\\")) {
|
|
24972
|
+
throw new Error(`Invalid ${label} for plan artifact path`);
|
|
24973
|
+
}
|
|
24974
|
+
if (!/^[A-Za-z0-9._-]+$/.test(trimmed)) {
|
|
24975
|
+
throw new Error(`Invalid ${label} for plan artifact path`);
|
|
24976
|
+
}
|
|
24977
|
+
return trimmed;
|
|
24978
|
+
}
|
|
24979
|
+
function frontmatterScalar(value) {
|
|
24980
|
+
return JSON.stringify(value);
|
|
24981
|
+
}
|
|
24982
|
+
function parseFrontmatterScalar(value) {
|
|
24983
|
+
const trimmed = value.trim();
|
|
24984
|
+
if (trimmed === "null")
|
|
24985
|
+
return null;
|
|
24986
|
+
try {
|
|
24987
|
+
const parsed = JSON.parse(trimmed);
|
|
24988
|
+
if (parsed === null || typeof parsed === "string")
|
|
24989
|
+
return parsed;
|
|
24990
|
+
} catch {}
|
|
24991
|
+
return trimmed.replace(/^["']|["']$/g, "") || null;
|
|
24992
|
+
}
|
|
24993
|
+
function markdownEscape(text) {
|
|
24994
|
+
return text.replace(/<!--[\s\S]*?-->/g, "").trim();
|
|
24995
|
+
}
|
|
24996
|
+
function markdownLine(text) {
|
|
24997
|
+
return markdownEscape(text).replace(/\s+/g, " ").trim();
|
|
24998
|
+
}
|
|
24999
|
+
function projectSlugMatches(project, ref) {
|
|
25000
|
+
const normalized = slugify(ref);
|
|
25001
|
+
return Boolean(normalized) && (project.task_list_id === normalized || slugify(project.name) === normalized);
|
|
25002
|
+
}
|
|
25003
|
+
function resolvePlanArtifactProject(input) {
|
|
25004
|
+
const db = input.db || getDatabase();
|
|
25005
|
+
const ref = input.project_id || input.project_ref;
|
|
25006
|
+
if (!ref)
|
|
25007
|
+
throw new Error("Plan artifacts require a project id or project reference");
|
|
25008
|
+
const byPath = getProjectByPath(resolve10(ref), db);
|
|
25009
|
+
if (byPath)
|
|
25010
|
+
return byPath;
|
|
25011
|
+
const resolvedId = resolvePartialId(db, "projects", ref);
|
|
25012
|
+
if (resolvedId) {
|
|
25013
|
+
const project2 = getProject(resolvedId, db);
|
|
25014
|
+
if (project2)
|
|
25015
|
+
return project2;
|
|
25016
|
+
}
|
|
25017
|
+
const project = listProjects(db).find((candidate) => projectSlugMatches(candidate, ref));
|
|
25018
|
+
if (project)
|
|
25019
|
+
return project;
|
|
25020
|
+
throw new Error(`Project not found for plan artifacts: ${ref}`);
|
|
25021
|
+
}
|
|
25022
|
+
function resolvePlanArtifactPaths(input) {
|
|
25023
|
+
const project = resolvePlanArtifactProject(input);
|
|
25024
|
+
const projectId = assertSafePathSegment(project.id, "project id");
|
|
25025
|
+
const projectRoot = resolve10(project.path);
|
|
25026
|
+
const directory = join10(projectRoot, ".hasna", "todos", "plans", projectId);
|
|
25027
|
+
const planId = input.plan_id ? assertSafePathSegment(input.plan_id, "plan id") : null;
|
|
25028
|
+
return {
|
|
25029
|
+
project_id: project.id,
|
|
25030
|
+
project_root: projectRoot,
|
|
25031
|
+
directory,
|
|
25032
|
+
file_path: planId ? join10(directory, `${planId}.md`) : directory
|
|
25033
|
+
};
|
|
25034
|
+
}
|
|
25035
|
+
function buildPlanArtifactSnapshot(plan, tasks = [], artifactUpdatedAt = new Date().toISOString()) {
|
|
25036
|
+
if (!plan.project_id)
|
|
25037
|
+
throw new Error("Plan artifacts require a project-scoped plan");
|
|
25038
|
+
const taskReferences = tasks.map((task2) => ({
|
|
25039
|
+
task_id: task2.id,
|
|
25040
|
+
title: task2.title,
|
|
25041
|
+
status: task2.status,
|
|
25042
|
+
priority: task2.priority
|
|
25043
|
+
}));
|
|
25044
|
+
const body = renderPlanArtifactBody(plan, taskReferences);
|
|
25045
|
+
return {
|
|
25046
|
+
metadata: {
|
|
25047
|
+
schema: PLAN_MARKDOWN_SCHEMA,
|
|
25048
|
+
plan_id: plan.id,
|
|
25049
|
+
project_id: plan.project_id,
|
|
25050
|
+
task_list_id: plan.task_list_id ?? null,
|
|
25051
|
+
agent_id: plan.agent_id ?? null,
|
|
25052
|
+
stable_id: plan.id,
|
|
25053
|
+
name: plan.name,
|
|
25054
|
+
status: plan.status,
|
|
25055
|
+
created_at: plan.created_at,
|
|
25056
|
+
updated_at: plan.updated_at,
|
|
25057
|
+
artifact_updated_at: artifactUpdatedAt
|
|
25058
|
+
},
|
|
25059
|
+
task_references: taskReferences,
|
|
25060
|
+
body
|
|
25061
|
+
};
|
|
25062
|
+
}
|
|
25063
|
+
function renderPlanArtifactBody(plan, tasks) {
|
|
25064
|
+
const lines = [`# ${markdownLine(plan.name) || plan.id}`, ""];
|
|
25065
|
+
if (plan.description?.trim()) {
|
|
25066
|
+
lines.push(markdownEscape(plan.description), "");
|
|
25067
|
+
}
|
|
25068
|
+
lines.push("## Tasks", "");
|
|
25069
|
+
if (tasks.length === 0) {
|
|
25070
|
+
lines.push("_No tasks are currently attached to this plan._", "");
|
|
25071
|
+
} else {
|
|
25072
|
+
for (const task2 of tasks) {
|
|
25073
|
+
const check = task2.status === "completed" ? "x" : " ";
|
|
25074
|
+
lines.push(`- [${check}] ${markdownLine(task2.title) || task2.task_id}`);
|
|
25075
|
+
lines.push(` <!-- todos: task_id=${task2.task_id} status=${task2.status} priority=${task2.priority} -->`);
|
|
25076
|
+
}
|
|
25077
|
+
lines.push("");
|
|
25078
|
+
}
|
|
25079
|
+
return lines.join(`
|
|
25080
|
+
`);
|
|
25081
|
+
}
|
|
25082
|
+
function renderPlanArtifactMarkdown(snapshot) {
|
|
25083
|
+
const metadata = snapshot.metadata;
|
|
25084
|
+
const lines = [
|
|
25085
|
+
"---",
|
|
25086
|
+
`schema: ${frontmatterScalar(metadata.schema)}`,
|
|
25087
|
+
`plan_id: ${frontmatterScalar(metadata.plan_id)}`,
|
|
25088
|
+
`project_id: ${frontmatterScalar(metadata.project_id)}`,
|
|
25089
|
+
`task_list_id: ${frontmatterScalar(metadata.task_list_id)}`,
|
|
25090
|
+
`agent_id: ${frontmatterScalar(metadata.agent_id)}`,
|
|
25091
|
+
`stable_id: ${frontmatterScalar(metadata.stable_id)}`,
|
|
25092
|
+
`name: ${frontmatterScalar(metadata.name)}`,
|
|
25093
|
+
`status: ${frontmatterScalar(metadata.status)}`,
|
|
25094
|
+
`created_at: ${frontmatterScalar(metadata.created_at)}`,
|
|
25095
|
+
`updated_at: ${frontmatterScalar(metadata.updated_at)}`,
|
|
25096
|
+
`artifact_updated_at: ${frontmatterScalar(metadata.artifact_updated_at)}`,
|
|
25097
|
+
"---",
|
|
25098
|
+
"",
|
|
25099
|
+
snapshot.body
|
|
25100
|
+
];
|
|
25101
|
+
return `${lines.join(`
|
|
25102
|
+
`).replace(/\n{3,}/g, `
|
|
25103
|
+
|
|
25104
|
+
`).trimEnd()}
|
|
25105
|
+
`;
|
|
25106
|
+
}
|
|
25107
|
+
function parsePlanArtifactMarkdown(markdown) {
|
|
25108
|
+
const match = markdown.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
|
|
25109
|
+
if (!match)
|
|
25110
|
+
throw new Error("Invalid plan artifact: missing frontmatter");
|
|
25111
|
+
const rawMetadata = {};
|
|
25112
|
+
for (const line of match[1].split(/\r?\n/)) {
|
|
25113
|
+
const separator = line.indexOf(":");
|
|
25114
|
+
if (separator === -1)
|
|
25115
|
+
continue;
|
|
25116
|
+
const key = line.slice(0, separator).trim();
|
|
25117
|
+
const value = line.slice(separator + 1);
|
|
25118
|
+
rawMetadata[key] = parseFrontmatterScalar(value);
|
|
25119
|
+
}
|
|
25120
|
+
if (rawMetadata.schema !== PLAN_MARKDOWN_SCHEMA) {
|
|
25121
|
+
throw new Error(`Unsupported plan artifact schema: ${rawMetadata.schema ?? "unknown"}`);
|
|
25122
|
+
}
|
|
25123
|
+
const required = ["plan_id", "project_id", "stable_id", "name", "status", "created_at", "updated_at", "artifact_updated_at"];
|
|
25124
|
+
for (const key of required) {
|
|
25125
|
+
if (!rawMetadata[key])
|
|
25126
|
+
throw new Error(`Invalid plan artifact: missing ${key}`);
|
|
25127
|
+
}
|
|
25128
|
+
const body = match[2] ?? "";
|
|
25129
|
+
return {
|
|
25130
|
+
metadata: {
|
|
25131
|
+
schema: PLAN_MARKDOWN_SCHEMA,
|
|
25132
|
+
plan_id: rawMetadata.plan_id,
|
|
25133
|
+
project_id: rawMetadata.project_id,
|
|
25134
|
+
task_list_id: rawMetadata.task_list_id ?? null,
|
|
25135
|
+
agent_id: rawMetadata.agent_id ?? null,
|
|
25136
|
+
stable_id: rawMetadata.stable_id,
|
|
25137
|
+
name: rawMetadata.name,
|
|
25138
|
+
status: rawMetadata.status,
|
|
25139
|
+
created_at: rawMetadata.created_at,
|
|
25140
|
+
updated_at: rawMetadata.updated_at,
|
|
25141
|
+
artifact_updated_at: rawMetadata.artifact_updated_at
|
|
25142
|
+
},
|
|
25143
|
+
task_references: parseTaskReferences(body),
|
|
25144
|
+
body
|
|
25145
|
+
};
|
|
25146
|
+
}
|
|
25147
|
+
function parseTaskReferences(body) {
|
|
25148
|
+
const references = [];
|
|
25149
|
+
const taskLine2 = /^\s*-\s+\[[ xX]\]\s+(.+)$/;
|
|
25150
|
+
const metadataLine = /<!--\s*todos:\s*task_id=([A-Za-z0-9._-]+)\s+status=([A-Za-z_]+)\s+priority=([A-Za-z_]+)\s*-->/;
|
|
25151
|
+
const lines = body.split(/\r?\n/);
|
|
25152
|
+
for (let index = 0;index < lines.length; index++) {
|
|
25153
|
+
const titleMatch = lines[index].match(taskLine2);
|
|
25154
|
+
if (!titleMatch)
|
|
25155
|
+
continue;
|
|
25156
|
+
const metadataMatch = lines[index + 1]?.match(metadataLine);
|
|
25157
|
+
if (!metadataMatch)
|
|
25158
|
+
continue;
|
|
25159
|
+
references.push({
|
|
25160
|
+
task_id: metadataMatch[1],
|
|
25161
|
+
title: titleMatch[1].trim(),
|
|
25162
|
+
status: metadataMatch[2],
|
|
25163
|
+
priority: metadataMatch[3]
|
|
25164
|
+
});
|
|
25165
|
+
}
|
|
25166
|
+
return references;
|
|
25167
|
+
}
|
|
25168
|
+
function writePlanArtifact(plan, db) {
|
|
25169
|
+
if (!plan.project_id)
|
|
25170
|
+
return null;
|
|
25171
|
+
const d = db || getDatabase();
|
|
25172
|
+
const tasks = listTasks({ plan_id: plan.id, include_archived: true }, d);
|
|
25173
|
+
const paths = resolvePlanArtifactPaths({ project_id: plan.project_id, plan_id: plan.id, db: d });
|
|
25174
|
+
const snapshot = buildPlanArtifactSnapshot(plan, tasks);
|
|
25175
|
+
mkdirSync8(paths.directory, { recursive: true });
|
|
25176
|
+
writeFileSync6(paths.file_path, renderPlanArtifactMarkdown(snapshot), "utf8");
|
|
25177
|
+
return { path: paths.file_path, snapshot };
|
|
25178
|
+
}
|
|
25179
|
+
function readPlanArtifact(plan, db) {
|
|
25180
|
+
if (!plan.project_id)
|
|
25181
|
+
return null;
|
|
25182
|
+
const d = db || getDatabase();
|
|
25183
|
+
const paths = resolvePlanArtifactPaths({ project_id: plan.project_id, plan_id: plan.id, db: d });
|
|
25184
|
+
if (!existsSync9(paths.file_path))
|
|
25185
|
+
return null;
|
|
25186
|
+
const markdown = readFileSync7(paths.file_path, "utf8");
|
|
25187
|
+
return {
|
|
25188
|
+
path: paths.file_path,
|
|
25189
|
+
markdown,
|
|
25190
|
+
...parsePlanArtifactMarkdown(markdown)
|
|
25191
|
+
};
|
|
25192
|
+
}
|
|
25193
|
+
function inspectPlanArtifact(plan, db) {
|
|
25194
|
+
if (!plan.project_id)
|
|
25195
|
+
return null;
|
|
25196
|
+
const d = db || getDatabase();
|
|
25197
|
+
const paths = resolvePlanArtifactPaths({ project_id: plan.project_id, plan_id: plan.id, db: d });
|
|
25198
|
+
if (!existsSync9(paths.file_path)) {
|
|
25199
|
+
return {
|
|
25200
|
+
path: paths.file_path,
|
|
25201
|
+
exists: false,
|
|
25202
|
+
parse_error: null,
|
|
25203
|
+
metadata: null,
|
|
25204
|
+
task_references: [],
|
|
25205
|
+
conflicts: []
|
|
25206
|
+
};
|
|
25207
|
+
}
|
|
25208
|
+
try {
|
|
25209
|
+
const artifact = parsePlanArtifactMarkdown(readFileSync7(paths.file_path, "utf8"));
|
|
25210
|
+
return {
|
|
25211
|
+
path: paths.file_path,
|
|
25212
|
+
exists: true,
|
|
25213
|
+
parse_error: null,
|
|
25214
|
+
metadata: artifact.metadata,
|
|
25215
|
+
task_references: artifact.task_references,
|
|
25216
|
+
conflicts: comparePlanArtifact(plan, artifact, listTasks({ plan_id: plan.id, include_archived: true }, d))
|
|
25217
|
+
};
|
|
25218
|
+
} catch (error) {
|
|
25219
|
+
return {
|
|
25220
|
+
path: paths.file_path,
|
|
25221
|
+
exists: true,
|
|
25222
|
+
parse_error: error instanceof Error ? error.message : String(error),
|
|
25223
|
+
metadata: null,
|
|
25224
|
+
task_references: [],
|
|
25225
|
+
conflicts: []
|
|
25226
|
+
};
|
|
25227
|
+
}
|
|
25228
|
+
}
|
|
25229
|
+
function comparePlanArtifact(plan, artifact, tasks) {
|
|
25230
|
+
const conflicts = [];
|
|
25231
|
+
compare("plan_id", plan.id, artifact.metadata.plan_id, conflicts);
|
|
25232
|
+
compare("project_id", plan.project_id ?? null, artifact.metadata.project_id, conflicts);
|
|
25233
|
+
compare("name", plan.name, artifact.metadata.name, conflicts);
|
|
25234
|
+
compare("status", plan.status, artifact.metadata.status, conflicts);
|
|
25235
|
+
compare("updated_at", plan.updated_at, artifact.metadata.updated_at, conflicts);
|
|
25236
|
+
const dbTaskIds = tasks.map((task2) => task2.id).sort();
|
|
25237
|
+
const artifactTaskIds = artifact.task_references.map((task2) => task2.task_id).sort();
|
|
25238
|
+
if (dbTaskIds.join(",") !== artifactTaskIds.join(",")) {
|
|
25239
|
+
conflicts.push({
|
|
25240
|
+
field: "task_references",
|
|
25241
|
+
database: dbTaskIds.join(",") || null,
|
|
25242
|
+
artifact: artifactTaskIds.join(",") || null
|
|
25243
|
+
});
|
|
25244
|
+
}
|
|
25245
|
+
return conflicts;
|
|
25246
|
+
}
|
|
25247
|
+
function compare(field2, database, artifact, conflicts) {
|
|
25248
|
+
if ((database ?? null) !== (artifact ?? null)) {
|
|
25249
|
+
conflicts.push({ field: field2, database: database ?? null, artifact: artifact ?? null });
|
|
25250
|
+
}
|
|
25251
|
+
}
|
|
24964
25252
|
// src/db/project-knowledge.ts
|
|
24965
25253
|
init_database();
|
|
24966
25254
|
|
|
@@ -25989,8 +26277,8 @@ function renderRetrospectiveMarkdown(record) {
|
|
|
25989
26277
|
}
|
|
25990
26278
|
// src/lib/project-bootstrap.ts
|
|
25991
26279
|
init_database();
|
|
25992
|
-
import { existsSync as
|
|
25993
|
-
import { basename as basename2, dirname as dirname7, resolve as
|
|
26280
|
+
import { existsSync as existsSync10, readFileSync as readFileSync8, statSync as statSync3 } from "fs";
|
|
26281
|
+
import { basename as basename2, dirname as dirname7, resolve as resolve11 } from "path";
|
|
25994
26282
|
function safeStat(path) {
|
|
25995
26283
|
try {
|
|
25996
26284
|
return statSync3(path);
|
|
@@ -25999,7 +26287,7 @@ function safeStat(path) {
|
|
|
25999
26287
|
}
|
|
26000
26288
|
}
|
|
26001
26289
|
function canonicalPath(input) {
|
|
26002
|
-
const resolved =
|
|
26290
|
+
const resolved = resolve11(input);
|
|
26003
26291
|
const stats2 = safeStat(resolved);
|
|
26004
26292
|
if (stats2?.isFile())
|
|
26005
26293
|
return dirname7(resolved);
|
|
@@ -26008,7 +26296,7 @@ function canonicalPath(input) {
|
|
|
26008
26296
|
function findUp(start, marker) {
|
|
26009
26297
|
let current = canonicalPath(start);
|
|
26010
26298
|
while (true) {
|
|
26011
|
-
if (
|
|
26299
|
+
if (existsSync10(resolve11(current, marker)))
|
|
26012
26300
|
return current;
|
|
26013
26301
|
const parent = dirname7(current);
|
|
26014
26302
|
if (parent === current)
|
|
@@ -26019,11 +26307,11 @@ function findUp(start, marker) {
|
|
|
26019
26307
|
function readPackageJson2(path) {
|
|
26020
26308
|
if (!path)
|
|
26021
26309
|
return null;
|
|
26022
|
-
const file =
|
|
26023
|
-
if (!
|
|
26310
|
+
const file = resolve11(path, "package.json");
|
|
26311
|
+
if (!existsSync10(file))
|
|
26024
26312
|
return null;
|
|
26025
26313
|
try {
|
|
26026
|
-
const parsed = JSON.parse(
|
|
26314
|
+
const parsed = JSON.parse(readFileSync8(file, "utf-8"));
|
|
26027
26315
|
return parsed && typeof parsed === "object" ? parsed : null;
|
|
26028
26316
|
} catch {
|
|
26029
26317
|
return null;
|
|
@@ -26042,7 +26330,7 @@ function workspaceMarker(root, rootPackage) {
|
|
|
26042
26330
|
if (rootPackage?.workspaces)
|
|
26043
26331
|
markers.push("package.json#workspaces");
|
|
26044
26332
|
for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
|
|
26045
|
-
if (
|
|
26333
|
+
if (existsSync10(resolve11(root, marker)))
|
|
26046
26334
|
markers.push(marker);
|
|
26047
26335
|
}
|
|
26048
26336
|
const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
|
|
@@ -26535,21 +26823,21 @@ var gatherTrainingData = async (options = {}) => {
|
|
|
26535
26823
|
};
|
|
26536
26824
|
// src/lib/model-config.ts
|
|
26537
26825
|
init_sync_utils();
|
|
26538
|
-
import { existsSync as
|
|
26539
|
-
import { join as
|
|
26826
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
|
|
26827
|
+
import { join as join11 } from "path";
|
|
26540
26828
|
var DEFAULT_MODEL = "gpt-4o-mini";
|
|
26541
26829
|
function getConfigDir() {
|
|
26542
26830
|
return getTodosGlobalDir();
|
|
26543
26831
|
}
|
|
26544
26832
|
function getConfigPath2() {
|
|
26545
|
-
return
|
|
26833
|
+
return join11(getConfigDir(), "config.json");
|
|
26546
26834
|
}
|
|
26547
26835
|
function readConfig() {
|
|
26548
26836
|
const configPath = getConfigPath2();
|
|
26549
|
-
if (!
|
|
26837
|
+
if (!existsSync11(configPath))
|
|
26550
26838
|
return {};
|
|
26551
26839
|
try {
|
|
26552
|
-
const raw =
|
|
26840
|
+
const raw = readFileSync9(configPath, "utf-8");
|
|
26553
26841
|
return JSON.parse(raw);
|
|
26554
26842
|
} catch {
|
|
26555
26843
|
return {};
|
|
@@ -26557,10 +26845,10 @@ function readConfig() {
|
|
|
26557
26845
|
}
|
|
26558
26846
|
function writeConfig(config) {
|
|
26559
26847
|
const configDir = getConfigDir();
|
|
26560
|
-
if (!
|
|
26561
|
-
|
|
26848
|
+
if (!existsSync11(configDir)) {
|
|
26849
|
+
mkdirSync9(configDir, { recursive: true });
|
|
26562
26850
|
}
|
|
26563
|
-
|
|
26851
|
+
writeFileSync7(getConfigPath2(), JSON.stringify(config, null, 2) + `
|
|
26564
26852
|
`, "utf-8");
|
|
26565
26853
|
}
|
|
26566
26854
|
function getActiveModel() {
|
|
@@ -27282,7 +27570,7 @@ CLI equivalent: \`${r.equivalent_cli}\`
|
|
|
27282
27570
|
`);
|
|
27283
27571
|
}
|
|
27284
27572
|
// src/lib/verification-providers.ts
|
|
27285
|
-
import { existsSync as
|
|
27573
|
+
import { existsSync as existsSync12, readFileSync as readFileSync10 } from "fs";
|
|
27286
27574
|
init_database();
|
|
27287
27575
|
init_config();
|
|
27288
27576
|
init_redaction();
|
|
@@ -27393,7 +27681,7 @@ function classifyLog(text) {
|
|
|
27393
27681
|
async function sleep2(ms) {
|
|
27394
27682
|
if (ms <= 0)
|
|
27395
27683
|
return;
|
|
27396
|
-
await new Promise((
|
|
27684
|
+
await new Promise((resolve12) => setTimeout(resolve12, ms));
|
|
27397
27685
|
}
|
|
27398
27686
|
async function runCommandProvider(provider, input) {
|
|
27399
27687
|
const commandTemplate = input.command || provider.command;
|
|
@@ -27448,7 +27736,7 @@ Timed out after ${provider.timeout_ms}ms`);
|
|
|
27448
27736
|
};
|
|
27449
27737
|
}
|
|
27450
27738
|
function runCiLogProvider(input) {
|
|
27451
|
-
const text = input.log_text ?? (input.log_path &&
|
|
27739
|
+
const text = input.log_text ?? (input.log_path && existsSync12(input.log_path) ? readFileSync10(input.log_path, "utf-8") : "");
|
|
27452
27740
|
return {
|
|
27453
27741
|
status: classifyLog(text),
|
|
27454
27742
|
attempts: 1,
|
|
@@ -27460,7 +27748,7 @@ function runBrowserProvider(input) {
|
|
|
27460
27748
|
if (!input.artifact_path) {
|
|
27461
27749
|
return { status: "unknown", attempts: 1, exit_code: null, output_summary: "browser provider needs a screenshot or artifact path" };
|
|
27462
27750
|
}
|
|
27463
|
-
if (!
|
|
27751
|
+
if (!existsSync12(input.artifact_path)) {
|
|
27464
27752
|
return { status: "failed", attempts: 1, exit_code: null, output_summary: `artifact not found: ${input.artifact_path}` };
|
|
27465
27753
|
}
|
|
27466
27754
|
return {
|
|
@@ -27624,7 +27912,7 @@ function listVerificationRecords(filter = {}, db) {
|
|
|
27624
27912
|
}
|
|
27625
27913
|
// src/lib/verification-evidence.ts
|
|
27626
27914
|
init_database();
|
|
27627
|
-
import { writeFileSync as
|
|
27915
|
+
import { writeFileSync as writeFileSync8, mkdirSync as mkdirSync10 } from "fs";
|
|
27628
27916
|
import { dirname as dirname8 } from "path";
|
|
27629
27917
|
var VERIFICATION_EVIDENCE_SCHEMA = "todos.verification_evidence.v1";
|
|
27630
27918
|
function getMachineId3() {
|
|
@@ -27745,15 +28033,15 @@ function exportVerificationEvidence(filter = {}, db) {
|
|
|
27745
28033
|
};
|
|
27746
28034
|
}
|
|
27747
28035
|
function writeVerificationExport(bundle, path) {
|
|
27748
|
-
|
|
27749
|
-
|
|
28036
|
+
mkdirSync10(dirname8(path), { recursive: true });
|
|
28037
|
+
writeFileSync8(path, JSON.stringify(bundle, null, 2), "utf8");
|
|
27750
28038
|
}
|
|
27751
28039
|
// src/lib/policy-packs.ts
|
|
27752
|
-
import { relative as relative3, resolve as
|
|
28040
|
+
import { relative as relative3, resolve as resolve12 } from "path";
|
|
27753
28041
|
init_database();
|
|
27754
28042
|
init_config();
|
|
27755
28043
|
function normalizePath3(path) {
|
|
27756
|
-
return
|
|
28044
|
+
return resolve12(path);
|
|
27757
28045
|
}
|
|
27758
28046
|
function unique4(values) {
|
|
27759
28047
|
return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
|
|
@@ -27808,7 +28096,7 @@ function commandMatches(commands, pattern) {
|
|
|
27808
28096
|
}
|
|
27809
28097
|
function pathMatches(paths, pattern, root) {
|
|
27810
28098
|
return paths.filter((path) => {
|
|
27811
|
-
const candidate = path.startsWith("/") ? path :
|
|
28099
|
+
const candidate = path.startsWith("/") ? path : resolve12(root, path);
|
|
27812
28100
|
if (!isPathInside3(root, candidate))
|
|
27813
28101
|
return matchesPattern3(path, pattern);
|
|
27814
28102
|
return matchesPattern3(path, pattern) || matchesPattern3(relative3(root, candidate), pattern);
|
|
@@ -28099,21 +28387,21 @@ function resourceDiagnostics() {
|
|
|
28099
28387
|
};
|
|
28100
28388
|
}
|
|
28101
28389
|
// src/lib/sandbox-profiles.ts
|
|
28102
|
-
import { existsSync as
|
|
28103
|
-
import { join as
|
|
28390
|
+
import { existsSync as existsSync13, readFileSync as readFileSync11, writeFileSync as writeFileSync9, mkdirSync as mkdirSync11 } from "fs";
|
|
28391
|
+
import { join as join12, dirname as dirname9 } from "path";
|
|
28104
28392
|
var SANDBOX_PROFILE_VERSION = "todos.sandbox-profile.v1";
|
|
28105
28393
|
function getProfilesPath() {
|
|
28106
28394
|
if (process.env["TODOS_SANDBOX_PROFILES_PATH"]) {
|
|
28107
28395
|
return process.env["TODOS_SANDBOX_PROFILES_PATH"];
|
|
28108
28396
|
}
|
|
28109
|
-
const localDir =
|
|
28110
|
-
const local =
|
|
28111
|
-
if (
|
|
28397
|
+
const localDir = join12(process.cwd(), ".todos");
|
|
28398
|
+
const local = join12(localDir, "sandbox-profiles.json");
|
|
28399
|
+
if (existsSync13(localDir))
|
|
28112
28400
|
return local;
|
|
28113
|
-
if (
|
|
28401
|
+
if (existsSync13(local))
|
|
28114
28402
|
return local;
|
|
28115
28403
|
const home = process.env["HOME"] || "~";
|
|
28116
|
-
return
|
|
28404
|
+
return join12(home, ".hasna", "todos", "sandbox-profiles.json");
|
|
28117
28405
|
}
|
|
28118
28406
|
var cached2 = null;
|
|
28119
28407
|
function resetSandboxProfileCache() {
|
|
@@ -28145,11 +28433,11 @@ function loadSandboxProfiles() {
|
|
|
28145
28433
|
if (cached2)
|
|
28146
28434
|
return cached2;
|
|
28147
28435
|
const path = getProfilesPath();
|
|
28148
|
-
if (!
|
|
28436
|
+
if (!existsSync13(path)) {
|
|
28149
28437
|
cached2 = getDefaultSandboxProfiles();
|
|
28150
28438
|
return cached2;
|
|
28151
28439
|
}
|
|
28152
|
-
const parsed = JSON.parse(
|
|
28440
|
+
const parsed = JSON.parse(readFileSync11(path, "utf8"));
|
|
28153
28441
|
cached2 = parsed.profiles?.length ? parsed.profiles : getDefaultSandboxProfiles();
|
|
28154
28442
|
return cached2;
|
|
28155
28443
|
}
|
|
@@ -28158,8 +28446,8 @@ function getSandboxProfile(name) {
|
|
|
28158
28446
|
}
|
|
28159
28447
|
function saveSandboxProfiles(profiles) {
|
|
28160
28448
|
const path = getProfilesPath();
|
|
28161
|
-
|
|
28162
|
-
|
|
28449
|
+
mkdirSync11(dirname9(path), { recursive: true });
|
|
28450
|
+
writeFileSync9(path, JSON.stringify({ schema_version: SANDBOX_PROFILE_VERSION, profiles }, null, 2));
|
|
28163
28451
|
cached2 = profiles;
|
|
28164
28452
|
}
|
|
28165
28453
|
function commandMatchesAllowlist(command, allow) {
|
|
@@ -28518,9 +28806,9 @@ function getDefaultAgentAdapters() {
|
|
|
28518
28806
|
}
|
|
28519
28807
|
function resetAgentAdapterCache() {}
|
|
28520
28808
|
// src/lib/git-traceability.ts
|
|
28521
|
-
import { existsSync as
|
|
28809
|
+
import { existsSync as existsSync14, readFileSync as readFileSync12 } from "fs";
|
|
28522
28810
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
28523
|
-
import { resolve as
|
|
28811
|
+
import { resolve as resolve13 } from "path";
|
|
28524
28812
|
var GIT_TRACEABILITY_SCHEMA_VERSION = "todos.git_traceability.v1";
|
|
28525
28813
|
function runGit(args, cwd) {
|
|
28526
28814
|
const result = spawnSync2("git", args, { cwd, encoding: "utf8" });
|
|
@@ -28563,11 +28851,11 @@ function inspectGitCommit(sha, cwd) {
|
|
|
28563
28851
|
};
|
|
28564
28852
|
}
|
|
28565
28853
|
function loadCiSnapshot(path) {
|
|
28566
|
-
const target = path ?
|
|
28567
|
-
if (!
|
|
28854
|
+
const target = path ? resolve13(path) : resolve13(process.cwd(), ".todos", "ci-snapshot.json");
|
|
28855
|
+
if (!existsSync14(target))
|
|
28568
28856
|
return null;
|
|
28569
28857
|
try {
|
|
28570
|
-
const parsed = JSON.parse(
|
|
28858
|
+
const parsed = JSON.parse(readFileSync12(target, "utf8"));
|
|
28571
28859
|
return { ...parsed, captured_at: parsed.captured_at ?? new Date().toISOString() };
|
|
28572
28860
|
} catch {
|
|
28573
28861
|
return null;
|
|
@@ -28660,8 +28948,8 @@ function formatTraceabilityReport(report) {
|
|
|
28660
28948
|
`);
|
|
28661
28949
|
}
|
|
28662
28950
|
// src/lib/mention-resolver.ts
|
|
28663
|
-
import { existsSync as
|
|
28664
|
-
import { basename as basename3, isAbsolute, join as
|
|
28951
|
+
import { existsSync as existsSync15, readdirSync as readdirSync2, readFileSync as readFileSync13, statSync as statSync4 } from "fs";
|
|
28952
|
+
import { basename as basename3, isAbsolute, join as join13, relative as relative4, resolve as resolve14, sep as sep2 } from "path";
|
|
28665
28953
|
init_database();
|
|
28666
28954
|
var PREFIXES = {
|
|
28667
28955
|
file: "file",
|
|
@@ -28737,7 +29025,7 @@ function backlink(kind, key, label, target = key) {
|
|
|
28737
29025
|
return { kind, key, label, target };
|
|
28738
29026
|
}
|
|
28739
29027
|
function normalizeWorkspace(workspace) {
|
|
28740
|
-
return
|
|
29028
|
+
return resolve14(workspace || process.cwd());
|
|
28741
29029
|
}
|
|
28742
29030
|
function isInside(root, absolutePath) {
|
|
28743
29031
|
const rel = relative4(root, absolutePath);
|
|
@@ -28805,14 +29093,14 @@ function resolveFile(parsed, workspace) {
|
|
|
28805
29093
|
resolution.warnings.push("path is empty or escapes the workspace");
|
|
28806
29094
|
return resolution;
|
|
28807
29095
|
}
|
|
28808
|
-
const absolutePath =
|
|
29096
|
+
const absolutePath = resolve14(workspace, relPath);
|
|
28809
29097
|
if (!isInside(workspace, absolutePath)) {
|
|
28810
29098
|
resolution.path = relPath;
|
|
28811
29099
|
resolution.warnings.push("path escapes the workspace");
|
|
28812
29100
|
return resolution;
|
|
28813
29101
|
}
|
|
28814
29102
|
resolution.path = relPath;
|
|
28815
|
-
if (!
|
|
29103
|
+
if (!existsSync15(absolutePath)) {
|
|
28816
29104
|
resolution.warnings.push("file does not exist in the local workspace");
|
|
28817
29105
|
return resolution;
|
|
28818
29106
|
}
|
|
@@ -28822,7 +29110,7 @@ function resolveFile(parsed, workspace) {
|
|
|
28822
29110
|
return resolution;
|
|
28823
29111
|
}
|
|
28824
29112
|
if (parsed.line !== undefined) {
|
|
28825
|
-
const lineCount =
|
|
29113
|
+
const lineCount = readFileSync13(absolutePath, "utf-8").split(/\r?\n/).length;
|
|
28826
29114
|
if (parsed.line < 1 || parsed.line > lineCount) {
|
|
28827
29115
|
resolution.warnings.push(`line ${parsed.line} is outside the file range 1-${lineCount}`);
|
|
28828
29116
|
return resolution;
|
|
@@ -28845,7 +29133,7 @@ function walkSourceFiles(root, current = root, files = []) {
|
|
|
28845
29133
|
if (SKIP_DIRS.has(entry2.name))
|
|
28846
29134
|
continue;
|
|
28847
29135
|
}
|
|
28848
|
-
const absolutePath =
|
|
29136
|
+
const absolutePath = join13(current, entry2.name);
|
|
28849
29137
|
if (entry2.isDirectory()) {
|
|
28850
29138
|
if (!SKIP_DIRS.has(entry2.name))
|
|
28851
29139
|
walkSourceFiles(root, absolutePath, files);
|
|
@@ -28875,7 +29163,7 @@ function resolveSymbol(parsed, workspace, maxMatches) {
|
|
|
28875
29163
|
const pattern = symbolPattern(name);
|
|
28876
29164
|
const matches = [];
|
|
28877
29165
|
for (const file of walkSourceFiles(workspace)) {
|
|
28878
|
-
const lines =
|
|
29166
|
+
const lines = readFileSync13(file, "utf-8").split(/\r?\n/);
|
|
28879
29167
|
for (let index = 0;index < lines.length; index += 1) {
|
|
28880
29168
|
const line = lines[index];
|
|
28881
29169
|
const found = pattern.exec(line);
|
|
@@ -30725,7 +31013,7 @@ function getAdapterDocsFingerprint() {
|
|
|
30725
31013
|
}
|
|
30726
31014
|
// src/lib/inbox-intake.ts
|
|
30727
31015
|
init_database();
|
|
30728
|
-
import { existsSync as
|
|
31016
|
+
import { existsSync as existsSync16, readFileSync as readFileSync14 } from "fs";
|
|
30729
31017
|
import { basename as basename4 } from "path";
|
|
30730
31018
|
import { createHash as createHash11 } from "crypto";
|
|
30731
31019
|
init_secret_redaction();
|
|
@@ -30771,9 +31059,9 @@ function loadRawContent(input) {
|
|
|
30771
31059
|
}
|
|
30772
31060
|
}
|
|
30773
31061
|
if (input.file_path) {
|
|
30774
|
-
if (!
|
|
31062
|
+
if (!existsSync16(input.file_path))
|
|
30775
31063
|
throw new Error(`File not found: ${input.file_path}`);
|
|
30776
|
-
const raw =
|
|
31064
|
+
const raw = readFileSync14(input.file_path, "utf8");
|
|
30777
31065
|
const name = basename4(input.file_path).toLowerCase();
|
|
30778
31066
|
const source_type2 = input.source_type ?? (name.includes("ci") || name.endsWith(".log") ? "ci_log" : "file");
|
|
30779
31067
|
return {
|
|
@@ -31389,7 +31677,7 @@ function formatNlIntakePreviewText(preview) {
|
|
|
31389
31677
|
}
|
|
31390
31678
|
// src/lib/issue-importers.ts
|
|
31391
31679
|
init_database();
|
|
31392
|
-
import { existsSync as
|
|
31680
|
+
import { existsSync as existsSync17, readFileSync as readFileSync15 } from "fs";
|
|
31393
31681
|
var ISSUE_IMPORT_SCHEMA = "todos.issue_import.v1";
|
|
31394
31682
|
var ISSUE_SOURCES = ["github", "linear", "jira", "auto"];
|
|
31395
31683
|
var GITHUB_LABEL_PRIORITY = {
|
|
@@ -31608,9 +31896,9 @@ function parseIssueExport(data, source9 = "auto") {
|
|
|
31608
31896
|
return normalized;
|
|
31609
31897
|
}
|
|
31610
31898
|
function loadIssueExportFromFile(path) {
|
|
31611
|
-
if (!
|
|
31899
|
+
if (!existsSync17(path))
|
|
31612
31900
|
throw new Error(`File not found: ${path}`);
|
|
31613
|
-
return JSON.parse(
|
|
31901
|
+
return JSON.parse(readFileSync15(path, "utf8"));
|
|
31614
31902
|
}
|
|
31615
31903
|
function loadIssueExportInput(input) {
|
|
31616
31904
|
if (input.file_path) {
|
|
@@ -31768,8 +32056,8 @@ todos import issues ./linear.json --source linear --dry-run
|
|
|
31768
32056
|
// src/lib/run-records.ts
|
|
31769
32057
|
init_database();
|
|
31770
32058
|
init_secret_redaction();
|
|
31771
|
-
import { existsSync as
|
|
31772
|
-
import { join as
|
|
32059
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync12, writeFileSync as writeFileSync10 } from "fs";
|
|
32060
|
+
import { join as join14, dirname as dirname10 } from "path";
|
|
31773
32061
|
var RUN_RECORD_SCHEMA = "todos.run_record.v1";
|
|
31774
32062
|
var RUN_RECORD_STATUSES = ["active", "completed", "failed", "archived"];
|
|
31775
32063
|
function parseJsonArray3(raw, fallback = []) {
|
|
@@ -31962,9 +32250,9 @@ function buildRunReplayBundle(id, db) {
|
|
|
31962
32250
|
}
|
|
31963
32251
|
function exportRunReplay(id, outputPath, db) {
|
|
31964
32252
|
const bundle = buildRunReplayBundle(id, db);
|
|
31965
|
-
const path = outputPath ??
|
|
31966
|
-
|
|
31967
|
-
|
|
32253
|
+
const path = outputPath ?? join14(process.cwd(), ".todos", "replays", `${id.slice(0, 8)}.json`);
|
|
32254
|
+
mkdirSync12(dirname10(path), { recursive: true });
|
|
32255
|
+
writeFileSync10(path, JSON.stringify(bundle, null, 2));
|
|
31968
32256
|
const d = db || getDatabase();
|
|
31969
32257
|
d.run(`UPDATE run_records SET replay_bundle = ?, updated_at = ? WHERE id = ?`, [path, now(), id]);
|
|
31970
32258
|
return { path, bundle };
|
|
@@ -32002,16 +32290,16 @@ function formatRunRecordMarkdown(record) {
|
|
|
32002
32290
|
`;
|
|
32003
32291
|
}
|
|
32004
32292
|
function getDefaultReplayDir() {
|
|
32005
|
-
const local =
|
|
32006
|
-
if (
|
|
32293
|
+
const local = join14(process.cwd(), ".todos", "replays");
|
|
32294
|
+
if (existsSync18(join14(process.cwd(), ".todos")))
|
|
32007
32295
|
return local;
|
|
32008
32296
|
const home = process.env["HOME"] || "~";
|
|
32009
|
-
return
|
|
32297
|
+
return join14(home, ".hasna", "todos", "replays");
|
|
32010
32298
|
}
|
|
32011
32299
|
// src/lib/release-checks.ts
|
|
32012
32300
|
init_secret_redaction();
|
|
32013
|
-
import { existsSync as
|
|
32014
|
-
import { join as
|
|
32301
|
+
import { existsSync as existsSync19, readFileSync as readFileSync16, readdirSync as readdirSync3, statSync as statSync5 } from "fs";
|
|
32302
|
+
import { join as join15, relative as relative5 } from "path";
|
|
32015
32303
|
var RELEASE_CHECK_SCHEMA = "todos.release_check.v1";
|
|
32016
32304
|
var FORBIDDEN_DIST_PATTERNS = [
|
|
32017
32305
|
{
|
|
@@ -32024,16 +32312,16 @@ var FORBIDDEN_DIST_PATTERNS = [
|
|
|
32024
32312
|
];
|
|
32025
32313
|
var REQUIRED_BINS = ["todos", "todos-mcp", "todos-serve"];
|
|
32026
32314
|
function readPackageJson3(root) {
|
|
32027
|
-
const path =
|
|
32028
|
-
if (!
|
|
32315
|
+
const path = join15(root, "package.json");
|
|
32316
|
+
if (!existsSync19(path))
|
|
32029
32317
|
throw new Error(`package.json not found in ${root}`);
|
|
32030
|
-
return JSON.parse(
|
|
32318
|
+
return JSON.parse(readFileSync16(path, "utf8"));
|
|
32031
32319
|
}
|
|
32032
32320
|
function walkFiles(dir, acc = []) {
|
|
32033
|
-
if (!
|
|
32321
|
+
if (!existsSync19(dir))
|
|
32034
32322
|
return acc;
|
|
32035
32323
|
for (const entry2 of readdirSync3(dir)) {
|
|
32036
|
-
const full =
|
|
32324
|
+
const full = join15(dir, entry2);
|
|
32037
32325
|
const st = statSync5(full);
|
|
32038
32326
|
if (st.isDirectory())
|
|
32039
32327
|
walkFiles(full, acc);
|
|
@@ -32051,8 +32339,8 @@ function auditPackageContents(root) {
|
|
|
32051
32339
|
checks.push({ id: "files_dist", severity: "error", message: "package.json files must include dist" });
|
|
32052
32340
|
}
|
|
32053
32341
|
for (const pattern of files) {
|
|
32054
|
-
const target =
|
|
32055
|
-
if (!
|
|
32342
|
+
const target = join15(root, pattern);
|
|
32343
|
+
if (!existsSync19(target)) {
|
|
32056
32344
|
checks.push({ id: `files_missing_${pattern}`, severity: "error", message: `Published file path missing: ${pattern}` });
|
|
32057
32345
|
}
|
|
32058
32346
|
}
|
|
@@ -32066,8 +32354,8 @@ function auditPackageContents(root) {
|
|
|
32066
32354
|
checks.push({ id: `bin_${name}`, severity: "error", message: `Missing bin entry: ${name}` });
|
|
32067
32355
|
continue;
|
|
32068
32356
|
}
|
|
32069
|
-
const binPath =
|
|
32070
|
-
if (!
|
|
32357
|
+
const binPath = join15(root, rel);
|
|
32358
|
+
if (!existsSync19(binPath)) {
|
|
32071
32359
|
checks.push({ id: `bin_path_${name}`, severity: "error", message: `Bin file missing: ${rel}` });
|
|
32072
32360
|
} else {
|
|
32073
32361
|
checks.push({ id: `bin_ok_${name}`, severity: "info", message: `Bin present: ${name} \u2192 ${rel}` });
|
|
@@ -32084,8 +32372,8 @@ function auditPackageContents(root) {
|
|
|
32084
32372
|
}
|
|
32085
32373
|
function scanDistArtifacts(root) {
|
|
32086
32374
|
const checks = [];
|
|
32087
|
-
const distDir =
|
|
32088
|
-
if (!
|
|
32375
|
+
const distDir = join15(root, "dist");
|
|
32376
|
+
if (!existsSync19(distDir)) {
|
|
32089
32377
|
checks.push({ id: "dist_missing", severity: "error", message: "dist/ directory not found \u2014 run bun run build" });
|
|
32090
32378
|
return checks;
|
|
32091
32379
|
}
|
|
@@ -32093,7 +32381,7 @@ function scanDistArtifacts(root) {
|
|
|
32093
32381
|
const rel = relative5(root, file);
|
|
32094
32382
|
let content;
|
|
32095
32383
|
try {
|
|
32096
|
-
content =
|
|
32384
|
+
content = readFileSync16(file, "utf8");
|
|
32097
32385
|
} catch {
|
|
32098
32386
|
continue;
|
|
32099
32387
|
}
|
|
@@ -32391,15 +32679,15 @@ function renderReleaseNotesMarkdown(document) {
|
|
|
32391
32679
|
// src/lib/db-backup.ts
|
|
32392
32680
|
init_database();
|
|
32393
32681
|
init_migrations();
|
|
32394
|
-
import { existsSync as
|
|
32395
|
-
import { dirname as dirname11, join as
|
|
32682
|
+
import { existsSync as existsSync20, copyFileSync, mkdirSync as mkdirSync13, readFileSync as readFileSync17, statSync as statSync6, writeFileSync as writeFileSync11, unlinkSync } from "fs";
|
|
32683
|
+
import { dirname as dirname11, join as join16, resolve as resolve15 } from "path";
|
|
32396
32684
|
import { Database as Database3 } from "bun:sqlite";
|
|
32397
32685
|
var DB_BACKUP_SCHEMA = "todos.db_backup.v1";
|
|
32398
32686
|
function resolveDbPath(dbPath) {
|
|
32399
32687
|
if (dbPath)
|
|
32400
|
-
return
|
|
32688
|
+
return resolve15(dbPath);
|
|
32401
32689
|
if (process.env["TODOS_DB_PATH"] && process.env["TODOS_DB_PATH"] !== ":memory:") {
|
|
32402
|
-
return
|
|
32690
|
+
return resolve15(process.env["TODOS_DB_PATH"]);
|
|
32403
32691
|
}
|
|
32404
32692
|
const db = getDatabase();
|
|
32405
32693
|
const filename = db.filename;
|
|
@@ -32409,9 +32697,9 @@ function resolveDbPath(dbPath) {
|
|
|
32409
32697
|
}
|
|
32410
32698
|
function backupDatabase(outputPath, sourcePath) {
|
|
32411
32699
|
const source9 = resolveDbPath(sourcePath);
|
|
32412
|
-
if (!
|
|
32700
|
+
if (!existsSync20(source9))
|
|
32413
32701
|
throw new Error(`Database not found: ${source9}`);
|
|
32414
|
-
|
|
32702
|
+
mkdirSync13(dirname11(outputPath), { recursive: true });
|
|
32415
32703
|
closeDatabase();
|
|
32416
32704
|
const src = new Database3(source9);
|
|
32417
32705
|
try {
|
|
@@ -32419,7 +32707,7 @@ function backupDatabase(outputPath, sourcePath) {
|
|
|
32419
32707
|
} catch {}
|
|
32420
32708
|
const image = src.serialize();
|
|
32421
32709
|
src.close();
|
|
32422
|
-
|
|
32710
|
+
writeFileSync11(outputPath, image);
|
|
32423
32711
|
const method = "file_copy";
|
|
32424
32712
|
const bytes = statSync6(outputPath).size;
|
|
32425
32713
|
return {
|
|
@@ -32432,14 +32720,14 @@ function backupDatabase(outputPath, sourcePath) {
|
|
|
32432
32720
|
};
|
|
32433
32721
|
}
|
|
32434
32722
|
function restoreDatabase(backupPath, targetPath) {
|
|
32435
|
-
if (!
|
|
32723
|
+
if (!existsSync20(backupPath))
|
|
32436
32724
|
throw new Error(`Backup not found: ${backupPath}`);
|
|
32437
32725
|
const integrity = checkDatabaseIntegrity(backupPath);
|
|
32438
32726
|
if (!integrity.ok) {
|
|
32439
32727
|
throw new Error(`Backup failed integrity check: ${integrity.errors.join("; ")}`);
|
|
32440
32728
|
}
|
|
32441
|
-
const target = targetPath ?
|
|
32442
|
-
|
|
32729
|
+
const target = targetPath ? resolve15(targetPath) : resolveDbPath();
|
|
32730
|
+
mkdirSync13(dirname11(target), { recursive: true });
|
|
32443
32731
|
const staging = `${target}.restore.tmp`;
|
|
32444
32732
|
copyFileSync(backupPath, staging);
|
|
32445
32733
|
copyFileSync(staging, target);
|
|
@@ -32457,9 +32745,9 @@ function restoreDatabase(backupPath, targetPath) {
|
|
|
32457
32745
|
};
|
|
32458
32746
|
}
|
|
32459
32747
|
function checkDatabaseIntegrity(dbPath) {
|
|
32460
|
-
const path = dbPath ?
|
|
32748
|
+
const path = dbPath ? resolve15(dbPath) : resolveDbPath();
|
|
32461
32749
|
const errors = [];
|
|
32462
|
-
if (!
|
|
32750
|
+
if (!existsSync20(path)) {
|
|
32463
32751
|
return {
|
|
32464
32752
|
schema_version: DB_BACKUP_SCHEMA,
|
|
32465
32753
|
path,
|
|
@@ -32522,7 +32810,7 @@ function checkDatabaseIntegrity(dbPath) {
|
|
|
32522
32810
|
};
|
|
32523
32811
|
}
|
|
32524
32812
|
function compactDatabase(dbPath) {
|
|
32525
|
-
const path = dbPath ?
|
|
32813
|
+
const path = dbPath ? resolve15(dbPath) : resolveDbPath();
|
|
32526
32814
|
const before = statSync6(path).size;
|
|
32527
32815
|
const db = new Database3(path);
|
|
32528
32816
|
db.exec("VACUUM");
|
|
@@ -32532,7 +32820,7 @@ function compactDatabase(dbPath) {
|
|
|
32532
32820
|
return { path, bytes_before: before, bytes_after: after };
|
|
32533
32821
|
}
|
|
32534
32822
|
function migrationDryRun(dbPath) {
|
|
32535
|
-
const path = dbPath ?
|
|
32823
|
+
const path = dbPath ? resolve15(dbPath) : resolveDbPath();
|
|
32536
32824
|
const db = new Database3(path, { readonly: true });
|
|
32537
32825
|
let current = 0;
|
|
32538
32826
|
try {
|
|
@@ -32556,16 +32844,16 @@ function migrationDryRun(dbPath) {
|
|
|
32556
32844
|
};
|
|
32557
32845
|
}
|
|
32558
32846
|
function defaultBackupPath(dbPath) {
|
|
32559
|
-
const base = dbPath ? dirname11(
|
|
32847
|
+
const base = dbPath ? dirname11(resolve15(dbPath)) : dirname11(resolveDbPath());
|
|
32560
32848
|
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
32561
|
-
return
|
|
32849
|
+
return join16(base, "backups", `todos-${stamp}.db`);
|
|
32562
32850
|
}
|
|
32563
32851
|
function readBackupManifest(backupPath) {
|
|
32564
32852
|
const manifestPath = `${backupPath}.json`;
|
|
32565
|
-
if (!
|
|
32853
|
+
if (!existsSync20(manifestPath))
|
|
32566
32854
|
return null;
|
|
32567
32855
|
try {
|
|
32568
|
-
return JSON.parse(
|
|
32856
|
+
return JSON.parse(readFileSync17(manifestPath, "utf8"));
|
|
32569
32857
|
} catch {
|
|
32570
32858
|
return null;
|
|
32571
32859
|
}
|
|
@@ -32575,8 +32863,8 @@ function writeBackupManifest(backupPath, result) {
|
|
|
32575
32863
|
writeFileSyncSafe(manifestPath, JSON.stringify(result, null, 2));
|
|
32576
32864
|
}
|
|
32577
32865
|
function writeFileSyncSafe(path, content) {
|
|
32578
|
-
|
|
32579
|
-
|
|
32866
|
+
mkdirSync13(dirname11(path), { recursive: true });
|
|
32867
|
+
writeFileSync11(path, content);
|
|
32580
32868
|
}
|
|
32581
32869
|
// src/lib/json-schemas.ts
|
|
32582
32870
|
var JSON_SCHEMA_CATALOG_VERSION = "todos.json_schema_catalog.v1";
|
|
@@ -33081,17 +33369,17 @@ ${SCHEMA_ENTITIES.map((e) => `- **${e}**: \`${JSON_SCHEMAS[e].schema_version}\``
|
|
|
33081
33369
|
`;
|
|
33082
33370
|
}
|
|
33083
33371
|
function exportSchemasToDirectory(dir) {
|
|
33084
|
-
const { mkdirSync:
|
|
33085
|
-
const { join:
|
|
33086
|
-
|
|
33372
|
+
const { mkdirSync: mkdirSync14, writeFileSync: writeFileSync12 } = __require("fs");
|
|
33373
|
+
const { join: join17 } = __require("path");
|
|
33374
|
+
mkdirSync14(dir, { recursive: true });
|
|
33087
33375
|
const written = [];
|
|
33088
33376
|
for (const entity of SCHEMA_ENTITIES) {
|
|
33089
|
-
const path =
|
|
33090
|
-
|
|
33377
|
+
const path = join17(dir, `${entity}.${JSON_SCHEMAS[entity].schema_version.replace(/\./g, "-")}.json`);
|
|
33378
|
+
writeFileSync12(path, JSON.stringify(JSON_SCHEMAS[entity], null, 2));
|
|
33091
33379
|
written.push(path);
|
|
33092
33380
|
}
|
|
33093
|
-
const catalogPath =
|
|
33094
|
-
|
|
33381
|
+
const catalogPath = join17(dir, "catalog.json");
|
|
33382
|
+
writeFileSync12(catalogPath, JSON.stringify({
|
|
33095
33383
|
catalog_version: JSON_SCHEMA_CATALOG_VERSION,
|
|
33096
33384
|
semver: SCHEMA_SEMVER,
|
|
33097
33385
|
entities: listJsonSchemas(),
|
|
@@ -33990,7 +34278,7 @@ function getReminderDocs() {
|
|
|
33990
34278
|
}
|
|
33991
34279
|
// src/lib/import-export-bridge.ts
|
|
33992
34280
|
init_database();
|
|
33993
|
-
import { readFileSync as
|
|
34281
|
+
import { readFileSync as readFileSync18, writeFileSync as writeFileSync12, mkdirSync as mkdirSync14 } from "fs";
|
|
33994
34282
|
import { dirname as dirname12 } from "path";
|
|
33995
34283
|
init_secret_redaction();
|
|
33996
34284
|
var BUNDLE_SCHEMA = "todos.bundle.v1";
|
|
@@ -34399,11 +34687,11 @@ function importBundle(bundle, options = {}, db) {
|
|
|
34399
34687
|
return result;
|
|
34400
34688
|
}
|
|
34401
34689
|
function writeBundleFile(bundle, path) {
|
|
34402
|
-
|
|
34403
|
-
|
|
34690
|
+
mkdirSync14(dirname12(path), { recursive: true });
|
|
34691
|
+
writeFileSync12(path, JSON.stringify(bundle, null, 2), "utf8");
|
|
34404
34692
|
}
|
|
34405
34693
|
function readBundleFile(path) {
|
|
34406
|
-
const raw = JSON.parse(
|
|
34694
|
+
const raw = JSON.parse(readFileSync18(path, "utf8"));
|
|
34407
34695
|
const validation = validateBundle(raw);
|
|
34408
34696
|
if (!validation.valid)
|
|
34409
34697
|
throw new Error(`Invalid bundle file: ${validation.errors.join("; ")}`);
|
|
@@ -34879,7 +35167,7 @@ function createPlanWithSteps(name, steps, opts = {}, db) {
|
|
|
34879
35167
|
}
|
|
34880
35168
|
// src/lib/handoff-packets.ts
|
|
34881
35169
|
init_database();
|
|
34882
|
-
import { writeFileSync as
|
|
35170
|
+
import { writeFileSync as writeFileSync13, mkdirSync as mkdirSync15 } from "fs";
|
|
34883
35171
|
import { dirname as dirname13 } from "path";
|
|
34884
35172
|
var HANDOFF_PACKET_SCHEMA = "todos.handoff_packet.v1";
|
|
34885
35173
|
function summarizeTask2(t) {
|
|
@@ -35054,8 +35342,8 @@ function formatHandoffPacket(packet, format = "json") {
|
|
|
35054
35342
|
function exportHandoffPacket(input = {}, path, db) {
|
|
35055
35343
|
const packet = createHandoffPacket(input, db);
|
|
35056
35344
|
if (path) {
|
|
35057
|
-
|
|
35058
|
-
|
|
35345
|
+
mkdirSync15(dirname13(path), { recursive: true });
|
|
35346
|
+
writeFileSync13(path, formatHandoffPacket(packet, "json"), "utf8");
|
|
35059
35347
|
}
|
|
35060
35348
|
return packet;
|
|
35061
35349
|
}
|
|
@@ -35658,8 +35946,8 @@ function generateCliReferenceMarkdown() {
|
|
|
35658
35946
|
}
|
|
35659
35947
|
// src/db/builtin-templates.ts
|
|
35660
35948
|
init_database();
|
|
35661
|
-
import { mkdirSync as
|
|
35662
|
-
import { join as
|
|
35949
|
+
import { mkdirSync as mkdirSync16, writeFileSync as writeFileSync14 } from "fs";
|
|
35950
|
+
import { join as join17 } from "path";
|
|
35663
35951
|
var BUILTIN_TEMPLATE_LIBRARY_VERSION = "2026-05-21";
|
|
35664
35952
|
var BUILTIN_TEMPLATE_LIBRARY_SOURCE = "bundled-local-template-library";
|
|
35665
35953
|
var TEMPLATE_LIBRARY_SCHEMA = "todos.template_library.v1";
|
|
@@ -35932,11 +36220,11 @@ function exportBuiltinTemplateFiles() {
|
|
|
35932
36220
|
}));
|
|
35933
36221
|
}
|
|
35934
36222
|
function writeBuiltinTemplateFiles(directory) {
|
|
35935
|
-
|
|
36223
|
+
mkdirSync16(directory, { recursive: true });
|
|
35936
36224
|
const files = [];
|
|
35937
36225
|
for (const entry2 of exportBuiltinTemplateFiles()) {
|
|
35938
|
-
const path =
|
|
35939
|
-
|
|
36226
|
+
const path = join17(directory, entry2.filename);
|
|
36227
|
+
writeFileSync14(path, `${JSON.stringify(entry2.template, null, 2)}
|
|
35940
36228
|
`, "utf-8");
|
|
35941
36229
|
files.push(path);
|
|
35942
36230
|
}
|
|
@@ -35979,7 +36267,7 @@ function initBuiltinTemplates(db) {
|
|
|
35979
36267
|
}
|
|
35980
36268
|
// src/lib/template-library.ts
|
|
35981
36269
|
init_database();
|
|
35982
|
-
import { writeFileSync as
|
|
36270
|
+
import { writeFileSync as writeFileSync15, readFileSync as readFileSync19, mkdirSync as mkdirSync17 } from "fs";
|
|
35983
36271
|
import { dirname as dirname14 } from "path";
|
|
35984
36272
|
function listTemplateLibrary(db) {
|
|
35985
36273
|
const d = db || getDatabase();
|
|
@@ -36022,8 +36310,8 @@ function exportTemplateLibraryCatalog(path, db) {
|
|
|
36022
36310
|
templates: listTemplateLibrary(db)
|
|
36023
36311
|
};
|
|
36024
36312
|
if (path) {
|
|
36025
|
-
|
|
36026
|
-
|
|
36313
|
+
mkdirSync17(dirname14(path), { recursive: true });
|
|
36314
|
+
writeFileSync15(path, JSON.stringify(catalog, null, 2), "utf8");
|
|
36027
36315
|
}
|
|
36028
36316
|
return catalog;
|
|
36029
36317
|
}
|
|
@@ -36040,7 +36328,7 @@ function exportInstalledTemplate(name, db) {
|
|
|
36040
36328
|
}
|
|
36041
36329
|
function importTemplateFromFile(path, db) {
|
|
36042
36330
|
const d = db || getDatabase();
|
|
36043
|
-
const raw = JSON.parse(
|
|
36331
|
+
const raw = JSON.parse(readFileSync19(path, "utf8"));
|
|
36044
36332
|
const payload = raw.template ?? raw;
|
|
36045
36333
|
const created = importTemplate(payload, d);
|
|
36046
36334
|
return { id: created.id, name: created.name };
|
|
@@ -36218,9 +36506,9 @@ todos machines topology # full diagnostic report
|
|
|
36218
36506
|
}
|
|
36219
36507
|
// src/lib/environment-snapshots.ts
|
|
36220
36508
|
import { createHash as createHash12 } from "crypto";
|
|
36221
|
-
import { existsSync as
|
|
36509
|
+
import { existsSync as existsSync21, readFileSync as readFileSync20, statSync as statSync7 } from "fs";
|
|
36222
36510
|
import { hostname as hostname3, platform, arch } from "os";
|
|
36223
|
-
import { dirname as dirname15, join as
|
|
36511
|
+
import { dirname as dirname15, join as join18, resolve as resolve16 } from "path";
|
|
36224
36512
|
import { tmpdir as tmpdir3 } from "os";
|
|
36225
36513
|
init_database();
|
|
36226
36514
|
init_redaction();
|
|
@@ -36245,20 +36533,20 @@ function sha2565(value) {
|
|
|
36245
36533
|
return createHash12("sha256").update(value).digest("hex");
|
|
36246
36534
|
}
|
|
36247
36535
|
function fileRecord(root, relativePath) {
|
|
36248
|
-
const path =
|
|
36249
|
-
if (!
|
|
36536
|
+
const path = join18(root, relativePath);
|
|
36537
|
+
if (!existsSync21(path))
|
|
36250
36538
|
return null;
|
|
36251
36539
|
const stat = statSync7(path);
|
|
36252
36540
|
if (!stat.isFile())
|
|
36253
36541
|
return null;
|
|
36254
|
-
const content =
|
|
36542
|
+
const content = readFileSync20(path);
|
|
36255
36543
|
return { path: relativePath, sha256: sha2565(content), size_bytes: content.length };
|
|
36256
36544
|
}
|
|
36257
36545
|
function manifestRecord(root, relativePath) {
|
|
36258
36546
|
const base = fileRecord(root, relativePath);
|
|
36259
36547
|
if (!base)
|
|
36260
36548
|
return null;
|
|
36261
|
-
const parsed = readJsonFile(
|
|
36549
|
+
const parsed = readJsonFile(join18(root, relativePath));
|
|
36262
36550
|
if (!parsed)
|
|
36263
36551
|
return { ...base, redacted: {} };
|
|
36264
36552
|
const redacted = redactValue({
|
|
@@ -36353,15 +36641,15 @@ function commandEnv(env, includeValues) {
|
|
|
36353
36641
|
function defaultSnapshotDir() {
|
|
36354
36642
|
const dbPath = getDatabasePath();
|
|
36355
36643
|
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
36356
|
-
return
|
|
36357
|
-
return
|
|
36644
|
+
return join18(tmpdir3(), "hasna-todos", "environment-snapshots");
|
|
36645
|
+
return join18(dirname15(resolve16(dbPath)), "environment-snapshots");
|
|
36358
36646
|
}
|
|
36359
36647
|
function snapshotWithId(snapshot) {
|
|
36360
36648
|
const digest = sha2565(JSON.stringify(snapshot)).slice(0, 24);
|
|
36361
36649
|
return { id: `env_${digest}`, ...snapshot };
|
|
36362
36650
|
}
|
|
36363
36651
|
function captureEnvironmentSnapshot(input = {}) {
|
|
36364
|
-
const root =
|
|
36652
|
+
const root = resolve16(input.root || process.cwd());
|
|
36365
36653
|
const env = input.env || process.env;
|
|
36366
36654
|
const warnings = [];
|
|
36367
36655
|
const manifests = MANIFEST_FILES.map((file) => manifestRecord(root, file)).filter((file) => Boolean(file));
|
|
@@ -36401,13 +36689,13 @@ function captureEnvironmentSnapshot(input = {}) {
|
|
|
36401
36689
|
});
|
|
36402
36690
|
}
|
|
36403
36691
|
function writeEnvironmentSnapshot(snapshot, outputPath) {
|
|
36404
|
-
const path = outputPath ?
|
|
36692
|
+
const path = outputPath ? resolve16(outputPath) : join18(defaultSnapshotDir(), `${snapshot.id}.json`);
|
|
36405
36693
|
ensureDir2(dirname15(path));
|
|
36406
36694
|
writeJsonFile(path, snapshot);
|
|
36407
36695
|
return path;
|
|
36408
36696
|
}
|
|
36409
36697
|
function readEnvironmentSnapshot(path) {
|
|
36410
|
-
const snapshot = readJsonFile(
|
|
36698
|
+
const snapshot = readJsonFile(resolve16(path));
|
|
36411
36699
|
if (!snapshot || snapshot.schema_version !== 1 || typeof snapshot.id !== "string") {
|
|
36412
36700
|
throw new Error(`Invalid environment snapshot: ${path}`);
|
|
36413
36701
|
}
|
|
@@ -36493,8 +36781,8 @@ function compareEnvironmentSnapshotFiles(leftPath, rightPath) {
|
|
|
36493
36781
|
// src/lib/decision-records.ts
|
|
36494
36782
|
init_database();
|
|
36495
36783
|
import { createHash as createHash13 } from "crypto";
|
|
36496
|
-
import { mkdirSync as
|
|
36497
|
-
import { dirname as dirname16, join as
|
|
36784
|
+
import { mkdirSync as mkdirSync18, writeFileSync as writeFileSync16 } from "fs";
|
|
36785
|
+
import { dirname as dirname16, join as join19 } from "path";
|
|
36498
36786
|
var DECISION_RECORD_SCHEMA = "todos.decision_record.v1";
|
|
36499
36787
|
var KNOWLEDGE_SNAPSHOT_SCHEMA = "todos.knowledge_snapshot.v1";
|
|
36500
36788
|
var DECISION_STATUSES = ["proposed", "accepted", "deprecated", "superseded", "rejected"];
|
|
@@ -36724,9 +37012,9 @@ function exportDecisionRecord(id, outputPath, format = "markdown", db) {
|
|
|
36724
37012
|
if (!record)
|
|
36725
37013
|
throw new Error(`Decision record not found: ${id}`);
|
|
36726
37014
|
const content = format === "markdown" ? formatDecisionRecordMarkdown(record) : JSON.stringify(record, null, 2);
|
|
36727
|
-
const path = outputPath ??
|
|
36728
|
-
|
|
36729
|
-
|
|
37015
|
+
const path = outputPath ?? join19(process.cwd(), ".todos", "decisions", `${record.short_ref}.${format === "markdown" ? "md" : "json"}`);
|
|
37016
|
+
mkdirSync18(dirname16(path), { recursive: true });
|
|
37017
|
+
writeFileSync16(path, content, "utf8");
|
|
36730
37018
|
return { path, content };
|
|
36731
37019
|
}
|
|
36732
37020
|
function buildKnowledgeSnapshotPayload(input, db) {
|
|
@@ -36872,9 +37160,9 @@ function exportKnowledgeSnapshot(id, outputPath, format = "markdown", db) {
|
|
|
36872
37160
|
throw new Error(`Knowledge snapshot not found: ${id}`);
|
|
36873
37161
|
const content = format === "markdown" ? formatKnowledgeSnapshotMarkdown(record) : JSON.stringify(record, null, 2);
|
|
36874
37162
|
const slug = record.title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 40);
|
|
36875
|
-
const path = outputPath ??
|
|
36876
|
-
|
|
36877
|
-
|
|
37163
|
+
const path = outputPath ?? join19(process.cwd(), ".todos", "knowledge", `${slug || record.id.slice(0, 8)}.${format === "markdown" ? "md" : "json"}`);
|
|
37164
|
+
mkdirSync18(dirname16(path), { recursive: true });
|
|
37165
|
+
writeFileSync16(path, content, "utf8");
|
|
36878
37166
|
return { path, content };
|
|
36879
37167
|
}
|
|
36880
37168
|
function getDecisionRecordsDocs() {
|
|
@@ -36900,7 +37188,7 @@ Schema versions:
|
|
|
36900
37188
|
}
|
|
36901
37189
|
// src/lib/report-exports.ts
|
|
36902
37190
|
init_database();
|
|
36903
|
-
import { writeFileSync as
|
|
37191
|
+
import { writeFileSync as writeFileSync17, mkdirSync as mkdirSync19 } from "fs";
|
|
36904
37192
|
import { dirname as dirname17 } from "path";
|
|
36905
37193
|
init_secret_redaction();
|
|
36906
37194
|
var REPORT_EXPORT_SCHEMA = "todos.report_export.v1";
|
|
@@ -37133,8 +37421,8 @@ function formatReportExport(data, format) {
|
|
|
37133
37421
|
return format === "html" ? formatReportHtml(data) : formatReportMarkdown(data);
|
|
37134
37422
|
}
|
|
37135
37423
|
function writeReportExport(data, format, path) {
|
|
37136
|
-
|
|
37137
|
-
|
|
37424
|
+
mkdirSync19(dirname17(path), { recursive: true });
|
|
37425
|
+
writeFileSync17(path, formatReportExport(data, format), "utf8");
|
|
37138
37426
|
}
|
|
37139
37427
|
function exportReport(input, db) {
|
|
37140
37428
|
const data = buildReportExportData(input, db);
|
|
@@ -37167,8 +37455,8 @@ todos report export --kind retrospective --days 14 --format markdown --out retro
|
|
|
37167
37455
|
`;
|
|
37168
37456
|
}
|
|
37169
37457
|
// src/lib/command-aliases.ts
|
|
37170
|
-
import { existsSync as
|
|
37171
|
-
import { join as
|
|
37458
|
+
import { existsSync as existsSync22, readFileSync as readFileSync21, writeFileSync as writeFileSync18, mkdirSync as mkdirSync20 } from "fs";
|
|
37459
|
+
import { join as join20 } from "path";
|
|
37172
37460
|
var COMMAND_ALIASES_SCHEMA = "todos.command_aliases.v1";
|
|
37173
37461
|
var RESERVED = new Set([...listTopLevelCommands(), "help", "version", "alias", "shortcuts"]);
|
|
37174
37462
|
var BUILTIN_SHORTCUTS = [
|
|
@@ -37187,7 +37475,7 @@ var BUILTIN_SHORTCUTS = [
|
|
|
37187
37475
|
{ pattern: /^reports?$/, argv: ["report", "docs"], explain: "Report export documentation" }
|
|
37188
37476
|
];
|
|
37189
37477
|
function aliasesPath(cwd = process.cwd()) {
|
|
37190
|
-
return
|
|
37478
|
+
return join20(cwd, ".todos", "aliases.json");
|
|
37191
37479
|
}
|
|
37192
37480
|
function emptyStore() {
|
|
37193
37481
|
return { schema_version: COMMAND_ALIASES_SCHEMA, aliases: {}, updated_at: new Date(0).toISOString() };
|
|
@@ -37204,9 +37492,9 @@ function validateAliasName(name) {
|
|
|
37204
37492
|
}
|
|
37205
37493
|
function loadAliasStore(cwd) {
|
|
37206
37494
|
const path = aliasesPath(cwd);
|
|
37207
|
-
if (!
|
|
37495
|
+
if (!existsSync22(path))
|
|
37208
37496
|
return emptyStore();
|
|
37209
|
-
const parsed = JSON.parse(
|
|
37497
|
+
const parsed = JSON.parse(readFileSync21(path, "utf8"));
|
|
37210
37498
|
if (parsed.schema_version !== COMMAND_ALIASES_SCHEMA) {
|
|
37211
37499
|
throw new Error(`Unsupported alias store schema: ${parsed.schema_version}`);
|
|
37212
37500
|
}
|
|
@@ -37214,9 +37502,9 @@ function loadAliasStore(cwd) {
|
|
|
37214
37502
|
}
|
|
37215
37503
|
function saveAliasStore(store, cwd) {
|
|
37216
37504
|
const path = aliasesPath(cwd);
|
|
37217
|
-
|
|
37505
|
+
mkdirSync20(join20(path, ".."), { recursive: true });
|
|
37218
37506
|
store.updated_at = new Date().toISOString();
|
|
37219
|
-
|
|
37507
|
+
writeFileSync18(path, JSON.stringify(store, null, 2), "utf8");
|
|
37220
37508
|
}
|
|
37221
37509
|
function parseArgv(command) {
|
|
37222
37510
|
const argv = [];
|
|
@@ -37862,18 +38150,18 @@ function createBranchWorkPlan(input, db) {
|
|
|
37862
38150
|
}
|
|
37863
38151
|
// src/lib/user-scaffolds.ts
|
|
37864
38152
|
init_database();
|
|
37865
|
-
import { existsSync as
|
|
37866
|
-
import { join as
|
|
38153
|
+
import { existsSync as existsSync23, readFileSync as readFileSync22, writeFileSync as writeFileSync19, mkdirSync as mkdirSync21 } from "fs";
|
|
38154
|
+
import { join as join21 } from "path";
|
|
37867
38155
|
var USER_SCAFFOLD_SCHEMA = "todos.user_scaffold.v1";
|
|
37868
38156
|
var SCAFFOLD_KINDS = ["task", "project", "plan", "checklist", "contract", "verification_policy"];
|
|
37869
38157
|
function storeDir(cwd = process.cwd()) {
|
|
37870
|
-
return
|
|
38158
|
+
return join21(cwd, ".todos", "scaffolds");
|
|
37871
38159
|
}
|
|
37872
38160
|
function storePath(cwd) {
|
|
37873
|
-
return
|
|
38161
|
+
return join21(storeDir(cwd), "store.json");
|
|
37874
38162
|
}
|
|
37875
38163
|
function versionsDir(cwd) {
|
|
37876
|
-
return
|
|
38164
|
+
return join21(storeDir(cwd), "versions");
|
|
37877
38165
|
}
|
|
37878
38166
|
function slugify5(name) {
|
|
37879
38167
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
@@ -37883,23 +38171,23 @@ function emptyStore2() {
|
|
|
37883
38171
|
}
|
|
37884
38172
|
function loadUserScaffoldStore(cwd) {
|
|
37885
38173
|
const path = storePath(cwd);
|
|
37886
|
-
if (!
|
|
38174
|
+
if (!existsSync23(path))
|
|
37887
38175
|
return emptyStore2();
|
|
37888
|
-
const parsed = JSON.parse(
|
|
38176
|
+
const parsed = JSON.parse(readFileSync22(path, "utf8"));
|
|
37889
38177
|
if (parsed.schema_version !== USER_SCAFFOLD_SCHEMA) {
|
|
37890
38178
|
throw new Error(`Unsupported scaffold store schema: ${parsed.schema_version}`);
|
|
37891
38179
|
}
|
|
37892
38180
|
return parsed;
|
|
37893
38181
|
}
|
|
37894
38182
|
function saveUserScaffoldStore(store, cwd) {
|
|
37895
|
-
|
|
38183
|
+
mkdirSync21(storeDir(cwd), { recursive: true });
|
|
37896
38184
|
store.updated_at = now();
|
|
37897
|
-
|
|
38185
|
+
writeFileSync19(storePath(cwd), JSON.stringify(store, null, 2), "utf8");
|
|
37898
38186
|
}
|
|
37899
38187
|
function snapshotVersion(scaffold, cwd) {
|
|
37900
|
-
|
|
37901
|
-
const path =
|
|
37902
|
-
|
|
38188
|
+
mkdirSync21(versionsDir(cwd), { recursive: true });
|
|
38189
|
+
const path = join21(versionsDir(cwd), `${scaffold.id}-v${scaffold.version}.json`);
|
|
38190
|
+
writeFileSync19(path, JSON.stringify(scaffold, null, 2), "utf8");
|
|
37903
38191
|
}
|
|
37904
38192
|
function listUserScaffolds(kind, cwd) {
|
|
37905
38193
|
const store = loadUserScaffoldStore(cwd);
|
|
@@ -38145,7 +38433,7 @@ function listLinkedTemplates(db, cwd) {
|
|
|
38145
38433
|
// src/lib/agent-workflow-demo.ts
|
|
38146
38434
|
init_database();
|
|
38147
38435
|
import { mkdtempSync } from "fs";
|
|
38148
|
-
import { join as
|
|
38436
|
+
import { join as join22 } from "path";
|
|
38149
38437
|
import { tmpdir as tmpdir4 } from "os";
|
|
38150
38438
|
var AGENT_WORKFLOW_DEMO_SCHEMA = "todos.agent_workflow_demo.v1";
|
|
38151
38439
|
var DEMO_DEFAULT_AGENT = "demoagent";
|
|
@@ -38161,7 +38449,7 @@ function setupEphemeralDemoDb(options = {}) {
|
|
|
38161
38449
|
if (options.db_path) {
|
|
38162
38450
|
db_path = options.db_path;
|
|
38163
38451
|
} else if (options.persist) {
|
|
38164
|
-
db_path =
|
|
38452
|
+
db_path = join22(mkdtempSync(join22(tmpdir4(), "todos-demo-")), "todos.db");
|
|
38165
38453
|
} else {
|
|
38166
38454
|
db_path = ":memory:";
|
|
38167
38455
|
}
|
|
@@ -40486,18 +40774,18 @@ function runSearchView(idOrName, db) {
|
|
|
40486
40774
|
return { ...runSavedSearch(view.filters, view.scope, d), view };
|
|
40487
40775
|
}
|
|
40488
40776
|
// src/lib/claude-tasks.ts
|
|
40489
|
-
import { existsSync as
|
|
40490
|
-
import { join as
|
|
40777
|
+
import { existsSync as existsSync24, readFileSync as readFileSync23, readdirSync as readdirSync4, writeFileSync as writeFileSync20 } from "fs";
|
|
40778
|
+
import { join as join23 } from "path";
|
|
40491
40779
|
init_config();
|
|
40492
40780
|
init_sync_utils();
|
|
40493
40781
|
function getTaskListDir(taskListId) {
|
|
40494
|
-
return
|
|
40782
|
+
return join23(HOME, ".claude", "tasks", taskListId);
|
|
40495
40783
|
}
|
|
40496
40784
|
function readClaudeTask(dir, filename) {
|
|
40497
|
-
return readJsonFile(
|
|
40785
|
+
return readJsonFile(join23(dir, filename));
|
|
40498
40786
|
}
|
|
40499
40787
|
function writeClaudeTask(dir, task2) {
|
|
40500
|
-
writeJsonFile(
|
|
40788
|
+
writeJsonFile(join23(dir, `${task2.id}.json`), task2);
|
|
40501
40789
|
}
|
|
40502
40790
|
function toClaudeStatus(status) {
|
|
40503
40791
|
if (status === "pending" || status === "in_progress" || status === "completed") {
|
|
@@ -40509,14 +40797,14 @@ function toSqliteStatus(status) {
|
|
|
40509
40797
|
return status;
|
|
40510
40798
|
}
|
|
40511
40799
|
function readPrefixCounter(dir) {
|
|
40512
|
-
const path =
|
|
40513
|
-
if (!
|
|
40800
|
+
const path = join23(dir, ".prefix-counter");
|
|
40801
|
+
if (!existsSync24(path))
|
|
40514
40802
|
return 0;
|
|
40515
|
-
const val = parseInt(
|
|
40803
|
+
const val = parseInt(readFileSync23(path, "utf-8").trim(), 10);
|
|
40516
40804
|
return isNaN(val) ? 0 : val;
|
|
40517
40805
|
}
|
|
40518
40806
|
function writePrefixCounter(dir, value) {
|
|
40519
|
-
|
|
40807
|
+
writeFileSync20(join23(dir, ".prefix-counter"), String(value));
|
|
40520
40808
|
}
|
|
40521
40809
|
function formatPrefixedSubject(title, prefix, counter) {
|
|
40522
40810
|
const padded = String(counter).padStart(5, "0");
|
|
@@ -40543,7 +40831,7 @@ function taskToClaudeTask(task2, claudeTaskId, existingMeta) {
|
|
|
40543
40831
|
}
|
|
40544
40832
|
function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
40545
40833
|
const dir = getTaskListDir(taskListId);
|
|
40546
|
-
if (!
|
|
40834
|
+
if (!existsSync24(dir))
|
|
40547
40835
|
ensureDir2(dir);
|
|
40548
40836
|
const filter = {};
|
|
40549
40837
|
if (projectId)
|
|
@@ -40552,7 +40840,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
40552
40840
|
const existingByTodosId = new Map;
|
|
40553
40841
|
const files = listJsonFiles(dir);
|
|
40554
40842
|
for (const f of files) {
|
|
40555
|
-
const path =
|
|
40843
|
+
const path = join23(dir, f);
|
|
40556
40844
|
const ct = readClaudeTask(dir, f);
|
|
40557
40845
|
if (ct?.metadata?.["todos_id"]) {
|
|
40558
40846
|
existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -40639,7 +40927,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
40639
40927
|
}
|
|
40640
40928
|
function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
|
|
40641
40929
|
const dir = getTaskListDir(taskListId);
|
|
40642
|
-
if (!
|
|
40930
|
+
if (!existsSync24(dir)) {
|
|
40643
40931
|
return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
|
|
40644
40932
|
}
|
|
40645
40933
|
const files = readdirSync4(dir).filter((f) => f.endsWith(".json"));
|
|
@@ -40659,7 +40947,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
40659
40947
|
}
|
|
40660
40948
|
for (const f of files) {
|
|
40661
40949
|
try {
|
|
40662
|
-
const filePath =
|
|
40950
|
+
const filePath = join23(dir, f);
|
|
40663
40951
|
const ct = readClaudeTask(dir, f);
|
|
40664
40952
|
if (!ct)
|
|
40665
40953
|
continue;
|
|
@@ -40727,22 +41015,22 @@ function syncClaudeTaskList(taskListId, projectId, options = {}) {
|
|
|
40727
41015
|
}
|
|
40728
41016
|
|
|
40729
41017
|
// src/lib/agent-tasks.ts
|
|
40730
|
-
import { existsSync as
|
|
40731
|
-
import { join as
|
|
41018
|
+
import { existsSync as existsSync25 } from "fs";
|
|
41019
|
+
import { join as join24 } from "path";
|
|
40732
41020
|
init_sync_utils();
|
|
40733
41021
|
init_config();
|
|
40734
41022
|
function agentBaseDir(agent) {
|
|
40735
41023
|
const key = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
|
|
40736
|
-
return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] ||
|
|
41024
|
+
return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join24(getTodosGlobalDir(), "agents");
|
|
40737
41025
|
}
|
|
40738
41026
|
function getTaskListDir2(agent, taskListId) {
|
|
40739
|
-
return
|
|
41027
|
+
return join24(agentBaseDir(agent), agent, taskListId);
|
|
40740
41028
|
}
|
|
40741
41029
|
function readAgentTask(dir, filename) {
|
|
40742
|
-
return readJsonFile(
|
|
41030
|
+
return readJsonFile(join24(dir, filename));
|
|
40743
41031
|
}
|
|
40744
41032
|
function writeAgentTask(dir, task2) {
|
|
40745
|
-
writeJsonFile(
|
|
41033
|
+
writeJsonFile(join24(dir, `${task2.id}.json`), task2);
|
|
40746
41034
|
}
|
|
40747
41035
|
function taskToAgentTask(task2, externalId, existingMeta) {
|
|
40748
41036
|
return {
|
|
@@ -40767,7 +41055,7 @@ function metadataKey(agent) {
|
|
|
40767
41055
|
}
|
|
40768
41056
|
function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
40769
41057
|
const dir = getTaskListDir2(agent, taskListId);
|
|
40770
|
-
if (!
|
|
41058
|
+
if (!existsSync25(dir))
|
|
40771
41059
|
ensureDir2(dir);
|
|
40772
41060
|
const filter = {};
|
|
40773
41061
|
if (projectId)
|
|
@@ -40776,7 +41064,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
40776
41064
|
const existingByTodosId = new Map;
|
|
40777
41065
|
const files = listJsonFiles(dir);
|
|
40778
41066
|
for (const f of files) {
|
|
40779
|
-
const path =
|
|
41067
|
+
const path = join24(dir, f);
|
|
40780
41068
|
const at = readAgentTask(dir, f);
|
|
40781
41069
|
if (at?.metadata?.["todos_id"]) {
|
|
40782
41070
|
existingByTodosId.set(at.metadata["todos_id"], { task: at, mtimeMs: getFileMtimeMs(path) });
|
|
@@ -40850,7 +41138,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
40850
41138
|
}
|
|
40851
41139
|
function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
40852
41140
|
const dir = getTaskListDir2(agent, taskListId);
|
|
40853
|
-
if (!
|
|
41141
|
+
if (!existsSync25(dir)) {
|
|
40854
41142
|
return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
|
|
40855
41143
|
}
|
|
40856
41144
|
const files = listJsonFiles(dir);
|
|
@@ -40869,7 +41157,7 @@ function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
|
|
|
40869
41157
|
}
|
|
40870
41158
|
for (const f of files) {
|
|
40871
41159
|
try {
|
|
40872
|
-
const filePath =
|
|
41160
|
+
const filePath = join24(dir, f);
|
|
40873
41161
|
const at = readAgentTask(dir, f);
|
|
40874
41162
|
if (!at)
|
|
40875
41163
|
continue;
|
|
@@ -41007,9 +41295,9 @@ function syncWithAgents(agents, taskListIdByAgent, projectId, direction = "both"
|
|
|
41007
41295
|
return { pushed, pulled, errors };
|
|
41008
41296
|
}
|
|
41009
41297
|
// src/lib/extract.ts
|
|
41010
|
-
import { existsSync as
|
|
41298
|
+
import { existsSync as existsSync26, readFileSync as readFileSync24, statSync as statSync8 } from "fs";
|
|
41011
41299
|
import { createHash as createHash14 } from "crypto";
|
|
41012
|
-
import { relative as relative6, resolve as
|
|
41300
|
+
import { relative as relative6, resolve as resolve17, join as join25 } from "path";
|
|
41013
41301
|
var EXTRACT_TAGS = ["TODO", "FIXME", "HACK", "XXX", "BUG", "NOTE"];
|
|
41014
41302
|
var DEFAULT_EXTENSIONS = new Set([
|
|
41015
41303
|
".ts",
|
|
@@ -41079,12 +41367,12 @@ function normalizePathForMatch(value) {
|
|
|
41079
41367
|
return value.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
41080
41368
|
}
|
|
41081
41369
|
function readGitignorePatterns(basePath) {
|
|
41082
|
-
const root = statSync8(basePath).isFile() ?
|
|
41083
|
-
const gitignorePath =
|
|
41084
|
-
if (!
|
|
41370
|
+
const root = statSync8(basePath).isFile() ? resolve17(basePath, "..") : basePath;
|
|
41371
|
+
const gitignorePath = join25(root, ".gitignore");
|
|
41372
|
+
if (!existsSync26(gitignorePath))
|
|
41085
41373
|
return [];
|
|
41086
41374
|
try {
|
|
41087
|
-
return
|
|
41375
|
+
return readFileSync24(gitignorePath, "utf-8").split(`
|
|
41088
41376
|
`).map((line) => line.trim()).filter((line) => line && !line.startsWith("#") && !line.startsWith("!"));
|
|
41089
41377
|
} catch {
|
|
41090
41378
|
return [];
|
|
@@ -41215,7 +41503,7 @@ function collectFiles(basePath, extensions, excludes, respectGitignore) {
|
|
|
41215
41503
|
return files.sort();
|
|
41216
41504
|
}
|
|
41217
41505
|
function buildCodebaseIndex(options) {
|
|
41218
|
-
const basePath =
|
|
41506
|
+
const basePath = resolve17(options.path);
|
|
41219
41507
|
const tags = options.patterns || [...EXTRACT_TAGS];
|
|
41220
41508
|
const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
|
|
41221
41509
|
const excludes = options.exclude || [];
|
|
@@ -41223,10 +41511,10 @@ function buildCodebaseIndex(options) {
|
|
|
41223
41511
|
const files = collectFiles(basePath, extensions, excludes, respectGitignore);
|
|
41224
41512
|
const indexed = [];
|
|
41225
41513
|
for (const file of files) {
|
|
41226
|
-
const fullPath = statSync8(basePath).isFile() ? basePath :
|
|
41514
|
+
const fullPath = statSync8(basePath).isFile() ? basePath : join25(basePath, file);
|
|
41227
41515
|
try {
|
|
41228
|
-
const source9 =
|
|
41229
|
-
const relPath = statSync8(basePath).isFile() ? relative6(
|
|
41516
|
+
const source9 = readFileSync24(fullPath, "utf-8");
|
|
41517
|
+
const relPath = statSync8(basePath).isFile() ? relative6(resolve17(basePath, ".."), fullPath) : file;
|
|
41230
41518
|
indexed.push({
|
|
41231
41519
|
file: relPath,
|
|
41232
41520
|
checksum: stableHash(source9).slice(0, 24),
|
|
@@ -41246,7 +41534,7 @@ function buildCodebaseIndex(options) {
|
|
|
41246
41534
|
};
|
|
41247
41535
|
}
|
|
41248
41536
|
function extractTodos(options, db) {
|
|
41249
|
-
const basePath =
|
|
41537
|
+
const basePath = resolve17(options.path);
|
|
41250
41538
|
const tags = options.patterns || [...EXTRACT_TAGS];
|
|
41251
41539
|
const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
|
|
41252
41540
|
const excludes = options.exclude || [];
|
|
@@ -41254,10 +41542,10 @@ function extractTodos(options, db) {
|
|
|
41254
41542
|
const files = collectFiles(basePath, extensions, excludes, respectGitignore);
|
|
41255
41543
|
const allComments = [];
|
|
41256
41544
|
for (const file of files) {
|
|
41257
|
-
const fullPath = statSync8(basePath).isFile() ? basePath :
|
|
41545
|
+
const fullPath = statSync8(basePath).isFile() ? basePath : join25(basePath, file);
|
|
41258
41546
|
try {
|
|
41259
|
-
const source9 =
|
|
41260
|
-
const relPath = statSync8(basePath).isFile() ? relative6(
|
|
41547
|
+
const source9 = readFileSync24(fullPath, "utf-8");
|
|
41548
|
+
const relPath = statSync8(basePath).isFile() ? relative6(resolve17(basePath, ".."), fullPath) : file;
|
|
41261
41549
|
const comments = extractFromSource(source9, relPath, tags);
|
|
41262
41550
|
allComments.push(...comments);
|
|
41263
41551
|
} catch {}
|
|
@@ -41351,7 +41639,7 @@ async function watchSourceTodos(options, onRun) {
|
|
|
41351
41639
|
const interval = Math.max(100, options.interval_ms || 2000);
|
|
41352
41640
|
const once = options.once !== false && (!options.max_runs || options.max_runs <= 1);
|
|
41353
41641
|
const maxRuns = options.max_runs ?? (once ? 1 : Number.POSITIVE_INFINITY);
|
|
41354
|
-
const root =
|
|
41642
|
+
const root = resolve17(options.path);
|
|
41355
41643
|
const runs = [];
|
|
41356
41644
|
let previous = new Map;
|
|
41357
41645
|
for (let runNumber = 1;runNumber <= maxRuns; runNumber++) {
|
|
@@ -41962,7 +42250,7 @@ function renderWorkflowStatesMarkdown(states = listWorkflowStates()) {
|
|
|
41962
42250
|
// src/lib/agent-replay-simulator.ts
|
|
41963
42251
|
init_redaction();
|
|
41964
42252
|
import { createHash as createHash15 } from "crypto";
|
|
41965
|
-
import { readFileSync as
|
|
42253
|
+
import { readFileSync as readFileSync25 } from "fs";
|
|
41966
42254
|
function isObject(value) {
|
|
41967
42255
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
41968
42256
|
}
|
|
@@ -42195,7 +42483,7 @@ function simulateAgentReplay(input, options = {}) {
|
|
|
42195
42483
|
};
|
|
42196
42484
|
}
|
|
42197
42485
|
function simulateAgentReplayFile(path, options = {}) {
|
|
42198
|
-
const parsed = JSON.parse(
|
|
42486
|
+
const parsed = JSON.parse(readFileSync25(path, "utf8"));
|
|
42199
42487
|
return simulateAgentReplay(parsed, options);
|
|
42200
42488
|
}
|
|
42201
42489
|
function renderAgentReplaySimulationMarkdown(simulation) {
|
|
@@ -42223,8 +42511,8 @@ function renderAgentReplaySimulationMarkdown(simulation) {
|
|
|
42223
42511
|
// src/lib/local-extensions.ts
|
|
42224
42512
|
init_config();
|
|
42225
42513
|
import { createHash as createHash16, createVerify } from "crypto";
|
|
42226
|
-
import { existsSync as
|
|
42227
|
-
import { basename as basename5, join as
|
|
42514
|
+
import { existsSync as existsSync27, readdirSync as readdirSync5, readFileSync as readFileSync26, statSync as statSync9 } from "fs";
|
|
42515
|
+
import { basename as basename5, join as join26, resolve as resolve18 } from "path";
|
|
42228
42516
|
init_redaction();
|
|
42229
42517
|
function isObject2(value) {
|
|
42230
42518
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
@@ -42306,7 +42594,7 @@ function normalizeManifest(input) {
|
|
|
42306
42594
|
};
|
|
42307
42595
|
}
|
|
42308
42596
|
function parseJson(path) {
|
|
42309
|
-
return JSON.parse(
|
|
42597
|
+
return JSON.parse(readFileSync26(path, "utf8"));
|
|
42310
42598
|
}
|
|
42311
42599
|
function sha2566(bytes) {
|
|
42312
42600
|
return `sha256:${createHash16("sha256").update(bytes).digest("hex")}`;
|
|
@@ -42504,14 +42792,14 @@ function verifyExtensionSignature(input) {
|
|
|
42504
42792
|
return verifier.verify(input.public_key, decodeSignature(input.signature));
|
|
42505
42793
|
}
|
|
42506
42794
|
function inspectExtensionSource(source9) {
|
|
42507
|
-
const resolved =
|
|
42508
|
-
if (!
|
|
42795
|
+
const resolved = resolve18(source9);
|
|
42796
|
+
if (!existsSync27(resolved))
|
|
42509
42797
|
throw new Error(`extension source not found: ${source9}`);
|
|
42510
42798
|
const stat = statSync9(resolved);
|
|
42511
|
-
const manifestPath = stat.isDirectory() ? [
|
|
42799
|
+
const manifestPath = stat.isDirectory() ? [join26(resolved, "todos.extension.json"), join26(resolved, "extension.json")].find(existsSync27) : resolved;
|
|
42512
42800
|
if (!manifestPath)
|
|
42513
42801
|
throw new Error(`extension directory ${source9} is missing todos.extension.json`);
|
|
42514
|
-
const raw =
|
|
42802
|
+
const raw = readFileSync26(manifestPath);
|
|
42515
42803
|
const parsed = parseJson(manifestPath);
|
|
42516
42804
|
const bundle = isObject2(parsed) && isObject2(parsed["manifest"]);
|
|
42517
42805
|
const manifest = normalizeManifest(bundle ? parsed["manifest"] : parsed);
|
|
@@ -42602,26 +42890,26 @@ function testExtensionCompatibility(sourceOrManifest) {
|
|
|
42602
42890
|
function projectExtensionSources(projectPath) {
|
|
42603
42891
|
if (!projectPath)
|
|
42604
42892
|
return [];
|
|
42605
|
-
const root =
|
|
42893
|
+
const root = resolve18(projectPath);
|
|
42606
42894
|
const candidates = [
|
|
42607
|
-
|
|
42608
|
-
|
|
42895
|
+
join26(root, "todos.extension.json"),
|
|
42896
|
+
join26(root, ".todos", "todos.extension.json")
|
|
42609
42897
|
];
|
|
42610
|
-
const extensionDir =
|
|
42611
|
-
if (
|
|
42898
|
+
const extensionDir = join26(root, ".todos", "extensions");
|
|
42899
|
+
if (existsSync27(extensionDir)) {
|
|
42612
42900
|
for (const entry2 of readdirSync5(extensionDir)) {
|
|
42613
42901
|
if (entry2.startsWith("."))
|
|
42614
42902
|
continue;
|
|
42615
|
-
const full =
|
|
42903
|
+
const full = join26(extensionDir, entry2);
|
|
42616
42904
|
if (statSync9(full).isDirectory() || entry2.endsWith(".json"))
|
|
42617
42905
|
candidates.push(full);
|
|
42618
42906
|
}
|
|
42619
42907
|
}
|
|
42620
|
-
return candidates.filter(
|
|
42908
|
+
return candidates.filter(existsSync27);
|
|
42621
42909
|
}
|
|
42622
42910
|
function discoverLocalExtensions(options = {}) {
|
|
42623
42911
|
const config = loadConfig();
|
|
42624
|
-
const projectPath = options.project_path ?
|
|
42912
|
+
const projectPath = options.project_path ? resolve18(options.project_path) : null;
|
|
42625
42913
|
const configuredSources = [
|
|
42626
42914
|
...config.extension_sources || [],
|
|
42627
42915
|
...projectPath ? config.project_overrides?.[projectPath]?.extension_sources || [] : []
|
|
@@ -42629,7 +42917,7 @@ function discoverLocalExtensions(options = {}) {
|
|
|
42629
42917
|
const sources = Array.from(new Set([
|
|
42630
42918
|
...configuredSources,
|
|
42631
42919
|
...projectExtensionSources(projectPath || undefined)
|
|
42632
|
-
])).map((source9) => projectPath && !source9.startsWith("/") ?
|
|
42920
|
+
])).map((source9) => projectPath && !source9.startsWith("/") ? resolve18(projectPath, source9) : resolve18(source9));
|
|
42633
42921
|
const warnings = [];
|
|
42634
42922
|
const discovered = [];
|
|
42635
42923
|
for (const source9 of sources) {
|
|
@@ -43478,7 +43766,7 @@ function resolveMissingTaskFindings(input, db) {
|
|
|
43478
43766
|
init_redaction();
|
|
43479
43767
|
|
|
43480
43768
|
// src/lib/retention-cleanup.ts
|
|
43481
|
-
import { existsSync as
|
|
43769
|
+
import { existsSync as existsSync28, unlinkSync as unlinkSync2 } from "fs";
|
|
43482
43770
|
init_database();
|
|
43483
43771
|
var RETENTION_CLEANUP_CONFIRMATION = "delete-local-retention-data";
|
|
43484
43772
|
var ALL_SCOPES = ["comments", "runs", "verifications", "expired_artifacts"];
|
|
@@ -43690,7 +43978,7 @@ function applyRetentionCleanup(input, db) {
|
|
|
43690
43978
|
for (const artifact of report.candidates.artifact_files) {
|
|
43691
43979
|
try {
|
|
43692
43980
|
const path = artifactStorePath(artifact.relative_path);
|
|
43693
|
-
if (!
|
|
43981
|
+
if (!existsSync28(path)) {
|
|
43694
43982
|
report.warnings.push(`stored artifact already missing: ${artifact.relative_path}`);
|
|
43695
43983
|
continue;
|
|
43696
43984
|
}
|
|
@@ -43928,8 +44216,8 @@ function renderScalePerformanceReportMarkdown(report) {
|
|
|
43928
44216
|
init_database();
|
|
43929
44217
|
init_migrations();
|
|
43930
44218
|
init_schema();
|
|
43931
|
-
import { chmodSync, copyFileSync as copyFileSync2, existsSync as
|
|
43932
|
-
import { basename as basename6, dirname as dirname18, join as
|
|
44219
|
+
import { chmodSync, copyFileSync as copyFileSync2, existsSync as existsSync29, mkdirSync as mkdirSync22, statSync as statSync10 } from "fs";
|
|
44220
|
+
import { basename as basename6, dirname as dirname18, join as join27 } from "path";
|
|
43933
44221
|
var REQUIRED_TABLES2 = [
|
|
43934
44222
|
"_migrations",
|
|
43935
44223
|
"projects",
|
|
@@ -44036,7 +44324,7 @@ function findMissingProjectRoots(db) {
|
|
|
44036
44324
|
continue;
|
|
44037
44325
|
if (!row.path.startsWith("/"))
|
|
44038
44326
|
continue;
|
|
44039
|
-
if (!
|
|
44327
|
+
if (!existsSync29(row.path))
|
|
44040
44328
|
missing++;
|
|
44041
44329
|
}
|
|
44042
44330
|
return missing;
|
|
@@ -44096,16 +44384,16 @@ function databasePermissionsAreUnsafe(dbPath) {
|
|
|
44096
44384
|
function createBackup(dbPath) {
|
|
44097
44385
|
if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
|
|
44098
44386
|
return;
|
|
44099
|
-
if (!
|
|
44387
|
+
if (!existsSync29(dbPath))
|
|
44100
44388
|
return;
|
|
44101
44389
|
const stamp = now().replace(/[:.]/g, "-");
|
|
44102
|
-
const backupDir =
|
|
44390
|
+
const backupDir = join27(dirname18(dbPath), `${basename6(dbPath)}.backup-${stamp}`);
|
|
44103
44391
|
const files = [];
|
|
44104
|
-
|
|
44392
|
+
mkdirSync22(backupDir, { recursive: true });
|
|
44105
44393
|
for (const source9 of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
|
|
44106
|
-
if (!
|
|
44394
|
+
if (!existsSync29(source9))
|
|
44107
44395
|
continue;
|
|
44108
|
-
const target =
|
|
44396
|
+
const target = join27(backupDir, basename6(source9));
|
|
44109
44397
|
copyFileSync2(source9, target);
|
|
44110
44398
|
files.push(target);
|
|
44111
44399
|
}
|
|
@@ -44769,6 +45057,7 @@ export {
|
|
|
44769
45057
|
writeVerificationExport,
|
|
44770
45058
|
writeSdkIntegrationFixtures,
|
|
44771
45059
|
writeReportExport,
|
|
45060
|
+
writePlanArtifact,
|
|
44772
45061
|
writeOnboardingFixtureFiles,
|
|
44773
45062
|
writeLocalBackupFile,
|
|
44774
45063
|
writeEnvironmentSnapshot,
|
|
@@ -44935,6 +45224,8 @@ export {
|
|
|
44935
45224
|
resolveTaskRunId,
|
|
44936
45225
|
resolvePlanRef,
|
|
44937
45226
|
resolvePlanId,
|
|
45227
|
+
resolvePlanArtifactProject,
|
|
45228
|
+
resolvePlanArtifactPaths,
|
|
44938
45229
|
resolvePartialId,
|
|
44939
45230
|
resolveMissingTaskFindings,
|
|
44940
45231
|
resolveMentions,
|
|
@@ -44965,6 +45256,7 @@ export {
|
|
|
44965
45256
|
renderReleaseNotesMarkdown,
|
|
44966
45257
|
renderReleaseCompatibilityMarkdown,
|
|
44967
45258
|
renderPlanningForecastMarkdown,
|
|
45259
|
+
renderPlanArtifactMarkdown,
|
|
44968
45260
|
renderLocalUsageLedgerMarkdown,
|
|
44969
45261
|
renderLocalSnapshotMarkdown,
|
|
44970
45262
|
renderLocalReportMarkdown,
|
|
@@ -45022,6 +45314,7 @@ export {
|
|
|
45022
45314
|
recordFilesTouched,
|
|
45023
45315
|
recordEnvironmentSnapshot,
|
|
45024
45316
|
readTesterIssueReportsPayload,
|
|
45317
|
+
readPlanArtifact,
|
|
45025
45318
|
readLocalBackupFile,
|
|
45026
45319
|
readEnvironmentSnapshot,
|
|
45027
45320
|
readBundleFile,
|
|
@@ -45051,6 +45344,7 @@ export {
|
|
|
45051
45344
|
parseStorageMode,
|
|
45052
45345
|
parseRecurrenceRule,
|
|
45053
45346
|
parseQuietHours,
|
|
45347
|
+
parsePlanArtifactMarkdown,
|
|
45054
45348
|
parseNaturalLanguageTask,
|
|
45055
45349
|
parseIssueExport,
|
|
45056
45350
|
parseGoalCommand,
|
|
@@ -45202,6 +45496,7 @@ export {
|
|
|
45202
45496
|
isAgentConflict,
|
|
45203
45497
|
installTemplateLibrary,
|
|
45204
45498
|
installLocalExtension,
|
|
45499
|
+
inspectPlanArtifact,
|
|
45205
45500
|
inspectGitCommit,
|
|
45206
45501
|
inspectExtensionSource,
|
|
45207
45502
|
initBuiltinTemplates,
|
|
@@ -45675,6 +45970,7 @@ export {
|
|
|
45675
45970
|
buildRunReplayBundle,
|
|
45676
45971
|
buildResourceSnapshot,
|
|
45677
45972
|
buildReportExportData,
|
|
45973
|
+
buildPlanArtifactSnapshot,
|
|
45678
45974
|
buildMcpToolGroups,
|
|
45679
45975
|
buildMachineTopologyReport,
|
|
45680
45976
|
buildKnowledgeSnapshotPayload,
|
|
@@ -45800,6 +46096,7 @@ export {
|
|
|
45800
46096
|
ProjectNotFoundError,
|
|
45801
46097
|
PlanNotFoundError,
|
|
45802
46098
|
PLAN_STATUSES,
|
|
46099
|
+
PLAN_MARKDOWN_SCHEMA,
|
|
45803
46100
|
PLAN_EXECUTION_SCHEMA,
|
|
45804
46101
|
PLAN_EXECUTION_MODES,
|
|
45805
46102
|
PARITY_SCHEMA_VERSION,
|