@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/index.js CHANGED
@@ -12448,6 +12448,7 @@ __export(exports_helpers, {
12448
12448
  resolveTaskId: () => resolveTaskId,
12449
12449
  resolveExplicitProject: () => resolveExplicitProject,
12450
12450
  priorityColors: () => priorityColors,
12451
+ printJson: () => printJson,
12451
12452
  output: () => output,
12452
12453
  normalizeStatusList: () => normalizeStatusList,
12453
12454
  normalizeStatus: () => normalizeStatus,
@@ -12460,6 +12461,7 @@ __export(exports_helpers, {
12460
12461
  });
12461
12462
  import chalk from "chalk";
12462
12463
  import { execSync } from "child_process";
12464
+ import { writeSync } from "fs";
12463
12465
  import { resolve as resolve8 } from "path";
12464
12466
  function handleError(e) {
12465
12467
  console.error(chalk.red(e instanceof Error ? e.message : String(e)));
@@ -12556,9 +12558,35 @@ function normalizeStatusList(statuses) {
12556
12558
  return statuses.map(normalizeStatus);
12557
12559
  return normalizeStatus(statuses);
12558
12560
  }
12561
+ function writeStdoutSync(text) {
12562
+ const buffer = Buffer.from(text);
12563
+ let offset = 0;
12564
+ while (offset < buffer.length) {
12565
+ try {
12566
+ const written = writeSync(1, buffer, offset, buffer.length - offset);
12567
+ if (written <= 0)
12568
+ throw new Error("Unable to write complete stdout payload");
12569
+ offset += written;
12570
+ } catch (error) {
12571
+ const code = typeof error === "object" && error !== null && "code" in error ? error.code : undefined;
12572
+ if (code === "EPIPE") {
12573
+ return;
12574
+ }
12575
+ if (code === "EAGAIN" || code === "EWOULDBLOCK" || code === "EINTR") {
12576
+ Atomics.wait(stdoutRetrySignal, 0, 0, 1);
12577
+ continue;
12578
+ }
12579
+ throw error;
12580
+ }
12581
+ }
12582
+ }
12583
+ function printJson(data) {
12584
+ writeStdoutSync(`${JSON.stringify(data, null, 2)}
12585
+ `);
12586
+ }
12559
12587
  function output(data, jsonMode) {
12560
12588
  if (jsonMode) {
12561
- console.log(JSON.stringify(data, null, 2));
12589
+ printJson(data);
12562
12590
  }
12563
12591
  }
12564
12592
  function formatTaskLine(t) {
@@ -12570,11 +12598,13 @@ function formatTaskLine(t) {
12570
12598
  const plan = t.plan_id ? chalk.magenta(` [plan:${t.plan_id.slice(0, 8)}]`) : "";
12571
12599
  return `${chalk.dim(t.id.slice(0, 8))} ${statusFn(t.status.padEnd(11))} ${priorityFn(t.priority.padEnd(8))} ${t.title}${assigned}${lock}${tags}${plan}`;
12572
12600
  }
12573
- var statusColors, priorityColors;
12601
+ var stdoutRetryBuffer, stdoutRetrySignal, statusColors, priorityColors;
12574
12602
  var init_helpers = __esm(() => {
12575
12603
  init_database();
12576
12604
  init_projects();
12577
12605
  init_package_version();
12606
+ stdoutRetryBuffer = new SharedArrayBuffer(4);
12607
+ stdoutRetrySignal = new Int32Array(stdoutRetryBuffer);
12578
12608
  statusColors = {
12579
12609
  pending: chalk.yellow,
12580
12610
  in_progress: chalk.blue,
@@ -13651,6 +13681,299 @@ var init_task_commands = __esm(() => {
13651
13681
  init_types();
13652
13682
  });
13653
13683
 
13684
+ // src/lib/plan-artifacts.ts
13685
+ import { existsSync as existsSync8, mkdirSync as mkdirSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "fs";
13686
+ import { join as join7, resolve as resolve10 } from "path";
13687
+ function assertSafePathSegment(value, label) {
13688
+ const trimmed = value.trim();
13689
+ if (!trimmed || trimmed === "." || trimmed === ".." || trimmed.includes("/") || trimmed.includes("\\")) {
13690
+ throw new Error(`Invalid ${label} for plan artifact path`);
13691
+ }
13692
+ if (!/^[A-Za-z0-9._-]+$/.test(trimmed)) {
13693
+ throw new Error(`Invalid ${label} for plan artifact path`);
13694
+ }
13695
+ return trimmed;
13696
+ }
13697
+ function frontmatterScalar(value) {
13698
+ return JSON.stringify(value);
13699
+ }
13700
+ function parseFrontmatterScalar(value) {
13701
+ const trimmed = value.trim();
13702
+ if (trimmed === "null")
13703
+ return null;
13704
+ try {
13705
+ const parsed = JSON.parse(trimmed);
13706
+ if (parsed === null || typeof parsed === "string")
13707
+ return parsed;
13708
+ } catch {}
13709
+ return trimmed.replace(/^["']|["']$/g, "") || null;
13710
+ }
13711
+ function markdownEscape(text) {
13712
+ return text.replace(/<!--[\s\S]*?-->/g, "").trim();
13713
+ }
13714
+ function markdownLine(text) {
13715
+ return markdownEscape(text).replace(/\s+/g, " ").trim();
13716
+ }
13717
+ function projectSlugMatches(project, ref) {
13718
+ const normalized = slugify(ref);
13719
+ return Boolean(normalized) && (project.task_list_id === normalized || slugify(project.name) === normalized);
13720
+ }
13721
+ function resolvePlanArtifactProject(input) {
13722
+ const db = input.db || getDatabase();
13723
+ const ref = input.project_id || input.project_ref;
13724
+ if (!ref)
13725
+ throw new Error("Plan artifacts require a project id or project reference");
13726
+ const byPath = getProjectByPath(resolve10(ref), db);
13727
+ if (byPath)
13728
+ return byPath;
13729
+ const resolvedId = resolvePartialId(db, "projects", ref);
13730
+ if (resolvedId) {
13731
+ const project2 = getProject(resolvedId, db);
13732
+ if (project2)
13733
+ return project2;
13734
+ }
13735
+ const project = listProjects(db).find((candidate) => projectSlugMatches(candidate, ref));
13736
+ if (project)
13737
+ return project;
13738
+ throw new Error(`Project not found for plan artifacts: ${ref}`);
13739
+ }
13740
+ function resolvePlanArtifactPaths(input) {
13741
+ const project = resolvePlanArtifactProject(input);
13742
+ const projectId = assertSafePathSegment(project.id, "project id");
13743
+ const projectRoot = resolve10(project.path);
13744
+ const directory = join7(projectRoot, ".hasna", "todos", "plans", projectId);
13745
+ const planId = input.plan_id ? assertSafePathSegment(input.plan_id, "plan id") : null;
13746
+ return {
13747
+ project_id: project.id,
13748
+ project_root: projectRoot,
13749
+ directory,
13750
+ file_path: planId ? join7(directory, `${planId}.md`) : directory
13751
+ };
13752
+ }
13753
+ function buildPlanArtifactSnapshot(plan, tasks = [], artifactUpdatedAt = new Date().toISOString()) {
13754
+ if (!plan.project_id)
13755
+ throw new Error("Plan artifacts require a project-scoped plan");
13756
+ const taskReferences = tasks.map((task) => ({
13757
+ task_id: task.id,
13758
+ title: task.title,
13759
+ status: task.status,
13760
+ priority: task.priority
13761
+ }));
13762
+ const body = renderPlanArtifactBody(plan, taskReferences);
13763
+ return {
13764
+ metadata: {
13765
+ schema: PLAN_MARKDOWN_SCHEMA,
13766
+ plan_id: plan.id,
13767
+ project_id: plan.project_id,
13768
+ task_list_id: plan.task_list_id ?? null,
13769
+ agent_id: plan.agent_id ?? null,
13770
+ stable_id: plan.id,
13771
+ name: plan.name,
13772
+ status: plan.status,
13773
+ created_at: plan.created_at,
13774
+ updated_at: plan.updated_at,
13775
+ artifact_updated_at: artifactUpdatedAt
13776
+ },
13777
+ task_references: taskReferences,
13778
+ body
13779
+ };
13780
+ }
13781
+ function renderPlanArtifactBody(plan, tasks) {
13782
+ const lines = [`# ${markdownLine(plan.name) || plan.id}`, ""];
13783
+ if (plan.description?.trim()) {
13784
+ lines.push(markdownEscape(plan.description), "");
13785
+ }
13786
+ lines.push("## Tasks", "");
13787
+ if (tasks.length === 0) {
13788
+ lines.push("_No tasks are currently attached to this plan._", "");
13789
+ } else {
13790
+ for (const task of tasks) {
13791
+ const check = task.status === "completed" ? "x" : " ";
13792
+ lines.push(`- [${check}] ${markdownLine(task.title) || task.task_id}`);
13793
+ lines.push(` <!-- todos: task_id=${task.task_id} status=${task.status} priority=${task.priority} -->`);
13794
+ }
13795
+ lines.push("");
13796
+ }
13797
+ return lines.join(`
13798
+ `);
13799
+ }
13800
+ function renderPlanArtifactMarkdown(snapshot) {
13801
+ const metadata = snapshot.metadata;
13802
+ const lines = [
13803
+ "---",
13804
+ `schema: ${frontmatterScalar(metadata.schema)}`,
13805
+ `plan_id: ${frontmatterScalar(metadata.plan_id)}`,
13806
+ `project_id: ${frontmatterScalar(metadata.project_id)}`,
13807
+ `task_list_id: ${frontmatterScalar(metadata.task_list_id)}`,
13808
+ `agent_id: ${frontmatterScalar(metadata.agent_id)}`,
13809
+ `stable_id: ${frontmatterScalar(metadata.stable_id)}`,
13810
+ `name: ${frontmatterScalar(metadata.name)}`,
13811
+ `status: ${frontmatterScalar(metadata.status)}`,
13812
+ `created_at: ${frontmatterScalar(metadata.created_at)}`,
13813
+ `updated_at: ${frontmatterScalar(metadata.updated_at)}`,
13814
+ `artifact_updated_at: ${frontmatterScalar(metadata.artifact_updated_at)}`,
13815
+ "---",
13816
+ "",
13817
+ snapshot.body
13818
+ ];
13819
+ return `${lines.join(`
13820
+ `).replace(/\n{3,}/g, `
13821
+
13822
+ `).trimEnd()}
13823
+ `;
13824
+ }
13825
+ function parsePlanArtifactMarkdown(markdown) {
13826
+ const match = markdown.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/);
13827
+ if (!match)
13828
+ throw new Error("Invalid plan artifact: missing frontmatter");
13829
+ const rawMetadata = {};
13830
+ for (const line of match[1].split(/\r?\n/)) {
13831
+ const separator = line.indexOf(":");
13832
+ if (separator === -1)
13833
+ continue;
13834
+ const key = line.slice(0, separator).trim();
13835
+ const value = line.slice(separator + 1);
13836
+ rawMetadata[key] = parseFrontmatterScalar(value);
13837
+ }
13838
+ if (rawMetadata.schema !== PLAN_MARKDOWN_SCHEMA) {
13839
+ throw new Error(`Unsupported plan artifact schema: ${rawMetadata.schema ?? "unknown"}`);
13840
+ }
13841
+ const required = ["plan_id", "project_id", "stable_id", "name", "status", "created_at", "updated_at", "artifact_updated_at"];
13842
+ for (const key of required) {
13843
+ if (!rawMetadata[key])
13844
+ throw new Error(`Invalid plan artifact: missing ${key}`);
13845
+ }
13846
+ const body = match[2] ?? "";
13847
+ return {
13848
+ metadata: {
13849
+ schema: PLAN_MARKDOWN_SCHEMA,
13850
+ plan_id: rawMetadata.plan_id,
13851
+ project_id: rawMetadata.project_id,
13852
+ task_list_id: rawMetadata.task_list_id ?? null,
13853
+ agent_id: rawMetadata.agent_id ?? null,
13854
+ stable_id: rawMetadata.stable_id,
13855
+ name: rawMetadata.name,
13856
+ status: rawMetadata.status,
13857
+ created_at: rawMetadata.created_at,
13858
+ updated_at: rawMetadata.updated_at,
13859
+ artifact_updated_at: rawMetadata.artifact_updated_at
13860
+ },
13861
+ task_references: parseTaskReferences(body),
13862
+ body
13863
+ };
13864
+ }
13865
+ function parseTaskReferences(body) {
13866
+ const references = [];
13867
+ const taskLine = /^\s*-\s+\[[ xX]\]\s+(.+)$/;
13868
+ const metadataLine = /<!--\s*todos:\s*task_id=([A-Za-z0-9._-]+)\s+status=([A-Za-z_]+)\s+priority=([A-Za-z_]+)\s*-->/;
13869
+ const lines = body.split(/\r?\n/);
13870
+ for (let index = 0;index < lines.length; index++) {
13871
+ const titleMatch = lines[index].match(taskLine);
13872
+ if (!titleMatch)
13873
+ continue;
13874
+ const metadataMatch = lines[index + 1]?.match(metadataLine);
13875
+ if (!metadataMatch)
13876
+ continue;
13877
+ references.push({
13878
+ task_id: metadataMatch[1],
13879
+ title: titleMatch[1].trim(),
13880
+ status: metadataMatch[2],
13881
+ priority: metadataMatch[3]
13882
+ });
13883
+ }
13884
+ return references;
13885
+ }
13886
+ function writePlanArtifact(plan, db) {
13887
+ if (!plan.project_id)
13888
+ return null;
13889
+ const d = db || getDatabase();
13890
+ const tasks = listTasks({ plan_id: plan.id, include_archived: true }, d);
13891
+ const paths = resolvePlanArtifactPaths({ project_id: plan.project_id, plan_id: plan.id, db: d });
13892
+ const snapshot = buildPlanArtifactSnapshot(plan, tasks);
13893
+ mkdirSync5(paths.directory, { recursive: true });
13894
+ writeFileSync3(paths.file_path, renderPlanArtifactMarkdown(snapshot), "utf8");
13895
+ return { path: paths.file_path, snapshot };
13896
+ }
13897
+ function readPlanArtifact(plan, db) {
13898
+ if (!plan.project_id)
13899
+ return null;
13900
+ const d = db || getDatabase();
13901
+ const paths = resolvePlanArtifactPaths({ project_id: plan.project_id, plan_id: plan.id, db: d });
13902
+ if (!existsSync8(paths.file_path))
13903
+ return null;
13904
+ const markdown = readFileSync4(paths.file_path, "utf8");
13905
+ return {
13906
+ path: paths.file_path,
13907
+ markdown,
13908
+ ...parsePlanArtifactMarkdown(markdown)
13909
+ };
13910
+ }
13911
+ function inspectPlanArtifact(plan, db) {
13912
+ if (!plan.project_id)
13913
+ return null;
13914
+ const d = db || getDatabase();
13915
+ const paths = resolvePlanArtifactPaths({ project_id: plan.project_id, plan_id: plan.id, db: d });
13916
+ if (!existsSync8(paths.file_path)) {
13917
+ return {
13918
+ path: paths.file_path,
13919
+ exists: false,
13920
+ parse_error: null,
13921
+ metadata: null,
13922
+ task_references: [],
13923
+ conflicts: []
13924
+ };
13925
+ }
13926
+ try {
13927
+ const artifact = parsePlanArtifactMarkdown(readFileSync4(paths.file_path, "utf8"));
13928
+ return {
13929
+ path: paths.file_path,
13930
+ exists: true,
13931
+ parse_error: null,
13932
+ metadata: artifact.metadata,
13933
+ task_references: artifact.task_references,
13934
+ conflicts: comparePlanArtifact(plan, artifact, listTasks({ plan_id: plan.id, include_archived: true }, d))
13935
+ };
13936
+ } catch (error) {
13937
+ return {
13938
+ path: paths.file_path,
13939
+ exists: true,
13940
+ parse_error: error instanceof Error ? error.message : String(error),
13941
+ metadata: null,
13942
+ task_references: [],
13943
+ conflicts: []
13944
+ };
13945
+ }
13946
+ }
13947
+ function comparePlanArtifact(plan, artifact, tasks) {
13948
+ const conflicts = [];
13949
+ compare("plan_id", plan.id, artifact.metadata.plan_id, conflicts);
13950
+ compare("project_id", plan.project_id ?? null, artifact.metadata.project_id, conflicts);
13951
+ compare("name", plan.name, artifact.metadata.name, conflicts);
13952
+ compare("status", plan.status, artifact.metadata.status, conflicts);
13953
+ compare("updated_at", plan.updated_at, artifact.metadata.updated_at, conflicts);
13954
+ const dbTaskIds = tasks.map((task) => task.id).sort();
13955
+ const artifactTaskIds = artifact.task_references.map((task) => task.task_id).sort();
13956
+ if (dbTaskIds.join(",") !== artifactTaskIds.join(",")) {
13957
+ conflicts.push({
13958
+ field: "task_references",
13959
+ database: dbTaskIds.join(",") || null,
13960
+ artifact: artifactTaskIds.join(",") || null
13961
+ });
13962
+ }
13963
+ return conflicts;
13964
+ }
13965
+ function compare(field, database, artifact, conflicts) {
13966
+ if ((database ?? null) !== (artifact ?? null)) {
13967
+ conflicts.push({ field, database: database ?? null, artifact: artifact ?? null });
13968
+ }
13969
+ }
13970
+ var PLAN_MARKDOWN_SCHEMA = "hasna.todos.plan/v1";
13971
+ var init_plan_artifacts = __esm(() => {
13972
+ init_database();
13973
+ init_projects();
13974
+ init_tasks();
13975
+ });
13976
+
13654
13977
  // src/db/builtin-templates.ts
13655
13978
  var exports_builtin_templates = {};
13656
13979
  __export(exports_builtin_templates, {
@@ -13665,8 +13988,8 @@ __export(exports_builtin_templates, {
13665
13988
  BUILTIN_TEMPLATE_LIBRARY_SOURCE: () => BUILTIN_TEMPLATE_LIBRARY_SOURCE,
13666
13989
  BUILTIN_TEMPLATES: () => BUILTIN_TEMPLATES
13667
13990
  });
13668
- import { mkdirSync as mkdirSync5, writeFileSync as writeFileSync3 } from "fs";
13669
- import { join as join7 } from "path";
13991
+ import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync4 } from "fs";
13992
+ import { join as join8 } from "path";
13670
13993
  function templateMetadata(template) {
13671
13994
  return {
13672
13995
  source: BUILTIN_TEMPLATE_LIBRARY_SOURCE,
@@ -13722,11 +14045,11 @@ function exportBuiltinTemplateFiles() {
13722
14045
  }));
13723
14046
  }
13724
14047
  function writeBuiltinTemplateFiles(directory) {
13725
- mkdirSync5(directory, { recursive: true });
14048
+ mkdirSync6(directory, { recursive: true });
13726
14049
  const files = [];
13727
14050
  for (const entry of exportBuiltinTemplateFiles()) {
13728
- const path = join7(directory, entry.filename);
13729
- writeFileSync3(path, `${JSON.stringify(entry.template, null, 2)}
14051
+ const path = join8(directory, entry.filename);
14052
+ writeFileSync4(path, `${JSON.stringify(entry.template, null, 2)}
13730
14053
  `, "utf-8");
13731
14054
  files.push(path);
13732
14055
  }
@@ -13994,7 +14317,7 @@ __export(exports_plan_template_commands, {
13994
14317
  });
13995
14318
  import chalk3 from "chalk";
13996
14319
  function registerPlanTemplateCommands(program2) {
13997
- program2.command("plans").description("List and manage plans").option("--add <name>", "Create a plan").option("-d, --description <text>", "Plan description (with --add)").option("--show <id>", "Show plan details with its tasks").option("--delete <id>", "Delete a plan").option("--complete <id>", "Mark a plan as completed").action((opts) => {
14320
+ program2.command("plans").description("List and manage plans").option("--add <name>", "Create a plan").option("-d, --description <text>", "Plan description (with --add)").option("--show <id>", "Show plan details with its tasks").option("--artifact <id>", "Show local Markdown artifact diagnostics for a plan").option("--write-artifacts", "Write local Markdown artifacts for all project-scoped plans in scope").option("--delete <id>", "Delete a plan").option("--complete <id>", "Mark a plan as completed").action((opts) => {
13998
14321
  const globalOpts = program2.opts();
13999
14322
  const projectId = autoProject(globalOpts);
14000
14323
  if (opts.add) {
@@ -14003,11 +14326,71 @@ function registerPlanTemplateCommands(program2) {
14003
14326
  description: opts.description,
14004
14327
  project_id: projectId
14005
14328
  });
14329
+ const artifact = writePlanArtifact(plan);
14006
14330
  if (globalOpts.json) {
14007
14331
  output(plan, true);
14008
14332
  } else {
14009
14333
  console.log(chalk3.green("Plan created:"));
14010
14334
  console.log(`${chalk3.dim(plan.id.slice(0, 8))} ${chalk3.bold(plan.name)} ${chalk3.cyan(`[${plan.status}]`)}`);
14335
+ if (artifact)
14336
+ console.log(`${chalk3.dim("Artifact:")} ${artifact.path}`);
14337
+ }
14338
+ return;
14339
+ }
14340
+ if (opts.artifact) {
14341
+ const db = getDatabase();
14342
+ const resolvedId = resolvePartialId(db, "plans", opts.artifact);
14343
+ if (!resolvedId) {
14344
+ console.error(chalk3.red(`Could not resolve plan ID: ${opts.artifact}`));
14345
+ process.exit(1);
14346
+ }
14347
+ const plan = getPlan(resolvedId);
14348
+ if (!plan) {
14349
+ console.error(chalk3.red(`Plan not found: ${opts.artifact}`));
14350
+ process.exit(1);
14351
+ }
14352
+ const inspection = inspectPlanArtifact(plan, db);
14353
+ if (!inspection) {
14354
+ const result = { plan_id: plan.id, artifact: null, reason: "plan is not project-scoped" };
14355
+ if (globalOpts.json)
14356
+ output(result, true);
14357
+ else
14358
+ console.log(chalk3.dim("Plan is not project-scoped; no local Markdown artifact path is available."));
14359
+ return;
14360
+ }
14361
+ if (globalOpts.json) {
14362
+ output({ plan, artifact: inspection }, true);
14363
+ return;
14364
+ }
14365
+ console.log(chalk3.bold(`Plan Artifact:
14366
+ `));
14367
+ console.log(` ${chalk3.dim("Plan:")} ${plan.id}`);
14368
+ console.log(` ${chalk3.dim("Path:")} ${inspection.path}`);
14369
+ console.log(` ${chalk3.dim("Exists:")} ${inspection.exists ? "yes" : "no"}`);
14370
+ if (inspection.parse_error)
14371
+ console.log(` ${chalk3.dim("Parse:")} ${chalk3.red(inspection.parse_error)}`);
14372
+ console.log(` ${chalk3.dim("Conflicts:")} ${inspection.conflicts.length}`);
14373
+ for (const conflict of inspection.conflicts) {
14374
+ console.log(` ${conflict.field}: db=${conflict.database ?? "null"} artifact=${conflict.artifact ?? "null"}`);
14375
+ }
14376
+ return;
14377
+ }
14378
+ if (opts.writeArtifacts) {
14379
+ const plans2 = listPlans(projectId);
14380
+ const written = plans2.map((plan) => ({ plan, artifact: writePlanArtifact(plan) })).filter((entry) => entry.artifact);
14381
+ const result = {
14382
+ count: written.length,
14383
+ artifacts: written.map((entry) => ({
14384
+ plan_id: entry.plan.id,
14385
+ path: entry.artifact.path
14386
+ }))
14387
+ };
14388
+ if (globalOpts.json) {
14389
+ output(result, true);
14390
+ } else {
14391
+ console.log(chalk3.green(`Wrote ${written.length} plan artifact(s).`));
14392
+ for (const artifact of result.artifacts)
14393
+ console.log(`${chalk3.dim(artifact.plan_id.slice(0, 8))} ${artifact.path}`);
14011
14394
  }
14012
14395
  return;
14013
14396
  }
@@ -14025,8 +14408,18 @@ function registerPlanTemplateCommands(program2) {
14025
14408
  }
14026
14409
  const { listTasks: listTasks2 } = (init_tasks(), __toCommonJS(exports_tasks));
14027
14410
  const tasks = listTasks2({ plan_id: resolvedId });
14411
+ const artifact = readPlanArtifact(plan, db);
14028
14412
  if (globalOpts.json) {
14029
- output({ plan, tasks }, true);
14413
+ output({
14414
+ plan,
14415
+ tasks,
14416
+ artifact: artifact ? {
14417
+ path: artifact.path,
14418
+ metadata: artifact.metadata,
14419
+ task_references: artifact.task_references,
14420
+ body: artifact.body
14421
+ } : null
14422
+ }, true);
14030
14423
  return;
14031
14424
  }
14032
14425
  console.log(chalk3.bold(`Plan Details:
@@ -14038,6 +14431,8 @@ function registerPlanTemplateCommands(program2) {
14038
14431
  console.log(` ${chalk3.dim("Desc:")} ${plan.description}`);
14039
14432
  if (plan.project_id)
14040
14433
  console.log(` ${chalk3.dim("Project:")} ${plan.project_id}`);
14434
+ if (artifact)
14435
+ console.log(` ${chalk3.dim("Artifact:")} ${artifact.path}`);
14041
14436
  console.log(` ${chalk3.dim("Created:")} ${plan.created_at}`);
14042
14437
  if (tasks.length > 0) {
14043
14438
  console.log(chalk3.bold(`
@@ -14078,11 +14473,14 @@ function registerPlanTemplateCommands(program2) {
14078
14473
  }
14079
14474
  try {
14080
14475
  const plan = updatePlan(resolvedId, { status: "completed" });
14476
+ const artifact = writePlanArtifact(plan);
14081
14477
  if (globalOpts.json) {
14082
14478
  output(plan, true);
14083
14479
  } else {
14084
14480
  console.log(chalk3.green("Plan completed:"));
14085
14481
  console.log(`${chalk3.dim(plan.id.slice(0, 8))} ${chalk3.bold(plan.name)} ${chalk3.cyan(`[${plan.status}]`)}`);
14482
+ if (artifact)
14483
+ console.log(`${chalk3.dim("Artifact:")} ${artifact.path}`);
14086
14484
  }
14087
14485
  } catch (e) {
14088
14486
  handleError(e);
@@ -14360,14 +14758,14 @@ function registerPlanTemplateCommands(program2) {
14360
14758
  program2.command("template-import [file]").alias("templates-import").description("Import a template from a JSON file").option("--file <path>", "Path to template JSON file (alternative to positional arg)").action(async (file, opts) => {
14361
14759
  const globalOpts = program2.opts();
14362
14760
  const { importTemplate: importTemplate2 } = await Promise.resolve().then(() => (init_templates(), exports_templates));
14363
- const { readFileSync: readFileSync4 } = await import("fs");
14761
+ const { readFileSync: readFileSync5 } = await import("fs");
14364
14762
  try {
14365
14763
  const filePath = file || opts.file;
14366
14764
  if (!filePath) {
14367
14765
  console.error(chalk3.red("Provide a file path: todos template-import <file> or --file <path>"));
14368
14766
  process.exit(1);
14369
14767
  }
14370
- const content = readFileSync4(filePath, "utf-8");
14768
+ const content = readFileSync5(filePath, "utf-8");
14371
14769
  const json = JSON.parse(content);
14372
14770
  const template = importTemplate2(json);
14373
14771
  if (globalOpts.json) {
@@ -14411,6 +14809,7 @@ var init_plan_template_commands = __esm(() => {
14411
14809
  init_database();
14412
14810
  init_plans();
14413
14811
  init_tasks();
14812
+ init_plan_artifacts();
14414
14813
  init_helpers();
14415
14814
  });
14416
14815
 
@@ -14979,16 +15378,16 @@ var init_saved_search_views = __esm(() => {
14979
15378
  });
14980
15379
 
14981
15380
  // src/lib/claude-tasks.ts
14982
- import { existsSync as existsSync8, readFileSync as readFileSync4, readdirSync as readdirSync2, writeFileSync as writeFileSync4 } from "fs";
14983
- import { join as join8 } from "path";
15381
+ import { existsSync as existsSync9, readFileSync as readFileSync5, readdirSync as readdirSync2, writeFileSync as writeFileSync5 } from "fs";
15382
+ import { join as join9 } from "path";
14984
15383
  function getTaskListDir(taskListId) {
14985
- return join8(HOME, ".claude", "tasks", taskListId);
15384
+ return join9(HOME, ".claude", "tasks", taskListId);
14986
15385
  }
14987
15386
  function readClaudeTask(dir, filename) {
14988
- return readJsonFile(join8(dir, filename));
15387
+ return readJsonFile(join9(dir, filename));
14989
15388
  }
14990
15389
  function writeClaudeTask(dir, task) {
14991
- writeJsonFile(join8(dir, `${task.id}.json`), task);
15390
+ writeJsonFile(join9(dir, `${task.id}.json`), task);
14992
15391
  }
14993
15392
  function toClaudeStatus(status) {
14994
15393
  if (status === "pending" || status === "in_progress" || status === "completed") {
@@ -15000,14 +15399,14 @@ function toSqliteStatus(status) {
15000
15399
  return status;
15001
15400
  }
15002
15401
  function readPrefixCounter(dir) {
15003
- const path = join8(dir, ".prefix-counter");
15004
- if (!existsSync8(path))
15402
+ const path = join9(dir, ".prefix-counter");
15403
+ if (!existsSync9(path))
15005
15404
  return 0;
15006
- const val = parseInt(readFileSync4(path, "utf-8").trim(), 10);
15405
+ const val = parseInt(readFileSync5(path, "utf-8").trim(), 10);
15007
15406
  return isNaN(val) ? 0 : val;
15008
15407
  }
15009
15408
  function writePrefixCounter(dir, value) {
15010
- writeFileSync4(join8(dir, ".prefix-counter"), String(value));
15409
+ writeFileSync5(join9(dir, ".prefix-counter"), String(value));
15011
15410
  }
15012
15411
  function formatPrefixedSubject(title, prefix, counter) {
15013
15412
  const padded = String(counter).padStart(5, "0");
@@ -15034,7 +15433,7 @@ function taskToClaudeTask(task, claudeTaskId, existingMeta) {
15034
15433
  }
15035
15434
  function pushToClaudeTaskList(taskListId, projectId, options = {}) {
15036
15435
  const dir = getTaskListDir(taskListId);
15037
- if (!existsSync8(dir))
15436
+ if (!existsSync9(dir))
15038
15437
  ensureDir2(dir);
15039
15438
  const filter = {};
15040
15439
  if (projectId)
@@ -15043,7 +15442,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
15043
15442
  const existingByTodosId = new Map;
15044
15443
  const files = listJsonFiles(dir);
15045
15444
  for (const f of files) {
15046
- const path = join8(dir, f);
15445
+ const path = join9(dir, f);
15047
15446
  const ct = readClaudeTask(dir, f);
15048
15447
  if (ct?.metadata?.["todos_id"]) {
15049
15448
  existingByTodosId.set(ct.metadata["todos_id"], { task: ct, mtimeMs: getFileMtimeMs(path) });
@@ -15130,7 +15529,7 @@ function pushToClaudeTaskList(taskListId, projectId, options = {}) {
15130
15529
  }
15131
15530
  function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
15132
15531
  const dir = getTaskListDir(taskListId);
15133
- if (!existsSync8(dir)) {
15532
+ if (!existsSync9(dir)) {
15134
15533
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
15135
15534
  }
15136
15535
  const files = readdirSync2(dir).filter((f) => f.endsWith(".json"));
@@ -15150,7 +15549,7 @@ function pullFromClaudeTaskList(taskListId, projectId, options = {}) {
15150
15549
  }
15151
15550
  for (const f of files) {
15152
15551
  try {
15153
- const filePath = join8(dir, f);
15552
+ const filePath = join9(dir, f);
15154
15553
  const ct = readClaudeTask(dir, f);
15155
15554
  if (!ct)
15156
15555
  continue;
@@ -15223,20 +15622,20 @@ var init_claude_tasks = __esm(() => {
15223
15622
  });
15224
15623
 
15225
15624
  // src/lib/agent-tasks.ts
15226
- import { existsSync as existsSync9 } from "fs";
15227
- import { join as join9 } from "path";
15625
+ import { existsSync as existsSync10 } from "fs";
15626
+ import { join as join10 } from "path";
15228
15627
  function agentBaseDir(agent) {
15229
15628
  const key = `TODOS_${agent.toUpperCase()}_TASKS_DIR`;
15230
- return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join9(getTodosGlobalDir(), "agents");
15629
+ return process.env[key] || getAgentTasksDir(agent) || process.env["TODOS_AGENT_TASKS_DIR"] || join10(getTodosGlobalDir(), "agents");
15231
15630
  }
15232
15631
  function getTaskListDir2(agent, taskListId) {
15233
- return join9(agentBaseDir(agent), agent, taskListId);
15632
+ return join10(agentBaseDir(agent), agent, taskListId);
15234
15633
  }
15235
15634
  function readAgentTask(dir, filename) {
15236
- return readJsonFile(join9(dir, filename));
15635
+ return readJsonFile(join10(dir, filename));
15237
15636
  }
15238
15637
  function writeAgentTask(dir, task) {
15239
- writeJsonFile(join9(dir, `${task.id}.json`), task);
15638
+ writeJsonFile(join10(dir, `${task.id}.json`), task);
15240
15639
  }
15241
15640
  function taskToAgentTask(task, externalId, existingMeta) {
15242
15641
  return {
@@ -15261,7 +15660,7 @@ function metadataKey(agent) {
15261
15660
  }
15262
15661
  function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
15263
15662
  const dir = getTaskListDir2(agent, taskListId);
15264
- if (!existsSync9(dir))
15663
+ if (!existsSync10(dir))
15265
15664
  ensureDir2(dir);
15266
15665
  const filter = {};
15267
15666
  if (projectId)
@@ -15270,7 +15669,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
15270
15669
  const existingByTodosId = new Map;
15271
15670
  const files = listJsonFiles(dir);
15272
15671
  for (const f of files) {
15273
- const path = join9(dir, f);
15672
+ const path = join10(dir, f);
15274
15673
  const at = readAgentTask(dir, f);
15275
15674
  if (at?.metadata?.["todos_id"]) {
15276
15675
  existingByTodosId.set(at.metadata["todos_id"], { task: at, mtimeMs: getFileMtimeMs(path) });
@@ -15344,7 +15743,7 @@ function pushToAgentTaskList(agent, taskListId, projectId, options = {}) {
15344
15743
  }
15345
15744
  function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
15346
15745
  const dir = getTaskListDir2(agent, taskListId);
15347
- if (!existsSync9(dir)) {
15746
+ if (!existsSync10(dir)) {
15348
15747
  return { pushed: 0, pulled: 0, errors: [`Task list directory not found: ${dir}`] };
15349
15748
  }
15350
15749
  const files = listJsonFiles(dir);
@@ -15363,7 +15762,7 @@ function pullFromAgentTaskList(agent, taskListId, projectId, options = {}) {
15363
15762
  }
15364
15763
  for (const f of files) {
15365
15764
  try {
15366
- const filePath = join9(dir, f);
15765
+ const filePath = join10(dir, f);
15367
15766
  const at = readAgentTask(dir, f);
15368
15767
  if (!at)
15369
15768
  continue;
@@ -15516,8 +15915,8 @@ __export(exports_project_bootstrap, {
15516
15915
  discoverProjectWorkspace: () => discoverProjectWorkspace,
15517
15916
  bootstrapProject: () => bootstrapProject
15518
15917
  });
15519
- import { existsSync as existsSync10, readFileSync as readFileSync5, statSync as statSync3 } from "fs";
15520
- import { basename as basename4, dirname as dirname6, resolve as resolve10 } from "path";
15918
+ import { existsSync as existsSync11, readFileSync as readFileSync6, statSync as statSync3 } from "fs";
15919
+ import { basename as basename4, dirname as dirname6, resolve as resolve11 } from "path";
15521
15920
  function safeStat(path) {
15522
15921
  try {
15523
15922
  return statSync3(path);
@@ -15526,7 +15925,7 @@ function safeStat(path) {
15526
15925
  }
15527
15926
  }
15528
15927
  function canonicalPath(input) {
15529
- const resolved = resolve10(input);
15928
+ const resolved = resolve11(input);
15530
15929
  const stats = safeStat(resolved);
15531
15930
  if (stats?.isFile())
15532
15931
  return dirname6(resolved);
@@ -15535,7 +15934,7 @@ function canonicalPath(input) {
15535
15934
  function findUp(start, marker) {
15536
15935
  let current = canonicalPath(start);
15537
15936
  while (true) {
15538
- if (existsSync10(resolve10(current, marker)))
15937
+ if (existsSync11(resolve11(current, marker)))
15539
15938
  return current;
15540
15939
  const parent = dirname6(current);
15541
15940
  if (parent === current)
@@ -15546,11 +15945,11 @@ function findUp(start, marker) {
15546
15945
  function readPackageJson(path) {
15547
15946
  if (!path)
15548
15947
  return null;
15549
- const file = resolve10(path, "package.json");
15550
- if (!existsSync10(file))
15948
+ const file = resolve11(path, "package.json");
15949
+ if (!existsSync11(file))
15551
15950
  return null;
15552
15951
  try {
15553
- const parsed = JSON.parse(readFileSync5(file, "utf-8"));
15952
+ const parsed = JSON.parse(readFileSync6(file, "utf-8"));
15554
15953
  return parsed && typeof parsed === "object" ? parsed : null;
15555
15954
  } catch {
15556
15955
  return null;
@@ -15569,7 +15968,7 @@ function workspaceMarker(root, rootPackage) {
15569
15968
  if (rootPackage?.workspaces)
15570
15969
  markers.push("package.json#workspaces");
15571
15970
  for (const marker of ["pnpm-workspace.yaml", "turbo.json", "nx.json", "lerna.json", "rush.json", "bun.lock", "bun.lockb"]) {
15572
- if (existsSync10(resolve10(root, marker)))
15971
+ if (existsSync11(resolve11(root, marker)))
15573
15972
  markers.push(marker);
15574
15973
  }
15575
15974
  const kind = markers.find((marker) => marker !== "bun.lock" && marker !== "bun.lockb") ?? null;
@@ -21092,9 +21491,9 @@ __export(exports_extract, {
21092
21491
  buildCodebaseIndex: () => buildCodebaseIndex,
21093
21492
  EXTRACT_TAGS: () => EXTRACT_TAGS
21094
21493
  });
21095
- import { existsSync as existsSync11, readFileSync as readFileSync6, statSync as statSync4 } from "fs";
21494
+ import { existsSync as existsSync12, readFileSync as readFileSync7, statSync as statSync4 } from "fs";
21096
21495
  import { createHash as createHash3 } from "crypto";
21097
- import { relative as relative3, resolve as resolve11, join as join10 } from "path";
21496
+ import { relative as relative3, resolve as resolve12, join as join11 } from "path";
21098
21497
  function stableHash(value) {
21099
21498
  return createHash3("sha256").update(value).digest("hex");
21100
21499
  }
@@ -21102,12 +21501,12 @@ function normalizePathForMatch(value) {
21102
21501
  return value.replace(/\\/g, "/").replace(/^\.\//, "");
21103
21502
  }
21104
21503
  function readGitignorePatterns(basePath) {
21105
- const root = statSync4(basePath).isFile() ? resolve11(basePath, "..") : basePath;
21106
- const gitignorePath = join10(root, ".gitignore");
21107
- if (!existsSync11(gitignorePath))
21504
+ const root = statSync4(basePath).isFile() ? resolve12(basePath, "..") : basePath;
21505
+ const gitignorePath = join11(root, ".gitignore");
21506
+ if (!existsSync12(gitignorePath))
21108
21507
  return [];
21109
21508
  try {
21110
- return readFileSync6(gitignorePath, "utf-8").split(`
21509
+ return readFileSync7(gitignorePath, "utf-8").split(`
21111
21510
  `).map((line) => line.trim()).filter((line) => line && !line.startsWith("#") && !line.startsWith("!"));
21112
21511
  } catch {
21113
21512
  return [];
@@ -21238,7 +21637,7 @@ function collectFiles(basePath, extensions, excludes, respectGitignore) {
21238
21637
  return files.sort();
21239
21638
  }
21240
21639
  function buildCodebaseIndex(options) {
21241
- const basePath = resolve11(options.path);
21640
+ const basePath = resolve12(options.path);
21242
21641
  const tags = options.patterns || [...EXTRACT_TAGS];
21243
21642
  const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
21244
21643
  const excludes = options.exclude || [];
@@ -21246,10 +21645,10 @@ function buildCodebaseIndex(options) {
21246
21645
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
21247
21646
  const indexed = [];
21248
21647
  for (const file of files) {
21249
- const fullPath = statSync4(basePath).isFile() ? basePath : join10(basePath, file);
21648
+ const fullPath = statSync4(basePath).isFile() ? basePath : join11(basePath, file);
21250
21649
  try {
21251
- const source = readFileSync6(fullPath, "utf-8");
21252
- const relPath = statSync4(basePath).isFile() ? relative3(resolve11(basePath, ".."), fullPath) : file;
21650
+ const source = readFileSync7(fullPath, "utf-8");
21651
+ const relPath = statSync4(basePath).isFile() ? relative3(resolve12(basePath, ".."), fullPath) : file;
21253
21652
  indexed.push({
21254
21653
  file: relPath,
21255
21654
  checksum: stableHash(source).slice(0, 24),
@@ -21269,7 +21668,7 @@ function buildCodebaseIndex(options) {
21269
21668
  };
21270
21669
  }
21271
21670
  function extractTodos(options, db) {
21272
- const basePath = resolve11(options.path);
21671
+ const basePath = resolve12(options.path);
21273
21672
  const tags = options.patterns || [...EXTRACT_TAGS];
21274
21673
  const extensions = options.extensions ? new Set(options.extensions.map((e) => e.startsWith(".") ? e : `.${e}`)) : DEFAULT_EXTENSIONS;
21275
21674
  const excludes = options.exclude || [];
@@ -21277,10 +21676,10 @@ function extractTodos(options, db) {
21277
21676
  const files = collectFiles(basePath, extensions, excludes, respectGitignore);
21278
21677
  const allComments = [];
21279
21678
  for (const file of files) {
21280
- const fullPath = statSync4(basePath).isFile() ? basePath : join10(basePath, file);
21679
+ const fullPath = statSync4(basePath).isFile() ? basePath : join11(basePath, file);
21281
21680
  try {
21282
- const source = readFileSync6(fullPath, "utf-8");
21283
- const relPath = statSync4(basePath).isFile() ? relative3(resolve11(basePath, ".."), fullPath) : file;
21681
+ const source = readFileSync7(fullPath, "utf-8");
21682
+ const relPath = statSync4(basePath).isFile() ? relative3(resolve12(basePath, ".."), fullPath) : file;
21284
21683
  const comments = extractFromSource(source, relPath, tags);
21285
21684
  allComments.push(...comments);
21286
21685
  } catch {}
@@ -21374,7 +21773,7 @@ async function watchSourceTodos(options, onRun) {
21374
21773
  const interval = Math.max(100, options.interval_ms || 2000);
21375
21774
  const once = options.once !== false && (!options.max_runs || options.max_runs <= 1);
21376
21775
  const maxRuns = options.max_runs ?? (once ? 1 : Number.POSITIVE_INFINITY);
21377
- const root = resolve11(options.path);
21776
+ const root = resolve12(options.path);
21378
21777
  const runs = [];
21379
21778
  let previous = new Map;
21380
21779
  for (let runNumber = 1;runNumber <= maxRuns; runNumber++) {
@@ -22545,7 +22944,7 @@ __export(exports_project_commands, {
22545
22944
  registerProjectCommands: () => registerProjectCommands
22546
22945
  });
22547
22946
  import chalk4 from "chalk";
22548
- import { basename as basename5, resolve as resolve12 } from "path";
22947
+ import { basename as basename5, resolve as resolve13 } from "path";
22549
22948
  function collectOption(value, previous = []) {
22550
22949
  return [...previous, value];
22551
22950
  }
@@ -22887,7 +23286,7 @@ function registerProjectCommands(program2) {
22887
23286
  program2.command("projects").description("List and manage projects").option("--add <path>", "Register a project by path").option("--name <name>", "Project name (with --add)").option("--task-list-id <id>", "Custom task list ID (with --add)").action(async (opts) => {
22888
23287
  const globalOpts = program2.opts();
22889
23288
  if (opts.add) {
22890
- const projectPath = resolve12(opts.add);
23289
+ const projectPath = resolve13(opts.add);
22891
23290
  const name = opts.name || basename5(projectPath);
22892
23291
  const existing = getProjectByPath(projectPath);
22893
23292
  let project;
@@ -22995,7 +23394,7 @@ function registerProjectCommands(program2) {
22995
23394
  console.error(chalk4.red(`Project not found: ${projectId}`));
22996
23395
  process.exit(1);
22997
23396
  }
22998
- const entry = setMachineLocalPath2(resolved, resolve12(projectPath));
23397
+ const entry = setMachineLocalPath2(resolved, resolve13(projectPath));
22999
23398
  if (useJson) {
23000
23399
  output(entry, true);
23001
23400
  } else {
@@ -23062,7 +23461,7 @@ function registerProjectCommands(program2) {
23062
23461
  const patterns = opts.pattern ? opts.pattern.split(",").map((t) => t.trim().toUpperCase()) : undefined;
23063
23462
  const taskListId = opts.list ? resolveTaskListId(opts.list) : undefined;
23064
23463
  const result = extractTodos2({
23065
- path: resolve12(scanPath),
23464
+ path: resolve13(scanPath),
23066
23465
  patterns,
23067
23466
  project_id: projectId,
23068
23467
  task_list_id: taskListId,
@@ -23123,7 +23522,7 @@ Indexed ${result.index.files.length} file(s), ${result.index.total_symbols} symb
23123
23522
  const taskListId = opts.list ? resolveTaskListId(opts.list) : undefined;
23124
23523
  const maxRuns = opts.maxRuns ? parseInt(opts.maxRuns, 10) : 1;
23125
23524
  const result = await watchSourceTodos2({
23126
- path: resolve12(scanPath),
23525
+ path: resolve13(scanPath),
23127
23526
  patterns,
23128
23527
  project_id: projectId,
23129
23528
  task_list_id: taskListId,
@@ -23159,8 +23558,8 @@ Indexed ${result.index.files.length} file(s), ${result.index.total_symbols} symb
23159
23558
  const projectId = autoProject(globalOpts);
23160
23559
  const writeOutput = async (content) => {
23161
23560
  if (opts.output) {
23162
- const { writeFileSync: writeFileSync5 } = await import("fs");
23163
- writeFileSync5(resolve12(opts.output), content.endsWith(`
23561
+ const { writeFileSync: writeFileSync6 } = await import("fs");
23562
+ writeFileSync6(resolve13(opts.output), content.endsWith(`
23164
23563
  `) ? content : `${content}
23165
23564
  `);
23166
23565
  } else {
@@ -23175,12 +23574,12 @@ Indexed ${result.index.files.length} file(s), ${result.index.total_symbols} symb
23175
23574
  const exported = opts.encrypt ? createEncryptedBridgeBundle2(bundle, { profile: opts.encryptionProfile }) : bundle;
23176
23575
  const json = JSON.stringify(exported, null, 2);
23177
23576
  await writeOutput(json);
23178
- emitLocalEventHooksQuiet3({ type: "export.finished", payload: { format: "bridge", encrypted: Boolean(opts.encrypt), project_id: projectId, output: opts.output ? resolve12(opts.output) : null, stats: bundle.stats } });
23577
+ emitLocalEventHooksQuiet3({ type: "export.finished", payload: { format: "bridge", encrypted: Boolean(opts.encrypt), project_id: projectId, output: opts.output ? resolve13(opts.output) : null, stats: bundle.stats } });
23179
23578
  if (!opts.encrypt && !opts.allowPlaintextSensitive) {
23180
23579
  console.error(chalk4.yellow("Warning: bridge exports are plaintext JSON. Use --encrypt for sensitive metadata, evidence, and artifact bundles."));
23181
23580
  }
23182
23581
  if (opts.output && !globalOpts.json) {
23183
- console.log(chalk4.green(`${opts.encrypt ? "Encrypted bridge export" : "Bridge export"} written to ${resolve12(opts.output)}`));
23582
+ console.log(chalk4.green(`${opts.encrypt ? "Encrypted bridge export" : "Bridge export"} written to ${resolve13(opts.output)}`));
23184
23583
  }
23185
23584
  return;
23186
23585
  }
@@ -23193,21 +23592,21 @@ Indexed ${result.index.files.length} file(s), ${result.index.total_symbols} symb
23193
23592
  await writeOutput(JSON.stringify(tasks, null, 2));
23194
23593
  }
23195
23594
  const { emitLocalEventHooksQuiet: emitLocalEventHooksQuiet2 } = await Promise.resolve().then(() => (init_event_hooks(), exports_event_hooks));
23196
- emitLocalEventHooksQuiet2({ type: "export.finished", payload: { format: opts.format, project_id: projectId, output: opts.output ? resolve12(opts.output) : null, count: exportedCount } });
23595
+ emitLocalEventHooksQuiet2({ type: "export.finished", payload: { format: opts.format, project_id: projectId, output: opts.output ? resolve13(opts.output) : null, count: exportedCount } });
23197
23596
  });
23198
23597
  program2.command("bridge-import <file>").description("Dry-run or apply a local hasna/todos bridge export bundle").option("--apply", "Apply the import. Defaults to dry-run.").option("--decrypt", "Decrypt an encrypted bridge export before importing").option("--resolve-conflicts", "Safely merge existing local tasks by filling blank fields, unioning tags, and recording unresolved divergences").action(async (file, opts) => {
23199
23598
  const globalOpts = program2.opts();
23200
23599
  try {
23201
- const { readFileSync: readFileSync7 } = await import("fs");
23600
+ const { readFileSync: readFileSync8 } = await import("fs");
23202
23601
  const { importLocalBridgeBundle: importLocalBridgeBundle2 } = await Promise.resolve().then(() => (init_local_bridge(), exports_local_bridge));
23203
23602
  const { decryptBridgeBundle: decryptBridgeBundle2, isEncryptedBridgeBundle: isEncryptedBridgeBundle2 } = await Promise.resolve().then(() => (init_local_encryption(), exports_local_encryption));
23204
- const parsed = JSON.parse(readFileSync7(resolve12(file), "utf-8"));
23603
+ const parsed = JSON.parse(readFileSync8(resolve13(file), "utf-8"));
23205
23604
  const bundle = isEncryptedBridgeBundle2(parsed) ? opts.decrypt ? decryptBridgeBundle2(parsed) : (() => {
23206
23605
  throw new Error("Bridge bundle is encrypted. Re-run with --decrypt and the configured key environment variable set.");
23207
23606
  })() : parsed;
23208
23607
  const result = importLocalBridgeBundle2(bundle, { dryRun: !opts.apply, conflictStrategy: opts.resolveConflicts ? "safe_merge" : "skip" });
23209
23608
  const { emitLocalEventHooksQuiet: emitLocalEventHooksQuiet2 } = await Promise.resolve().then(() => (init_event_hooks(), exports_event_hooks));
23210
- emitLocalEventHooksQuiet2({ type: "import.finished", payload: { file: resolve12(file), dry_run: result.dry_run, ok: result.ok, inserted: result.inserted, skipped: result.skipped, conflicts: result.conflicts.length, issues: result.issues.length } });
23609
+ emitLocalEventHooksQuiet2({ type: "import.finished", payload: { file: resolve13(file), dry_run: result.dry_run, ok: result.ok, inserted: result.inserted, skipped: result.skipped, conflicts: result.conflicts.length, issues: result.issues.length } });
23211
23610
  if (globalOpts.json) {
23212
23611
  output(result, true);
23213
23612
  return;
@@ -23235,11 +23634,11 @@ Indexed ${result.index.files.length} file(s), ${result.index.total_symbols} symb
23235
23634
  program2.command("todos-md-import <file>").alias("markdown-import").alias("import-md").description("Dry-run or apply a local todos.md Markdown import").option("--apply", "Apply the import. Defaults to dry-run.").option("--resolve-conflicts", "Safely merge embedded bridge task conflicts while preserving local divergent fields").action(async (file, opts) => {
23236
23635
  const globalOpts = program2.opts();
23237
23636
  try {
23238
- const { readFileSync: readFileSync7 } = await import("fs");
23637
+ const { readFileSync: readFileSync8 } = await import("fs");
23239
23638
  const { importTodosMarkdown: importTodosMarkdown2 } = await Promise.resolve().then(() => (init_todos_md(), exports_todos_md));
23240
- const result = importTodosMarkdown2(readFileSync7(resolve12(file), "utf-8"), { dryRun: !opts.apply, conflictStrategy: opts.resolveConflicts ? "safe_merge" : "skip" });
23639
+ const result = importTodosMarkdown2(readFileSync8(resolve13(file), "utf-8"), { dryRun: !opts.apply, conflictStrategy: opts.resolveConflicts ? "safe_merge" : "skip" });
23241
23640
  const { emitLocalEventHooksQuiet: emitLocalEventHooksQuiet2 } = await Promise.resolve().then(() => (init_event_hooks(), exports_event_hooks));
23242
- emitLocalEventHooksQuiet2({ type: "import.finished", payload: { file: resolve12(file), format: "todos.md", dry_run: result.dry_run, ok: result.ok, inserted: result.inserted, skipped: result.skipped, issues: result.issues.length } });
23641
+ emitLocalEventHooksQuiet2({ type: "import.finished", payload: { file: resolve13(file), format: "todos.md", dry_run: result.dry_run, ok: result.ok, inserted: result.inserted, skipped: result.skipped, issues: result.issues.length } });
23243
23642
  if (globalOpts.json) {
23244
23643
  output(result, true);
23245
23644
  return;
@@ -24367,7 +24766,7 @@ __export(exports_retention_cleanup, {
24367
24766
  applyRetentionCleanup: () => applyRetentionCleanup,
24368
24767
  RETENTION_CLEANUP_CONFIRMATION: () => RETENTION_CLEANUP_CONFIRMATION
24369
24768
  });
24370
- import { existsSync as existsSync12, unlinkSync } from "fs";
24769
+ import { existsSync as existsSync13, unlinkSync } from "fs";
24371
24770
  function normalizeScopes(scopes) {
24372
24771
  if (!scopes || scopes.length === 0)
24373
24772
  return [...ALL_SCOPES];
@@ -24570,7 +24969,7 @@ function applyRetentionCleanup(input, db) {
24570
24969
  for (const artifact of report.candidates.artifact_files) {
24571
24970
  try {
24572
24971
  const path = artifactStorePath(artifact.relative_path);
24573
- if (!existsSync12(path)) {
24972
+ if (!existsSync13(path)) {
24574
24973
  report.warnings.push(`stored artifact already missing: ${artifact.relative_path}`);
24575
24974
  continue;
24576
24975
  }
@@ -25248,8 +25647,8 @@ __export(exports_local_extensions, {
25248
25647
  discoverLocalExtensions: () => discoverLocalExtensions
25249
25648
  });
25250
25649
  import { createHash as createHash5, createVerify } from "crypto";
25251
- import { existsSync as existsSync13, readdirSync as readdirSync3, readFileSync as readFileSync7, statSync as statSync5 } from "fs";
25252
- import { basename as basename6, join as join11, resolve as resolve13 } from "path";
25650
+ import { existsSync as existsSync14, readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync5 } from "fs";
25651
+ import { basename as basename6, join as join12, resolve as resolve14 } from "path";
25253
25652
  function isObject(value) {
25254
25653
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
25255
25654
  }
@@ -25330,7 +25729,7 @@ function normalizeManifest(input) {
25330
25729
  };
25331
25730
  }
25332
25731
  function parseJson(path) {
25333
- return JSON.parse(readFileSync7(path, "utf8"));
25732
+ return JSON.parse(readFileSync8(path, "utf8"));
25334
25733
  }
25335
25734
  function sha2563(bytes) {
25336
25735
  return `sha256:${createHash5("sha256").update(bytes).digest("hex")}`;
@@ -25507,14 +25906,14 @@ function verifyExtensionSignature(input) {
25507
25906
  return verifier.verify(input.public_key, decodeSignature(input.signature));
25508
25907
  }
25509
25908
  function inspectExtensionSource(source2) {
25510
- const resolved = resolve13(source2);
25511
- if (!existsSync13(resolved))
25909
+ const resolved = resolve14(source2);
25910
+ if (!existsSync14(resolved))
25512
25911
  throw new Error(`extension source not found: ${source2}`);
25513
25912
  const stat = statSync5(resolved);
25514
- const manifestPath = stat.isDirectory() ? [join11(resolved, "todos.extension.json"), join11(resolved, "extension.json")].find(existsSync13) : resolved;
25913
+ const manifestPath = stat.isDirectory() ? [join12(resolved, "todos.extension.json"), join12(resolved, "extension.json")].find(existsSync14) : resolved;
25515
25914
  if (!manifestPath)
25516
25915
  throw new Error(`extension directory ${source2} is missing todos.extension.json`);
25517
- const raw = readFileSync7(manifestPath);
25916
+ const raw = readFileSync8(manifestPath);
25518
25917
  const parsed = parseJson(manifestPath);
25519
25918
  const bundle = isObject(parsed) && isObject(parsed["manifest"]);
25520
25919
  const manifest = normalizeManifest(bundle ? parsed["manifest"] : parsed);
@@ -25605,26 +26004,26 @@ function testExtensionCompatibility(sourceOrManifest) {
25605
26004
  function projectExtensionSources(projectPath) {
25606
26005
  if (!projectPath)
25607
26006
  return [];
25608
- const root = resolve13(projectPath);
26007
+ const root = resolve14(projectPath);
25609
26008
  const candidates = [
25610
- join11(root, "todos.extension.json"),
25611
- join11(root, ".todos", "todos.extension.json")
26009
+ join12(root, "todos.extension.json"),
26010
+ join12(root, ".todos", "todos.extension.json")
25612
26011
  ];
25613
- const extensionDir = join11(root, ".todos", "extensions");
25614
- if (existsSync13(extensionDir)) {
26012
+ const extensionDir = join12(root, ".todos", "extensions");
26013
+ if (existsSync14(extensionDir)) {
25615
26014
  for (const entry of readdirSync3(extensionDir)) {
25616
26015
  if (entry.startsWith("."))
25617
26016
  continue;
25618
- const full = join11(extensionDir, entry);
26017
+ const full = join12(extensionDir, entry);
25619
26018
  if (statSync5(full).isDirectory() || entry.endsWith(".json"))
25620
26019
  candidates.push(full);
25621
26020
  }
25622
26021
  }
25623
- return candidates.filter(existsSync13);
26022
+ return candidates.filter(existsSync14);
25624
26023
  }
25625
26024
  function discoverLocalExtensions(options = {}) {
25626
26025
  const config = loadConfig();
25627
- const projectPath = options.project_path ? resolve13(options.project_path) : null;
26026
+ const projectPath = options.project_path ? resolve14(options.project_path) : null;
25628
26027
  const configuredSources = [
25629
26028
  ...config.extension_sources || [],
25630
26029
  ...projectPath ? config.project_overrides?.[projectPath]?.extension_sources || [] : []
@@ -25632,7 +26031,7 @@ function discoverLocalExtensions(options = {}) {
25632
26031
  const sources = Array.from(new Set([
25633
26032
  ...configuredSources,
25634
26033
  ...projectExtensionSources(projectPath || undefined)
25635
- ])).map((source2) => projectPath && !source2.startsWith("/") ? resolve13(projectPath, source2) : resolve13(source2));
26034
+ ])).map((source2) => projectPath && !source2.startsWith("/") ? resolve14(projectPath, source2) : resolve14(source2));
25636
26035
  const warnings = [];
25637
26036
  const discovered = [];
25638
26037
  for (const source2 of sources) {
@@ -25892,9 +26291,9 @@ __export(exports_policy_packs, {
25892
26291
  getPolicyPack: () => getPolicyPack,
25893
26292
  explainPolicyPack: () => explainPolicyPack
25894
26293
  });
25895
- import { relative as relative4, resolve as resolve14 } from "path";
26294
+ import { relative as relative4, resolve as resolve15 } from "path";
25896
26295
  function normalizePath3(path) {
25897
- return resolve14(path);
26296
+ return resolve15(path);
25898
26297
  }
25899
26298
  function unique5(values) {
25900
26299
  return Array.from(new Set((values || []).map((value) => value.trim()).filter(Boolean)));
@@ -25949,7 +26348,7 @@ function commandMatches(commands, pattern) {
25949
26348
  }
25950
26349
  function pathMatches(paths, pattern, root) {
25951
26350
  return paths.filter((path) => {
25952
- const candidate = path.startsWith("/") ? path : resolve14(root, path);
26351
+ const candidate = path.startsWith("/") ? path : resolve15(root, path);
25953
26352
  if (!isPathInside3(root, candidate))
25954
26353
  return matchesPattern3(path, pattern);
25955
26354
  return matchesPattern3(path, pattern) || matchesPattern3(relative4(root, candidate), pattern);
@@ -26915,8 +27314,8 @@ var exports_doctor = {};
26915
27314
  __export(exports_doctor, {
26916
27315
  runTodosDoctor: () => runTodosDoctor
26917
27316
  });
26918
- import { chmodSync, copyFileSync, existsSync as existsSync14, mkdirSync as mkdirSync6, statSync as statSync6 } from "fs";
26919
- import { basename as basename7, dirname as dirname7, join as join12 } from "path";
27317
+ import { chmodSync, copyFileSync, existsSync as existsSync15, mkdirSync as mkdirSync7, statSync as statSync6 } from "fs";
27318
+ import { basename as basename7, dirname as dirname7, join as join13 } from "path";
26920
27319
  function tableExists(db, table) {
26921
27320
  return Boolean(db.query("SELECT name FROM sqlite_master WHERE type='table' AND name=?").get(table));
26922
27321
  }
@@ -27010,7 +27409,7 @@ function findMissingProjectRoots(db) {
27010
27409
  continue;
27011
27410
  if (!row.path.startsWith("/"))
27012
27411
  continue;
27013
- if (!existsSync14(row.path))
27412
+ if (!existsSync15(row.path))
27014
27413
  missing++;
27015
27414
  }
27016
27415
  return missing;
@@ -27070,16 +27469,16 @@ function databasePermissionsAreUnsafe(dbPath) {
27070
27469
  function createBackup(dbPath) {
27071
27470
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
27072
27471
  return;
27073
- if (!existsSync14(dbPath))
27472
+ if (!existsSync15(dbPath))
27074
27473
  return;
27075
27474
  const stamp = now().replace(/[:.]/g, "-");
27076
- const backupDir = join12(dirname7(dbPath), `${basename7(dbPath)}.backup-${stamp}`);
27475
+ const backupDir = join13(dirname7(dbPath), `${basename7(dbPath)}.backup-${stamp}`);
27077
27476
  const files = [];
27078
- mkdirSync6(backupDir, { recursive: true });
27477
+ mkdirSync7(backupDir, { recursive: true });
27079
27478
  for (const source2 of [dbPath, `${dbPath}-wal`, `${dbPath}-shm`]) {
27080
- if (!existsSync14(source2))
27479
+ if (!existsSync15(source2))
27081
27480
  continue;
27082
- const target = join12(backupDir, basename7(source2));
27481
+ const target = join13(backupDir, basename7(source2));
27083
27482
  copyFileSync(source2, target);
27084
27483
  files.push(target);
27085
27484
  }
@@ -27305,7 +27704,7 @@ var init_doctor = __esm(() => {
27305
27704
  });
27306
27705
 
27307
27706
  // src/server/routes.ts
27308
- import { join as join13, resolve as resolve15, sep as sep2 } from "path";
27707
+ import { join as join14, resolve as resolve16, sep as sep2 } from "path";
27309
27708
  function parseFieldsParam(url) {
27310
27709
  const fieldsParam = url.searchParams.get("fields");
27311
27710
  return fieldsParam ? fieldsParam.split(",").map((f) => f.trim()).filter(Boolean) : undefined;
@@ -28042,9 +28441,9 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
28042
28441
  if (!ctx.dashboardExists || method !== "GET" && method !== "HEAD")
28043
28442
  return null;
28044
28443
  if (path !== "/") {
28045
- const filePath = join13(ctx.dashboardDir, path);
28046
- const resolvedFile = resolve15(filePath);
28047
- const resolvedBase = resolve15(ctx.dashboardDir);
28444
+ const filePath = join14(ctx.dashboardDir, path);
28445
+ const resolvedFile = resolve16(filePath);
28446
+ const resolvedBase = resolve16(ctx.dashboardDir);
28048
28447
  if (!resolvedFile.startsWith(resolvedBase + sep2) && resolvedFile !== resolvedBase) {
28049
28448
  return json2({ error: "Forbidden" }, 403);
28050
28449
  }
@@ -28052,7 +28451,7 @@ function handleStaticFiles(path, method, ctx, json2, serveStaticFile2) {
28052
28451
  if (res2)
28053
28452
  return res2;
28054
28453
  }
28055
- const indexPath = join13(ctx.dashboardDir, "index.html");
28454
+ const indexPath = join14(ctx.dashboardDir, "index.html");
28056
28455
  const res = serveStaticFile2(indexPath);
28057
28456
  if (res)
28058
28457
  return res;
@@ -33047,8 +33446,8 @@ var exports_mention_resolver = {};
33047
33446
  __export(exports_mention_resolver, {
33048
33447
  resolveMentions: () => resolveMentions
33049
33448
  });
33050
- import { existsSync as existsSync15, readdirSync as readdirSync4, readFileSync as readFileSync8, statSync as statSync7 } from "fs";
33051
- import { basename as basename8, isAbsolute, join as join14, relative as relative5, resolve as resolve16, sep as sep3 } from "path";
33449
+ import { existsSync as existsSync16, readdirSync as readdirSync4, readFileSync as readFileSync9, statSync as statSync7 } from "fs";
33450
+ import { basename as basename8, isAbsolute, join as join15, relative as relative5, resolve as resolve17, sep as sep3 } from "path";
33052
33451
  function blankResolution(parsed) {
33053
33452
  return {
33054
33453
  input: parsed.input,
@@ -33071,7 +33470,7 @@ function backlink(kind, key, label, target = key) {
33071
33470
  return { kind, key, label, target };
33072
33471
  }
33073
33472
  function normalizeWorkspace(workspace) {
33074
- return resolve16(workspace || process.cwd());
33473
+ return resolve17(workspace || process.cwd());
33075
33474
  }
33076
33475
  function isInside(root, absolutePath) {
33077
33476
  const rel = relative5(root, absolutePath);
@@ -33139,14 +33538,14 @@ function resolveFile(parsed, workspace) {
33139
33538
  resolution.warnings.push("path is empty or escapes the workspace");
33140
33539
  return resolution;
33141
33540
  }
33142
- const absolutePath = resolve16(workspace, relPath);
33541
+ const absolutePath = resolve17(workspace, relPath);
33143
33542
  if (!isInside(workspace, absolutePath)) {
33144
33543
  resolution.path = relPath;
33145
33544
  resolution.warnings.push("path escapes the workspace");
33146
33545
  return resolution;
33147
33546
  }
33148
33547
  resolution.path = relPath;
33149
- if (!existsSync15(absolutePath)) {
33548
+ if (!existsSync16(absolutePath)) {
33150
33549
  resolution.warnings.push("file does not exist in the local workspace");
33151
33550
  return resolution;
33152
33551
  }
@@ -33156,7 +33555,7 @@ function resolveFile(parsed, workspace) {
33156
33555
  return resolution;
33157
33556
  }
33158
33557
  if (parsed.line !== undefined) {
33159
- const lineCount = readFileSync8(absolutePath, "utf-8").split(/\r?\n/).length;
33558
+ const lineCount = readFileSync9(absolutePath, "utf-8").split(/\r?\n/).length;
33160
33559
  if (parsed.line < 1 || parsed.line > lineCount) {
33161
33560
  resolution.warnings.push(`line ${parsed.line} is outside the file range 1-${lineCount}`);
33162
33561
  return resolution;
@@ -33179,7 +33578,7 @@ function walkSourceFiles(root, current = root, files = []) {
33179
33578
  if (SKIP_DIRS3.has(entry.name))
33180
33579
  continue;
33181
33580
  }
33182
- const absolutePath = join14(current, entry.name);
33581
+ const absolutePath = join15(current, entry.name);
33183
33582
  if (entry.isDirectory()) {
33184
33583
  if (!SKIP_DIRS3.has(entry.name))
33185
33584
  walkSourceFiles(root, absolutePath, files);
@@ -33209,7 +33608,7 @@ function resolveSymbol(parsed, workspace, maxMatches) {
33209
33608
  const pattern = symbolPattern(name);
33210
33609
  const matches = [];
33211
33610
  for (const file of walkSourceFiles(workspace)) {
33212
- const lines = readFileSync8(file, "utf-8").split(/\r?\n/);
33611
+ const lines = readFileSync9(file, "utf-8").split(/\r?\n/);
33213
33612
  for (let index = 0;index < lines.length; index += 1) {
33214
33613
  const line = lines[index];
33215
33614
  const found = pattern.exec(line);
@@ -35943,8 +36342,8 @@ __export(exports_release_compatibility, {
35943
36342
  createReleaseCompatibilityReport: () => createReleaseCompatibilityReport,
35944
36343
  LOCAL_RELEASE_COMPATIBILITY_SCHEMA_VERSION: () => LOCAL_RELEASE_COMPATIBILITY_SCHEMA_VERSION
35945
36344
  });
35946
- import { readFileSync as readFileSync9 } from "fs";
35947
- import { join as join15, resolve as resolve17 } from "path";
36345
+ import { readFileSync as readFileSync10 } from "fs";
36346
+ import { join as join16, resolve as resolve18 } from "path";
35948
36347
  import { Database as Database2 } from "bun:sqlite";
35949
36348
  function pass(id, message, details) {
35950
36349
  return { id, status: "passed", message, details };
@@ -35956,7 +36355,7 @@ function warn(id, message, details) {
35956
36355
  return { id, status: "warning", message, details };
35957
36356
  }
35958
36357
  function readPackageJson2(root) {
35959
- return JSON.parse(readFileSync9(join15(root, "package.json"), "utf8"));
36358
+ return JSON.parse(readFileSync10(join16(root, "package.json"), "utf8"));
35960
36359
  }
35961
36360
  function sortedKeys(value) {
35962
36361
  return Object.keys(value ?? {}).sort((left, right) => left.localeCompare(right));
@@ -36052,7 +36451,7 @@ function checkChangelog() {
36052
36451
  ];
36053
36452
  }
36054
36453
  function createReleaseCompatibilityReport(options = {}) {
36055
- const root = resolve17(options.root ?? process.cwd());
36454
+ const root = resolve18(options.root ?? process.cwd());
36056
36455
  const packageJson = readPackageJson2(root);
36057
36456
  const simulatedLevels = options.simulated_levels ?? defaultSimulationLevels();
36058
36457
  const checks = [
@@ -41635,7 +42034,7 @@ __export(exports_verification_providers, {
41635
42034
  getVerificationRecord: () => getVerificationRecord,
41636
42035
  discoverVerificationProviderCapabilities: () => discoverVerificationProviderCapabilities
41637
42036
  });
41638
- import { existsSync as existsSync16, readFileSync as readFileSync10 } from "fs";
42037
+ import { existsSync as existsSync17, readFileSync as readFileSync11 } from "fs";
41639
42038
  function normalizeName6(name) {
41640
42039
  const normalized = name.trim().toLowerCase();
41641
42040
  if (!/^[a-z0-9][a-z0-9_-]{0,63}$/.test(normalized)) {
@@ -41732,7 +42131,7 @@ function classifyLog(text) {
41732
42131
  async function sleep3(ms) {
41733
42132
  if (ms <= 0)
41734
42133
  return;
41735
- await new Promise((resolve18) => setTimeout(resolve18, ms));
42134
+ await new Promise((resolve19) => setTimeout(resolve19, ms));
41736
42135
  }
41737
42136
  async function runCommandProvider(provider, input) {
41738
42137
  const commandTemplate = input.command || provider.command;
@@ -41787,7 +42186,7 @@ Timed out after ${provider.timeout_ms}ms`);
41787
42186
  };
41788
42187
  }
41789
42188
  function runCiLogProvider(input) {
41790
- const text = input.log_text ?? (input.log_path && existsSync16(input.log_path) ? readFileSync10(input.log_path, "utf-8") : "");
42189
+ const text = input.log_text ?? (input.log_path && existsSync17(input.log_path) ? readFileSync11(input.log_path, "utf-8") : "");
41791
42190
  return {
41792
42191
  status: classifyLog(text),
41793
42192
  attempts: 1,
@@ -41799,7 +42198,7 @@ function runBrowserProvider(input) {
41799
42198
  if (!input.artifact_path) {
41800
42199
  return { status: "unknown", attempts: 1, exit_code: null, output_summary: "browser provider needs a screenshot or artifact path" };
41801
42200
  }
41802
- if (!existsSync16(input.artifact_path)) {
42201
+ if (!existsSync17(input.artifact_path)) {
41803
42202
  return { status: "failed", attempts: 1, exit_code: null, output_summary: `artifact not found: ${input.artifact_path}` };
41804
42203
  }
41805
42204
  return {
@@ -44081,9 +44480,9 @@ __export(exports_local_backups, {
44081
44480
  LOCAL_BACKUP_CHECKSUM_ALGORITHM: () => LOCAL_BACKUP_CHECKSUM_ALGORITHM
44082
44481
  });
44083
44482
  import { createHash as createHash8 } from "crypto";
44084
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync5 } from "fs";
44085
- import { dirname as dirname8, resolve as resolve18 } from "path";
44086
- import { mkdirSync as mkdirSync7 } from "fs";
44483
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync6 } from "fs";
44484
+ import { dirname as dirname8, resolve as resolve19 } from "path";
44485
+ import { mkdirSync as mkdirSync8 } from "fs";
44087
44486
  function stableJson(value) {
44088
44487
  if (value === null || typeof value !== "object")
44089
44488
  return JSON.stringify(value);
@@ -44184,14 +44583,14 @@ function createLocalBackup(options = {}, db) {
44184
44583
  return backup;
44185
44584
  }
44186
44585
  function writeLocalBackupFile(backup, outputPath) {
44187
- const path = resolve18(outputPath);
44188
- mkdirSync7(dirname8(path), { recursive: true });
44189
- writeFileSync5(path, `${JSON.stringify(backup, null, 2)}
44586
+ const path = resolve19(outputPath);
44587
+ mkdirSync8(dirname8(path), { recursive: true });
44588
+ writeFileSync6(path, `${JSON.stringify(backup, null, 2)}
44190
44589
  `);
44191
44590
  return path;
44192
44591
  }
44193
44592
  function readLocalBackupFile(path) {
44194
- return JSON.parse(readFileSync11(resolve18(path), "utf-8"));
44593
+ return JSON.parse(readFileSync12(resolve19(path), "utf-8"));
44195
44594
  }
44196
44595
  function verifyLocalBackup(value, options = {}, db) {
44197
44596
  const verifiedAt = options.verified_at ?? now();
@@ -44387,8 +44786,8 @@ __export(exports_onboarding_fixtures, {
44387
44786
  TODOS_ONBOARDING_FIXTURE_SOURCE: () => TODOS_ONBOARDING_FIXTURE_SOURCE,
44388
44787
  TODOS_ONBOARDING_FIXTURE_LIBRARY_VERSION: () => TODOS_ONBOARDING_FIXTURE_LIBRARY_VERSION
44389
44788
  });
44390
- import { mkdirSync as mkdirSync8, writeFileSync as writeFileSync6 } from "fs";
44391
- import { join as join16 } from "path";
44789
+ import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync7 } from "fs";
44790
+ import { join as join17 } from "path";
44392
44791
  function emptyData() {
44393
44792
  return {
44394
44793
  projects: [],
@@ -44718,11 +45117,11 @@ function getOnboardingFixtureBundle(name = "agent-project-demo") {
44718
45117
  return getOnboardingFixture(name).bundle;
44719
45118
  }
44720
45119
  function writeOnboardingFixtureFiles(directory) {
44721
- mkdirSync8(directory, { recursive: true });
45120
+ mkdirSync9(directory, { recursive: true });
44722
45121
  const files = [];
44723
45122
  for (const fixture of allFixtures()) {
44724
- const path = join16(directory, `${fixture.summary.name}.bridge.json`);
44725
- writeFileSync6(path, `${JSON.stringify(fixture.bundle, null, 2)}
45123
+ const path = join17(directory, `${fixture.summary.name}.bridge.json`);
45124
+ writeFileSync7(path, `${JSON.stringify(fixture.bundle, null, 2)}
44726
45125
  `, "utf-8");
44727
45126
  files.push(path);
44728
45127
  }
@@ -45369,7 +45768,7 @@ __export(exports_agent_replay_simulator, {
45369
45768
  renderAgentReplaySimulationMarkdown: () => renderAgentReplaySimulationMarkdown
45370
45769
  });
45371
45770
  import { createHash as createHash10 } from "crypto";
45372
- import { readFileSync as readFileSync12 } from "fs";
45771
+ import { readFileSync as readFileSync13 } from "fs";
45373
45772
  function isObject2(value) {
45374
45773
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
45375
45774
  }
@@ -45602,7 +46001,7 @@ function simulateAgentReplay(input, options = {}) {
45602
46001
  };
45603
46002
  }
45604
46003
  function simulateAgentReplayFile(path, options = {}) {
45605
- const parsed = JSON.parse(readFileSync12(path, "utf8"));
46004
+ const parsed = JSON.parse(readFileSync13(path, "utf8"));
45606
46005
  return simulateAgentReplay(parsed, options);
45607
46006
  }
45608
46007
  function renderAgentReplaySimulationMarkdown(simulation) {
@@ -50889,28 +51288,28 @@ __export(exports_environment_snapshots, {
50889
51288
  captureEnvironmentSnapshot: () => captureEnvironmentSnapshot
50890
51289
  });
50891
51290
  import { createHash as createHash12 } from "crypto";
50892
- import { existsSync as existsSync17, readFileSync as readFileSync13, statSync as statSync8 } from "fs";
51291
+ import { existsSync as existsSync18, readFileSync as readFileSync14, statSync as statSync8 } from "fs";
50893
51292
  import { hostname as hostname2, platform, arch } from "os";
50894
- import { dirname as dirname9, join as join17, resolve as resolve19 } from "path";
51293
+ import { dirname as dirname9, join as join18, resolve as resolve20 } from "path";
50895
51294
  import { tmpdir as tmpdir3 } from "os";
50896
51295
  function sha2566(value) {
50897
51296
  return createHash12("sha256").update(value).digest("hex");
50898
51297
  }
50899
51298
  function fileRecord(root, relativePath) {
50900
- const path = join17(root, relativePath);
50901
- if (!existsSync17(path))
51299
+ const path = join18(root, relativePath);
51300
+ if (!existsSync18(path))
50902
51301
  return null;
50903
51302
  const stat = statSync8(path);
50904
51303
  if (!stat.isFile())
50905
51304
  return null;
50906
- const content = readFileSync13(path);
51305
+ const content = readFileSync14(path);
50907
51306
  return { path: relativePath, sha256: sha2566(content), size_bytes: content.length };
50908
51307
  }
50909
51308
  function manifestRecord(root, relativePath) {
50910
51309
  const base = fileRecord(root, relativePath);
50911
51310
  if (!base)
50912
51311
  return null;
50913
- const parsed = readJsonFile(join17(root, relativePath));
51312
+ const parsed = readJsonFile(join18(root, relativePath));
50914
51313
  if (!parsed)
50915
51314
  return { ...base, redacted: {} };
50916
51315
  const redacted = redactValue({
@@ -51005,15 +51404,15 @@ function commandEnv(env, includeValues) {
51005
51404
  function defaultSnapshotDir() {
51006
51405
  const dbPath = getDatabasePath();
51007
51406
  if (dbPath === ":memory:" || dbPath.startsWith("file::memory:"))
51008
- return join17(tmpdir3(), "hasna-todos", "environment-snapshots");
51009
- return join17(dirname9(resolve19(dbPath)), "environment-snapshots");
51407
+ return join18(tmpdir3(), "hasna-todos", "environment-snapshots");
51408
+ return join18(dirname9(resolve20(dbPath)), "environment-snapshots");
51010
51409
  }
51011
51410
  function snapshotWithId(snapshot) {
51012
51411
  const digest = sha2566(JSON.stringify(snapshot)).slice(0, 24);
51013
51412
  return { id: `env_${digest}`, ...snapshot };
51014
51413
  }
51015
51414
  function captureEnvironmentSnapshot(input = {}) {
51016
- const root = resolve19(input.root || process.cwd());
51415
+ const root = resolve20(input.root || process.cwd());
51017
51416
  const env = input.env || process.env;
51018
51417
  const warnings = [];
51019
51418
  const manifests = MANIFEST_FILES.map((file) => manifestRecord(root, file)).filter((file) => Boolean(file));
@@ -51053,13 +51452,13 @@ function captureEnvironmentSnapshot(input = {}) {
51053
51452
  });
51054
51453
  }
51055
51454
  function writeEnvironmentSnapshot(snapshot, outputPath) {
51056
- const path = outputPath ? resolve19(outputPath) : join17(defaultSnapshotDir(), `${snapshot.id}.json`);
51455
+ const path = outputPath ? resolve20(outputPath) : join18(defaultSnapshotDir(), `${snapshot.id}.json`);
51057
51456
  ensureDir2(dirname9(path));
51058
51457
  writeJsonFile(path, snapshot);
51059
51458
  return path;
51060
51459
  }
51061
51460
  function readEnvironmentSnapshot(path) {
51062
- const snapshot = readJsonFile(resolve19(path));
51461
+ const snapshot = readJsonFile(resolve20(path));
51063
51462
  if (!snapshot || snapshot.schema_version !== 1 || typeof snapshot.id !== "string") {
51064
51463
  throw new Error(`Invalid environment snapshot: ${path}`);
51065
51464
  }
@@ -51584,27 +51983,27 @@ __export(exports_serve, {
51584
51983
  SECURITY_HEADERS: () => SECURITY_HEADERS,
51585
51984
  MIME_TYPES: () => MIME_TYPES
51586
51985
  });
51587
- import { existsSync as existsSync18 } from "fs";
51588
- import { join as join18, dirname as dirname10, extname } from "path";
51986
+ import { existsSync as existsSync19 } from "fs";
51987
+ import { join as join19, dirname as dirname10, extname } from "path";
51589
51988
  import { fileURLToPath as fileURLToPath2 } from "url";
51590
51989
  function resolveDashboardDir() {
51591
51990
  const candidates = [];
51592
51991
  try {
51593
51992
  const scriptDir = dirname10(fileURLToPath2(import.meta.url));
51594
- candidates.push(join18(scriptDir, "..", "dashboard", "dist"));
51595
- candidates.push(join18(scriptDir, "..", "..", "dashboard", "dist"));
51993
+ candidates.push(join19(scriptDir, "..", "dashboard", "dist"));
51994
+ candidates.push(join19(scriptDir, "..", "..", "dashboard", "dist"));
51596
51995
  } catch {}
51597
51996
  if (process.argv[1]) {
51598
51997
  const mainDir = dirname10(process.argv[1]);
51599
- candidates.push(join18(mainDir, "..", "dashboard", "dist"));
51600
- candidates.push(join18(mainDir, "..", "..", "dashboard", "dist"));
51998
+ candidates.push(join19(mainDir, "..", "dashboard", "dist"));
51999
+ candidates.push(join19(mainDir, "..", "..", "dashboard", "dist"));
51601
52000
  }
51602
- candidates.push(join18(process.cwd(), "dashboard", "dist"));
52001
+ candidates.push(join19(process.cwd(), "dashboard", "dist"));
51603
52002
  for (const candidate of candidates) {
51604
- if (existsSync18(candidate))
52003
+ if (existsSync19(candidate))
51605
52004
  return candidate;
51606
52005
  }
51607
- return join18(process.cwd(), "dashboard", "dist");
52006
+ return join19(process.cwd(), "dashboard", "dist");
51608
52007
  }
51609
52008
  function getProvidedApiKey(req) {
51610
52009
  const headerKey = req.headers.get("x-api-key");
@@ -51654,7 +52053,7 @@ function json(data, status = 200, headers) {
51654
52053
  });
51655
52054
  }
51656
52055
  function serveStaticFile(filePath) {
51657
- if (!existsSync18(filePath))
52056
+ if (!existsSync19(filePath))
51658
52057
  return null;
51659
52058
  const ext = extname(filePath);
51660
52059
  const contentType = MIME_TYPES[ext] || "application/octet-stream";
@@ -51735,7 +52134,7 @@ data: ${data}
51735
52134
  filteredSseClients.delete(client);
51736
52135
  }
51737
52136
  const dashboardDir = resolveDashboardDir();
51738
- const dashboardExists = existsSync18(dashboardDir);
52137
+ const dashboardExists = existsSync19(dashboardDir);
51739
52138
  if (!dashboardExists) {
51740
52139
  console.error(`
51741
52140
  Dashboard not found at: ${dashboardDir}`);
@@ -53565,12 +53964,12 @@ __export(exports_config_serve_commands, {
53565
53964
  registerConfigServeCommands: () => registerConfigServeCommands
53566
53965
  });
53567
53966
  import chalk6 from "chalk";
53568
- import { existsSync as existsSync19, mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync7 } from "fs";
53569
- import { dirname as dirname11, join as join19 } from "path";
53967
+ import { existsSync as existsSync20, mkdirSync as mkdirSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
53968
+ import { dirname as dirname11, join as join20 } from "path";
53570
53969
  function registerConfigServeCommands(program2) {
53571
53970
  program2.command("config").description("View or update configuration").option("--get <key>", "Get a config value").option("--set <key=value>", "Set a config value (e.g. completion_guard.enabled=true)").action((opts) => {
53572
53971
  const globalOpts = program2.opts();
53573
- const configPath = join19(getTodosGlobalDir(), "config.json");
53972
+ const configPath = join20(getTodosGlobalDir(), "config.json");
53574
53973
  if (opts.get) {
53575
53974
  const config2 = loadConfig();
53576
53975
  const keys = opts.get.split(".");
@@ -53596,7 +53995,7 @@ function registerConfigServeCommands(program2) {
53596
53995
  }
53597
53996
  let config2 = {};
53598
53997
  try {
53599
- config2 = JSON.parse(readFileSync14(configPath, "utf-8"));
53998
+ config2 = JSON.parse(readFileSync15(configPath, "utf-8"));
53600
53999
  } catch {}
53601
54000
  const keys = key.split(".");
53602
54001
  let obj = config2;
@@ -53607,9 +54006,9 @@ function registerConfigServeCommands(program2) {
53607
54006
  }
53608
54007
  obj[keys[keys.length - 1]] = parsedValue;
53609
54008
  const dir = dirname11(configPath);
53610
- if (!existsSync19(dir))
53611
- mkdirSync9(dir, { recursive: true });
53612
- writeFileSync7(configPath, JSON.stringify(config2, null, 2));
54009
+ if (!existsSync20(dir))
54010
+ mkdirSync10(dir, { recursive: true });
54011
+ writeFileSync8(configPath, JSON.stringify(config2, null, 2));
53613
54012
  if (globalOpts.json) {
53614
54013
  output({ key, value: parsedValue }, true);
53615
54014
  } else {
@@ -53736,7 +54135,7 @@ function registerConfigServeCommands(program2) {
53736
54135
  redaction.command("scan [text]").description("Scan text or a file for secret-like values without printing values").option("--file <path>", "File to scan").action(async (text2, opts) => {
53737
54136
  const globalOpts = program2.opts();
53738
54137
  const { listSecretFindings: listSecretFindings2 } = await Promise.resolve().then(() => (init_redaction(), exports_redaction));
53739
- const value = opts.file ? readFileSync14(opts.file, "utf-8") : text2 || "";
54138
+ const value = opts.file ? readFileSync15(opts.file, "utf-8") : text2 || "";
53740
54139
  const findings = listSecretFindings2(value);
53741
54140
  if (globalOpts.json) {
53742
54141
  output({ ok: findings.length === 0, findings }, true);
@@ -55122,7 +55521,7 @@ __export(exports_query_commands, {
55122
55521
  registerQueryCommands: () => registerQueryCommands
55123
55522
  });
55124
55523
  import chalk7 from "chalk";
55125
- import { readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "fs";
55524
+ import { readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "fs";
55126
55525
  function parseJsonObjectOption2(value, label) {
55127
55526
  if (!value)
55128
55527
  return;
@@ -55781,9 +56180,9 @@ Repairs`));
55781
56180
  const db = getDatabase();
55782
56181
  const row = db.query("SELECT COUNT(*) as count FROM tasks").get();
55783
56182
  const { statSync: statSync9 } = await import("fs");
55784
- const { join: join20 } = await import("path");
56183
+ const { join: join21 } = await import("path");
55785
56184
  const home = process.env["HOME"] || process.env["USERPROFILE"] || "~";
55786
- const dbPath = process.env["HASNA_TODOS_DB_PATH"] || process.env["TODOS_DB_PATH"] || join20(home, ".hasna", "todos", "todos.db");
56185
+ const dbPath = process.env["HASNA_TODOS_DB_PATH"] || process.env["TODOS_DB_PATH"] || join21(home, ".hasna", "todos", "todos.db");
55787
56186
  let size = "unknown";
55788
56187
  try {
55789
56188
  size = `${(statSync9(dbPath).size / 1024 / 1024).toFixed(1)} MB`;
@@ -56400,7 +56799,7 @@ Repairs`));
56400
56799
  const sessionId = opts.session || globalOpts.session || undefined;
56401
56800
  try {
56402
56801
  if (opts.import) {
56403
- const bundle = JSON.parse(readFileSync15(opts.import, "utf-8"));
56802
+ const bundle = JSON.parse(readFileSync16(opts.import, "utf-8"));
56404
56803
  const result = importHandoffBundle(bundle, { apply: opts.apply }, db);
56405
56804
  if (opts.json || globalOpts.json) {
56406
56805
  console.log(JSON.stringify(result));
@@ -56416,7 +56815,7 @@ Repairs`));
56416
56815
  const bundle = exportHandoffBundle(opts.export, db);
56417
56816
  const json2 = JSON.stringify(bundle, null, 2);
56418
56817
  if (opts.output) {
56419
- writeFileSync8(opts.output, `${json2}
56818
+ writeFileSync9(opts.output, `${json2}
56420
56819
  `);
56421
56820
  if (opts.json || globalOpts.json) {
56422
56821
  console.log(JSON.stringify({ path: opts.output, handoff_id: bundle.handoff.id }));
@@ -56647,7 +57046,7 @@ Repairs`));
56647
57046
  });
56648
57047
  const content = format === "json" ? JSON.stringify(document, null, 2) : renderReleaseNotesMarkdown2(document);
56649
57048
  if (opts.out) {
56650
- writeFileSync8(opts.out, content);
57049
+ writeFileSync9(opts.out, content);
56651
57050
  if (format !== "json")
56652
57051
  console.log(chalk7.green(`Wrote release notes to ${opts.out}`));
56653
57052
  return;
@@ -56762,7 +57161,7 @@ Repairs`));
56762
57161
  redact: Boolean(opts.redact)
56763
57162
  });
56764
57163
  if (opts.out) {
56765
- writeFileSync8(opts.out, exported.content);
57164
+ writeFileSync9(opts.out, exported.content);
56766
57165
  if (!(opts.json || globalOpts.json))
56767
57166
  console.log(chalk7.green(`Wrote ${exported.events.length} events to ${opts.out}`));
56768
57167
  }
@@ -56778,7 +57177,7 @@ Repairs`));
56778
57177
  });
56779
57178
  calendar.command("import <path>").description("Import VEVENT entries from an ICS file as local imported calendar items").option("-j, --json", "Output JSON").action((path, opts) => {
56780
57179
  try {
56781
- const result = importCalendarIcs(readFileSync15(path, "utf-8"));
57180
+ const result = importCalendarIcs(readFileSync16(path, "utf-8"));
56782
57181
  if (opts.json || program2.opts().json) {
56783
57182
  output(result, true);
56784
57183
  return;
@@ -56917,7 +57316,7 @@ Repairs`));
56917
57316
  const bundle = exportTaskBoardBundle(boardId);
56918
57317
  const json2 = JSON.stringify(bundle, null, 2);
56919
57318
  if (opts.out) {
56920
- writeFileSync8(opts.out, json2);
57319
+ writeFileSync9(opts.out, json2);
56921
57320
  if (!(opts.json || program2.opts().json))
56922
57321
  console.log(chalk7.green(`Wrote ${bundle.boards.length} board(s) to ${opts.out}`));
56923
57322
  }
@@ -56929,7 +57328,7 @@ Repairs`));
56929
57328
  });
56930
57329
  board.command("import <path>").description("Import local board definitions from a JSON bundle").option("-j, --json", "Output JSON").action((path, opts) => {
56931
57330
  try {
56932
- const bundle = JSON.parse(readFileSync15(path, "utf-8"));
57331
+ const bundle = JSON.parse(readFileSync16(path, "utf-8"));
56933
57332
  const result = importTaskBoardBundle(bundle);
56934
57333
  if (opts.json || program2.opts().json) {
56935
57334
  output(result, true);
@@ -57305,7 +57704,7 @@ Repairs`));
57305
57704
  const { importExternalIssues: importExternalIssues2 } = await Promise.resolve().then(() => (init_external_issue_importers(), exports_external_issue_importers));
57306
57705
  let body = text2 || "";
57307
57706
  if (opts.file)
57308
- body = readFileSync15(opts.file, "utf-8");
57707
+ body = readFileSync16(opts.file, "utf-8");
57309
57708
  if (!body && !opts.url && !process.stdin.isTTY)
57310
57709
  body = await Bun.stdin.text();
57311
57710
  if (!body.trim() && !opts.url) {
@@ -57363,7 +57762,7 @@ Repairs`));
57363
57762
  } = await Promise.resolve().then(() => (init_tester_issue_reports(), exports_tester_issue_reports));
57364
57763
  let body = jsonText || "";
57365
57764
  if (opts.file)
57366
- body = readFileSync15(opts.file, "utf-8");
57765
+ body = readFileSync16(opts.file, "utf-8");
57367
57766
  if (!body && !process.stdin.isTTY)
57368
57767
  body = await Bun.stdin.text();
57369
57768
  if (!body.trim()) {
@@ -57406,11 +57805,11 @@ Repairs`));
57406
57805
  const inbox = program2.command("inbox").description("Capture local inbox items from pasted errors, CI logs, git context, files, or GitHub issue URLs");
57407
57806
  inbox.command("add [text]").description("Create a local inbox item and linked task from text, stdin, or a file").option("--file <path>", "Read captured context from a file").option("--source-type <type>", "pasted_error, ci_log, git_context, github_issue, file, or other").option("--source-name <name>", "Human-readable source name").option("--source-url <url>", "Source URL, including GitHub issue URLs").option("--title <title>", "Task/inbox title").option("--priority <priority>", "Task priority").option("--tags <tags>", "Comma-separated extra tags").option("--metadata <json>", "Additional JSON metadata").option("--no-task", "Only store inbox item; do not create a linked task").option("-j, --json", "Output as JSON").action(async (text2, opts) => {
57408
57807
  const globalOpts = program2.opts();
57409
- const { readFileSync: readFileSync16 } = await import("fs");
57808
+ const { readFileSync: readFileSync17 } = await import("fs");
57410
57809
  const { createInboxItem: createInboxItem2 } = await Promise.resolve().then(() => (init_inbox(), exports_inbox));
57411
57810
  let body = text2 || "";
57412
57811
  if (opts.file)
57413
- body = readFileSync16(opts.file, "utf-8");
57812
+ body = readFileSync17(opts.file, "utf-8");
57414
57813
  if (!body && !process.stdin.isTTY)
57415
57814
  body = await Bun.stdin.text();
57416
57815
  if (!body.trim()) {
@@ -57471,11 +57870,11 @@ ${diff}` : null].filter(Boolean).join(`
57471
57870
  });
57472
57871
  inbox.command("parse [text]").description("Preview or apply deterministic local natural-language task intake").option("--file <path>", "Read natural-language input from a file").option("--priority <priority>", "Default priority for parsed tasks", "medium").option("--project <id>", "Project ID for applied tasks").option("--list <id>", "Task list ID for applied tasks").option("--reference-date <iso>", "Reference date for due today/tomorrow/next week").option("--apply", "Create parsed tasks; default is dry-run preview").option("-j, --json", "Output as JSON").action(async (text2, opts) => {
57473
57872
  const globalOpts = program2.opts();
57474
- const { readFileSync: readFileSync16 } = await import("fs");
57873
+ const { readFileSync: readFileSync17 } = await import("fs");
57475
57874
  const { previewNaturalLanguageIntake: previewNaturalLanguageIntake2 } = await Promise.resolve().then(() => (init_natural_language_intake(), exports_natural_language_intake));
57476
57875
  let body = text2 || "";
57477
57876
  if (opts.file)
57478
- body = readFileSync16(opts.file, "utf-8");
57877
+ body = readFileSync17(opts.file, "utf-8");
57479
57878
  if (!body && !process.stdin.isTTY)
57480
57879
  body = await Bun.stdin.text();
57481
57880
  if (!body.trim()) {
@@ -57599,45 +57998,45 @@ __export(exports_mcp_hooks_commands, {
57599
57998
  });
57600
57999
  import chalk8 from "chalk";
57601
58000
  import { execSync as execSync3 } from "child_process";
57602
- import { existsSync as existsSync20, readFileSync as readFileSync16, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10, chmodSync as chmodSync2 } from "fs";
57603
- import { dirname as dirname12, join as join20 } from "path";
58001
+ import { existsSync as existsSync21, readFileSync as readFileSync17, writeFileSync as writeFileSync10, mkdirSync as mkdirSync11, chmodSync as chmodSync2 } from "fs";
58002
+ import { dirname as dirname12, join as join21 } from "path";
57604
58003
  function getMcpBinaryPath() {
57605
58004
  try {
57606
58005
  const p = execSync3("which todos-mcp", { encoding: "utf-8" }).trim();
57607
58006
  if (p)
57608
58007
  return p;
57609
58008
  } catch {}
57610
- const bunBin = join20(HOME2, ".bun", "bin", "todos-mcp");
57611
- if (existsSync20(bunBin))
58009
+ const bunBin = join21(HOME2, ".bun", "bin", "todos-mcp");
58010
+ if (existsSync21(bunBin))
57612
58011
  return bunBin;
57613
58012
  return "todos-mcp";
57614
58013
  }
57615
58014
  function readJsonFile2(path) {
57616
- if (!existsSync20(path))
58015
+ if (!existsSync21(path))
57617
58016
  return {};
57618
58017
  try {
57619
- return JSON.parse(readFileSync16(path, "utf-8"));
58018
+ return JSON.parse(readFileSync17(path, "utf-8"));
57620
58019
  } catch {
57621
58020
  return {};
57622
58021
  }
57623
58022
  }
57624
58023
  function writeJsonFile2(path, data) {
57625
58024
  const dir = dirname12(path);
57626
- if (!existsSync20(dir))
57627
- mkdirSync10(dir, { recursive: true });
57628
- writeFileSync9(path, JSON.stringify(data, null, 2) + `
58025
+ if (!existsSync21(dir))
58026
+ mkdirSync11(dir, { recursive: true });
58027
+ writeFileSync10(path, JSON.stringify(data, null, 2) + `
57629
58028
  `);
57630
58029
  }
57631
58030
  function readTomlFile(path) {
57632
- if (!existsSync20(path))
58031
+ if (!existsSync21(path))
57633
58032
  return "";
57634
- return readFileSync16(path, "utf-8");
58033
+ return readFileSync17(path, "utf-8");
57635
58034
  }
57636
58035
  function writeTomlFile(path, content) {
57637
58036
  const dir = dirname12(path);
57638
- if (!existsSync20(dir))
57639
- mkdirSync10(dir, { recursive: true });
57640
- writeFileSync9(path, content);
58037
+ if (!existsSync21(dir))
58038
+ mkdirSync11(dir, { recursive: true });
58039
+ writeFileSync10(path, content);
57641
58040
  }
57642
58041
  function removeTomlBlock(content, blockName) {
57643
58042
  const lines = content.split(`
@@ -57701,7 +58100,7 @@ function unregisterClaude(_global) {
57701
58100
  }
57702
58101
  }
57703
58102
  function registerCodex(binPath) {
57704
- const configPath = join20(HOME2, ".codex", "config.toml");
58103
+ const configPath = join21(HOME2, ".codex", "config.toml");
57705
58104
  let content = readTomlFile(configPath);
57706
58105
  content = removeTomlBlock(content, "mcp_servers.todos");
57707
58106
  const block = `
@@ -57715,7 +58114,7 @@ args = []
57715
58114
  console.log(chalk8.green(`Codex CLI: registered in ${configPath}`));
57716
58115
  }
57717
58116
  function unregisterCodex() {
57718
- const configPath = join20(HOME2, ".codex", "config.toml");
58117
+ const configPath = join21(HOME2, ".codex", "config.toml");
57719
58118
  let content = readTomlFile(configPath);
57720
58119
  if (!content.includes("[mcp_servers.todos]")) {
57721
58120
  console.log(chalk8.dim(`Codex CLI: todos not found in ${configPath}`));
@@ -57727,7 +58126,7 @@ function unregisterCodex() {
57727
58126
  console.log(chalk8.green(`Codex CLI: unregistered from ${configPath}`));
57728
58127
  }
57729
58128
  function registerGemini(binPath) {
57730
- const configPath = join20(HOME2, ".gemini", "settings.json");
58129
+ const configPath = join21(HOME2, ".gemini", "settings.json");
57731
58130
  const config = readJsonFile2(configPath);
57732
58131
  if (!config["mcpServers"]) {
57733
58132
  config["mcpServers"] = {};
@@ -57741,7 +58140,7 @@ function registerGemini(binPath) {
57741
58140
  console.log(chalk8.green(`Gemini CLI: registered in ${configPath}`));
57742
58141
  }
57743
58142
  function unregisterGemini() {
57744
- const configPath = join20(HOME2, ".gemini", "settings.json");
58143
+ const configPath = join21(HOME2, ".gemini", "settings.json");
57745
58144
  const config = readJsonFile2(configPath);
57746
58145
  const servers = config["mcpServers"];
57747
58146
  if (!servers || !("todos" in servers)) {
@@ -57798,9 +58197,9 @@ function registerMcpHooksCommands(program2) {
57798
58197
  if (p)
57799
58198
  todosBin = p;
57800
58199
  } catch {}
57801
- const hooksDir = join20(process.cwd(), ".claude", "hooks");
57802
- if (!existsSync20(hooksDir))
57803
- mkdirSync10(hooksDir, { recursive: true });
58200
+ const hooksDir = join21(process.cwd(), ".claude", "hooks");
58201
+ if (!existsSync21(hooksDir))
58202
+ mkdirSync11(hooksDir, { recursive: true });
57804
58203
  const hookScript = `#!/usr/bin/env bash
57805
58204
  # Auto-generated by: todos hooks install
57806
58205
  # Syncs todos with Claude Code task list on tool use events.
@@ -57824,11 +58223,11 @@ esac
57824
58223
 
57825
58224
  exit 0
57826
58225
  `;
57827
- const hookPath = join20(hooksDir, "todos-sync.sh");
57828
- writeFileSync9(hookPath, hookScript);
58226
+ const hookPath = join21(hooksDir, "todos-sync.sh");
58227
+ writeFileSync10(hookPath, hookScript);
57829
58228
  execSync3(`chmod +x "${hookPath}"`);
57830
58229
  console.log(chalk8.green(`Hook script created: ${hookPath}`));
57831
- const settingsPath = join20(process.cwd(), ".claude", "settings.json");
58230
+ const settingsPath = join21(process.cwd(), ".claude", "settings.json");
57832
58231
  const settings = readJsonFile2(settingsPath);
57833
58232
  if (!settings["hooks"]) {
57834
58233
  settings["hooks"] = {};
@@ -58697,18 +59096,18 @@ Artifacts:`));
58697
59096
  const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
58698
59097
  const hookPath = `${gitDir}/hooks/post-commit`;
58699
59098
  const marker = "# todos-auto-link";
58700
- if (existsSync20(hookPath)) {
58701
- const existing = readFileSync16(hookPath, "utf-8");
59099
+ if (existsSync21(hookPath)) {
59100
+ const existing = readFileSync17(hookPath, "utf-8");
58702
59101
  if (existing.includes(marker)) {
58703
59102
  console.log(chalk8.yellow("Hook already installed."));
58704
59103
  return;
58705
59104
  }
58706
- writeFileSync9(hookPath, existing + `
59105
+ writeFileSync10(hookPath, existing + `
58707
59106
  ${marker}
58708
59107
  $(dirname "$0")/../../scripts/post-commit-hook.sh
58709
59108
  `);
58710
59109
  } else {
58711
- writeFileSync9(hookPath, `#!/usr/bin/env bash
59110
+ writeFileSync10(hookPath, `#!/usr/bin/env bash
58712
59111
  ${marker}
58713
59112
  $(dirname "$0")/../../scripts/post-commit-hook.sh
58714
59113
  `);
@@ -58725,11 +59124,11 @@ $(dirname "$0")/../../scripts/post-commit-hook.sh
58725
59124
  const gitDir = execSync3("git rev-parse --git-dir", { encoding: "utf-8" }).trim();
58726
59125
  const hookPath = `${gitDir}/hooks/post-commit`;
58727
59126
  const marker = "# todos-auto-link";
58728
- if (!existsSync20(hookPath)) {
59127
+ if (!existsSync21(hookPath)) {
58729
59128
  console.log(chalk8.dim("No post-commit hook found."));
58730
59129
  return;
58731
59130
  }
58732
- const content = readFileSync16(hookPath, "utf-8");
59131
+ const content = readFileSync17(hookPath, "utf-8");
58733
59132
  if (!content.includes(marker)) {
58734
59133
  console.log(chalk8.dim("Hook not managed by todos."));
58735
59134
  return;
@@ -58740,7 +59139,7 @@ $(dirname "$0")/../../scripts/post-commit-hook.sh
58740
59139
  if (cleaned === "#!/usr/bin/env bash" || cleaned === "") {
58741
59140
  (await import("fs")).unlinkSync(hookPath);
58742
59141
  } else {
58743
- writeFileSync9(hookPath, cleaned + `
59142
+ writeFileSync10(hookPath, cleaned + `
58744
59143
  `);
58745
59144
  }
58746
59145
  console.log(chalk8.green("Post-commit hook removed."));
@@ -58909,9 +59308,9 @@ __export(exports_machines, {
58909
59308
  });
58910
59309
  import chalk10 from "chalk";
58911
59310
  import { execSync as execSync4 } from "child_process";
58912
- import { readFileSync as readFileSync17, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "fs";
59311
+ import { readFileSync as readFileSync18, unlinkSync as unlinkSync2, writeFileSync as writeFileSync11 } from "fs";
58913
59312
  import { tmpdir as tmpdir4 } from "os";
58914
- import { join as join21 } from "path";
59313
+ import { join as join22 } from "path";
58915
59314
  function getOrCreateLocalMachineName() {
58916
59315
  return process.env["TODOS_MACHINE_NAME"] || __require("os").hostname() || "unknown";
58917
59316
  }
@@ -58949,11 +59348,11 @@ function remoteTempPath(sshAddress) {
58949
59348
  }
58950
59349
  function readRemoteBridgeBundle(sshAddress) {
58951
59350
  const remotePath = remoteTempPath(sshAddress);
58952
- const localPath = join21(tmpdir4(), `todos-bridge-pull-${uuid()}.json`);
59351
+ const localPath = join22(tmpdir4(), `todos-bridge-pull-${uuid()}.json`);
58953
59352
  try {
58954
59353
  runSsh(sshAddress, `todos export --format bridge --allow-plaintext-sensitive --output ${shellQuote(remotePath)}`, 120000);
58955
59354
  scpFromRemote(sshAddress, remotePath, localPath);
58956
- return JSON.parse(readFileSync17(localPath, "utf-8"));
59355
+ return JSON.parse(readFileSync18(localPath, "utf-8"));
58957
59356
  } finally {
58958
59357
  try {
58959
59358
  runSsh(sshAddress, `rm -f ${shellQuote(remotePath)}`, 1e4);
@@ -58964,8 +59363,8 @@ function readRemoteBridgeBundle(sshAddress) {
58964
59363
  }
58965
59364
  }
58966
59365
  function writeLocalBridgeBundle() {
58967
- const localPath = join21(tmpdir4(), `todos-bridge-push-${uuid()}.json`);
58968
- writeFileSync10(localPath, JSON.stringify(createLocalBridgeBundle(), null, 2));
59366
+ const localPath = join22(tmpdir4(), `todos-bridge-push-${uuid()}.json`);
59367
+ writeFileSync11(localPath, JSON.stringify(createLocalBridgeBundle(), null, 2));
58969
59368
  return localPath;
58970
59369
  }
58971
59370
  function pushLocalBridgeBundle(sshAddress, dryRun) {
@@ -59403,7 +59802,7 @@ __export(exports_environment_snapshots2, {
59403
59802
  registerEnvironmentSnapshotCommands: () => registerEnvironmentSnapshotCommands
59404
59803
  });
59405
59804
  import chalk12 from "chalk";
59406
- function printJson(value) {
59805
+ function printJson2(value) {
59407
59806
  console.log(JSON.stringify(value, null, 2));
59408
59807
  }
59409
59808
  function registerEnvironmentSnapshotCommands(program2) {
@@ -59421,7 +59820,7 @@ function registerEnvironmentSnapshotCommands(program2) {
59421
59820
  include_env_values: Boolean(opts.includeEnvValues)
59422
59821
  });
59423
59822
  if (globalOpts.json) {
59424
- printJson(result);
59823
+ printJson2(result);
59425
59824
  return;
59426
59825
  }
59427
59826
  console.log(chalk12.green("Captured") + ` ${result.snapshot.id}`);
@@ -59438,7 +59837,7 @@ function registerEnvironmentSnapshotCommands(program2) {
59438
59837
  } catch (error) {
59439
59838
  const message = error instanceof Error ? error.message : String(error);
59440
59839
  if (globalOpts.json)
59441
- printJson({ error: message });
59840
+ printJson2({ error: message });
59442
59841
  else
59443
59842
  console.error(chalk12.red(`Error: ${message}`));
59444
59843
  process.exit(1);
@@ -59449,7 +59848,7 @@ function registerEnvironmentSnapshotCommands(program2) {
59449
59848
  try {
59450
59849
  const comparison = compareEnvironmentSnapshotFiles(left, right);
59451
59850
  if (globalOpts.json) {
59452
- printJson(comparison);
59851
+ printJson2(comparison);
59453
59852
  return;
59454
59853
  }
59455
59854
  console.log(`left: ${comparison.left_id}`);
@@ -59464,7 +59863,7 @@ function registerEnvironmentSnapshotCommands(program2) {
59464
59863
  } catch (error) {
59465
59864
  const message = error instanceof Error ? error.message : String(error);
59466
59865
  if (globalOpts.json)
59467
- printJson({ error: message });
59866
+ printJson2({ error: message });
59468
59867
  else
59469
59868
  console.error(chalk12.red(`Error: ${message}`));
59470
59869
  process.exit(1);
@@ -60028,7 +60427,7 @@ __export(exports_onboarding_commands, {
60028
60427
  registerOnboardingCommands: () => registerOnboardingCommands
60029
60428
  });
60030
60429
  import chalk17 from "chalk";
60031
- import { resolve as resolve20 } from "path";
60430
+ import { resolve as resolve21 } from "path";
60032
60431
  function registerOnboardingCommands(program2) {
60033
60432
  program2.command("onboarding").alias("demo-fixtures").description("List, show, write, or import bundled local onboarding fixtures").option("--show <name>", "Show one fixture bridge bundle as JSON").option("--write <dir>", "Write all bundled fixture bridge bundles to a directory").option("--import <name>", "Dry-run or apply an onboarding fixture import").option("--apply", "Apply an onboarding fixture import. Defaults to dry-run.").option("--resolve-conflicts", "Safely merge existing local tasks while preserving divergent fields").action(async (opts) => {
60034
60433
  const globalOpts = program2.opts();
@@ -60044,7 +60443,7 @@ function registerOnboardingCommands(program2) {
60044
60443
  return;
60045
60444
  }
60046
60445
  if (opts.write) {
60047
- const result = writeOnboardingFixtureFiles2(resolve20(opts.write));
60446
+ const result = writeOnboardingFixtureFiles2(resolve21(opts.write));
60048
60447
  if (globalOpts.json) {
60049
60448
  output(result, true);
60050
60449
  return;
@@ -63492,8 +63891,8 @@ __export(exports_sdk_integration_fixtures, {
63492
63891
  TODOS_SDK_INTEGRATION_FIXTURE_SCHEMA_VERSION: () => TODOS_SDK_INTEGRATION_FIXTURE_SCHEMA_VERSION,
63493
63892
  TODOS_SDK_INTEGRATION_FIXTURE_GENERATED_AT: () => TODOS_SDK_INTEGRATION_FIXTURE_GENERATED_AT
63494
63893
  });
63495
- import { mkdirSync as mkdirSync11, writeFileSync as writeFileSync11 } from "fs";
63496
- import { join as join22 } from "path";
63894
+ import { mkdirSync as mkdirSync12, writeFileSync as writeFileSync12 } from "fs";
63895
+ import { join as join23 } from "path";
63497
63896
  function source5(version) {
63498
63897
  return {
63499
63898
  packageName: "@hasna/todos",
@@ -63589,7 +63988,7 @@ function createSdkIntegrationFixturePack(options = {}) {
63589
63988
  };
63590
63989
  }
63591
63990
  function writeSdkIntegrationFixtures(directory, options = {}) {
63592
- mkdirSync11(directory, { recursive: true });
63991
+ mkdirSync12(directory, { recursive: true });
63593
63992
  const pack = createSdkIntegrationFixturePack(options);
63594
63993
  const bundle = getOnboardingFixtureBundle("agent-project-demo");
63595
63994
  const files = [
@@ -63600,8 +63999,8 @@ function writeSdkIntegrationFixtures(directory, options = {}) {
63600
63999
  ];
63601
64000
  const written = [];
63602
64001
  for (const [name, payload] of files) {
63603
- const file = join22(directory, name);
63604
- writeFileSync11(file, `${JSON.stringify(payload, null, 2)}
64002
+ const file = join23(directory, name);
64003
+ writeFileSync12(file, `${JSON.stringify(payload, null, 2)}
63605
64004
  `, "utf-8");
63606
64005
  written.push(file);
63607
64006
  }
@@ -63624,7 +64023,7 @@ __export(exports_sdk_fixture_commands, {
63624
64023
  registerSdkFixtureCommands: () => registerSdkFixtureCommands
63625
64024
  });
63626
64025
  import chalk19 from "chalk";
63627
- import { resolve as resolve21 } from "path";
64026
+ import { resolve as resolve22 } from "path";
63628
64027
  function registerSdkFixtureCommands(program2) {
63629
64028
  program2.command("sdk-fixtures").description("List, show, or write local SDK integration fixtures").option("--show", "Print the full fixture pack JSON").option("--write <dir>", "Write fixture pack, bridge fixture, contract snapshots, and example index to a directory").action(async (opts) => {
63630
64029
  const globalOpts = program2.opts();
@@ -63635,7 +64034,7 @@ function registerSdkFixtureCommands(program2) {
63635
64034
  writeSdkIntegrationFixtures: writeSdkIntegrationFixtures2
63636
64035
  } = await Promise.resolve().then(() => (init_sdk_integration_fixtures(), exports_sdk_integration_fixtures));
63637
64036
  if (opts.write) {
63638
- const result = writeSdkIntegrationFixtures2(resolve21(opts.write));
64037
+ const result = writeSdkIntegrationFixtures2(resolve22(opts.write));
63639
64038
  if (globalOpts.json) {
63640
64039
  console.log(JSON.stringify(result));
63641
64040
  return;
@@ -63865,7 +64264,7 @@ var exports_roadmap_commands = {};
63865
64264
  __export(exports_roadmap_commands, {
63866
64265
  registerRoadmapCommands: () => registerRoadmapCommands
63867
64266
  });
63868
- import { readFileSync as readFileSync18, writeFileSync as writeFileSync12 } from "fs";
64267
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync13 } from "fs";
63869
64268
  import chalk21 from "chalk";
63870
64269
  function splitList3(value) {
63871
64270
  return value?.split(";").flatMap((part) => part.split(",")).map((item) => item.trim()).filter(Boolean);
@@ -64076,7 +64475,7 @@ function registerRoadmapCommands(program2) {
64076
64475
  const { exportRoadmapBundle: exportRoadmapBundle2, renderRoadmapMarkdown: renderRoadmapMarkdown2 } = await Promise.resolve().then(() => (init_roadmaps(), exports_roadmaps));
64077
64476
  const content = opts.format === "markdown" ? renderRoadmapMarkdown2(roadmap) : JSON.stringify(exportRoadmapBundle2(roadmap), null, 2);
64078
64477
  if (opts.out) {
64079
- writeFileSync12(opts.out, content);
64478
+ writeFileSync13(opts.out, content);
64080
64479
  if (!globalOpts.json)
64081
64480
  console.log(chalk21.green(`Wrote roadmap export to ${opts.out}`));
64082
64481
  }
@@ -64094,7 +64493,7 @@ function registerRoadmapCommands(program2) {
64094
64493
  const globalOpts = globalOptions(program2);
64095
64494
  try {
64096
64495
  const { importRoadmapBundle: importRoadmapBundle2 } = await Promise.resolve().then(() => (init_roadmaps(), exports_roadmaps));
64097
- const bundle = JSON.parse(readFileSync18(path, "utf8"));
64496
+ const bundle = JSON.parse(readFileSync19(path, "utf8"));
64098
64497
  const result = importRoadmapBundle2(bundle, { apply: Boolean(opts.apply) });
64099
64498
  if (globalOpts.json) {
64100
64499
  output(result, true);
@@ -64457,7 +64856,7 @@ __export(exports_local_backup_commands, {
64457
64856
  registerLocalBackupCommands: () => registerLocalBackupCommands
64458
64857
  });
64459
64858
  import chalk26 from "chalk";
64460
- import { resolve as resolve22 } from "path";
64859
+ import { resolve as resolve23 } from "path";
64461
64860
  function globalOptions6(program2) {
64462
64861
  const command = program2;
64463
64862
  return command.optsWithGlobals?.() ?? program2.opts();
@@ -64479,10 +64878,10 @@ function registerLocalBackupCommands(program2) {
64479
64878
  const projectId = opts.projectId ?? autoProject(globalOpts);
64480
64879
  const backupBundle = createLocalBackup2({
64481
64880
  project_id: projectId,
64482
- output_path: opts.output ? resolve22(opts.output) : undefined
64881
+ output_path: opts.output ? resolve23(opts.output) : undefined
64483
64882
  });
64484
64883
  const result = {
64485
- output_path: opts.output ? resolve22(opts.output) : null,
64884
+ output_path: opts.output ? resolve23(opts.output) : null,
64486
64885
  backup: backupBundle
64487
64886
  };
64488
64887
  if (opts.json || globalOpts.json) {