@cleocode/cleo 2026.2.8 → 2026.3.0

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.
Files changed (62) hide show
  1. package/README.md +14 -13
  2. package/dist/cli/index.js +22676 -26012
  3. package/dist/cli/index.js.map +4 -4
  4. package/dist/mcp/index.js +1651 -802
  5. package/dist/mcp/index.js.map +4 -4
  6. package/drizzle/20260301053344_careless_changeling/migration.sql +27 -0
  7. package/drizzle/20260301053344_careless_changeling/snapshot.json +2598 -0
  8. package/package.json +3 -4
  9. package/packages/ct-skills/skills/ct-cleo/SKILL.md +49 -22
  10. package/templates/CLEO-INJECTION.md +32 -138
  11. package/templates/cleo-gitignore +66 -49
  12. package/templates/git-hooks/pre-commit +24 -6
  13. package/schemas/archive/agent-configs.schema.json +0 -120
  14. package/schemas/archive/agent-registry.schema.json +0 -132
  15. package/schemas/archive/archive.schema.json +0 -450
  16. package/schemas/archive/claudedocs-frontmatter.schema.json +0 -162
  17. package/schemas/archive/commands-index.schema.json +0 -158
  18. package/schemas/archive/contribution.schema.json +0 -722
  19. package/schemas/archive/critical-path.schema.json +0 -246
  20. package/schemas/archive/deps-cache.schema.json +0 -97
  21. package/schemas/archive/doctor-output.schema.json +0 -283
  22. package/schemas/archive/error.schema.json +0 -161
  23. package/schemas/archive/export-package.schema.json +0 -375
  24. package/schemas/archive/global-config.schema.json +0 -219
  25. package/schemas/archive/log.schema.json +0 -250
  26. package/schemas/archive/metrics.schema.json +0 -328
  27. package/schemas/archive/migrations.schema.json +0 -150
  28. package/schemas/archive/nexus-registry.schema.json +0 -90
  29. package/schemas/archive/output.schema.json +0 -164
  30. package/schemas/archive/rcsd-consensus-report.schema.json +0 -491
  31. package/schemas/archive/rcsd-hitl-resolution.schema.json +0 -216
  32. package/schemas/archive/rcsd-index.schema.json +0 -384
  33. package/schemas/archive/rcsd-manifest.schema.json +0 -264
  34. package/schemas/archive/rcsd-research-output.schema.json +0 -564
  35. package/schemas/archive/rcsd-spec-frontmatter.schema.json +0 -225
  36. package/schemas/archive/releases.schema.json +0 -267
  37. package/schemas/archive/skills-manifest.schema.json +0 -91
  38. package/schemas/archive/skillsmp.schema.json +0 -208
  39. package/schemas/archive/spec-index.schema.json +0 -196
  40. package/schemas/archive/todo.schema.json +0 -995
  41. package/schemas/claudedocs-frontmatter.schema.json +0 -162
  42. package/schemas/commands-index.schema.json +0 -158
  43. package/schemas/rcsd-consensus-report.schema.json +0 -494
  44. package/schemas/rcsd-hitl-resolution.schema.json +0 -219
  45. package/schemas/rcsd-index.schema.json +0 -387
  46. package/schemas/rcsd-manifest.schema.json +0 -267
  47. package/schemas/rcsd-research-output.schema.json +0 -567
  48. package/schemas/rcsd-spec-frontmatter.schema.json +0 -225
  49. package/schemas/todo.schema.json +0 -994
  50. package/skills/_shared/cleo-style-guide.md +0 -84
  51. package/skills/_shared/manifest-operations.md +0 -810
  52. package/skills/_shared/placeholders.json +0 -433
  53. package/skills/_shared/skill-chaining-patterns.md +0 -240
  54. package/skills/_shared/subagent-protocol-base.md +0 -221
  55. package/skills/_shared/task-system-integration.md +0 -232
  56. package/skills/_shared/testing-framework-config.md +0 -110
  57. package/skills/agentskills-integrate.md +0 -104
  58. package/skills/agentskills-specs.md +0 -255
  59. package/skills/agentskills-what-are-skills.md +0 -75
  60. package/skills/manifest.json +0 -510
  61. package/templates/AGENT-INJECTION.md +0 -166
  62. /package/schemas/{research-manifest.schema.json → archive/research-manifest.schema.json} +0 -0
package/dist/mcp/index.js CHANGED
@@ -501,6 +501,7 @@ __export(schema_exports, {
501
501
  lifecyclePipelines: () => lifecyclePipelines,
502
502
  lifecycleStages: () => lifecycleStages,
503
503
  lifecycleTransitions: () => lifecycleTransitions,
504
+ manifestEntries: () => manifestEntries,
504
505
  schemaMeta: () => schemaMeta,
505
506
  sessions: () => sessions,
506
507
  statusRegistryTable: () => statusRegistryTable,
@@ -517,7 +518,7 @@ import {
517
518
  primaryKey
518
519
  } from "drizzle-orm/sqlite-core";
519
520
  import { sql } from "drizzle-orm";
520
- var TASK_PRIORITIES, TASK_TYPES, TASK_SIZES, LIFECYCLE_STAGE_NAMES, LIFECYCLE_GATE_RESULTS, LIFECYCLE_EVIDENCE_TYPES, tasks, taskDependencies, taskRelations, sessions, taskWorkHistory, lifecyclePipelines, lifecycleStages, lifecycleGateResults, lifecycleEvidence, lifecycleTransitions, schemaMeta, auditLog, architectureDecisions, adrTaskLinks, adrRelations, statusRegistryTable;
521
+ var TASK_PRIORITIES, TASK_TYPES, TASK_SIZES, LIFECYCLE_STAGE_NAMES, LIFECYCLE_GATE_RESULTS, LIFECYCLE_EVIDENCE_TYPES, tasks, taskDependencies, taskRelations, sessions, taskWorkHistory, lifecyclePipelines, lifecycleStages, lifecycleGateResults, lifecycleEvidence, lifecycleTransitions, manifestEntries, schemaMeta, auditLog, architectureDecisions, adrTaskLinks, adrRelations, statusRegistryTable;
521
522
  var init_schema = __esm({
522
523
  "src/store/schema.ts"() {
523
524
  "use strict";
@@ -613,8 +614,9 @@ var init_schema = __esm({
613
614
  taskId: text("task_id").notNull().references(() => tasks.id, { onDelete: "cascade" }),
614
615
  relatedTo: text("related_to").notNull().references(() => tasks.id, { onDelete: "cascade" }),
615
616
  relationType: text("relation_type", {
616
- enum: ["related", "blocks", "duplicates"]
617
- }).notNull().default("related")
617
+ enum: ["related", "blocks", "duplicates", "absorbs", "fixes", "extends", "supersedes"]
618
+ }).notNull().default("related"),
619
+ reason: text("reason")
618
620
  }, (table) => [
619
621
  primaryKey({ columns: [table.taskId, table.relatedTo] })
620
622
  ]);
@@ -687,7 +689,16 @@ var init_schema = __esm({
687
689
  skippedAt: text("skipped_at"),
688
690
  skipReason: text("skip_reason"),
689
691
  notesJson: text("notes_json").default("[]"),
690
- metadataJson: text("metadata_json").default("{}")
692
+ metadataJson: text("metadata_json").default("{}"),
693
+ // RCASD provenance tracking columns (T5100)
694
+ outputFile: text("output_file"),
695
+ createdBy: text("created_by"),
696
+ validatedBy: text("validated_by"),
697
+ validatedAt: text("validated_at"),
698
+ validationStatus: text("validation_status", {
699
+ enum: ["pending", "in_review", "approved", "rejected", "needs_revision"]
700
+ }),
701
+ provenanceChainJson: text("provenance_chain_json")
691
702
  }, (table) => [
692
703
  index("idx_lifecycle_stages_pipeline_id").on(table.pipelineId),
693
704
  index("idx_lifecycle_stages_stage_name").on(table.stageName),
@@ -732,6 +743,25 @@ var init_schema = __esm({
732
743
  }, (table) => [
733
744
  index("idx_lifecycle_transitions_pipeline_id").on(table.pipelineId)
734
745
  ]);
746
+ manifestEntries = sqliteTable("manifest_entries", {
747
+ id: text("id").primaryKey(),
748
+ pipelineId: text("pipeline_id").references(() => lifecyclePipelines.id, { onDelete: "cascade" }),
749
+ stageId: text("stage_id").references(() => lifecycleStages.id, { onDelete: "cascade" }),
750
+ title: text("title").notNull(),
751
+ date: text("date").notNull(),
752
+ status: text("status", { enum: MANIFEST_STATUSES }).notNull(),
753
+ agentType: text("agent_type"),
754
+ outputFile: text("output_file"),
755
+ topicsJson: text("topics_json").default("[]"),
756
+ findingsJson: text("findings_json").default("[]"),
757
+ linkedTasksJson: text("linked_tasks_json").default("[]"),
758
+ createdBy: text("created_by"),
759
+ createdAt: text("created_at").notNull().default(sql`(datetime('now'))`)
760
+ }, (table) => [
761
+ index("idx_manifest_entries_pipeline_id").on(table.pipelineId),
762
+ index("idx_manifest_entries_stage_id").on(table.stageId),
763
+ index("idx_manifest_entries_status").on(table.status)
764
+ ]);
735
765
  schemaMeta = sqliteTable("schema_meta", {
736
766
  key: text("key").primaryKey(),
737
767
  value: text("value").notNull()
@@ -829,11 +859,36 @@ function openNativeDatabase(path, options) {
829
859
  readOnly: options?.readonly ?? false,
830
860
  timeout: options?.timeout ?? 5e3
831
861
  });
862
+ db.exec("PRAGMA busy_timeout=5000");
832
863
  if (options?.enableWal !== false) {
833
- db.exec("PRAGMA journal_mode=WAL");
864
+ const MAX_WAL_RETRIES = 3;
865
+ const RETRY_DELAY_MS = 200;
866
+ let walSet = false;
867
+ for (let attempt = 1; attempt <= MAX_WAL_RETRIES; attempt++) {
868
+ db.exec("PRAGMA journal_mode=WAL");
869
+ const result = db.prepare("PRAGMA journal_mode").get();
870
+ const currentMode = result?.journal_mode?.toLowerCase?.() ?? "unknown";
871
+ if (currentMode === "wal") {
872
+ walSet = true;
873
+ break;
874
+ }
875
+ if (attempt < MAX_WAL_RETRIES) {
876
+ const buf = new SharedArrayBuffer(4);
877
+ Atomics.wait(new Int32Array(buf), 0, 0, RETRY_DELAY_MS * attempt);
878
+ }
879
+ }
880
+ if (!walSet) {
881
+ const finalResult = db.prepare("PRAGMA journal_mode").get();
882
+ const finalMode = finalResult?.journal_mode?.toLowerCase?.() ?? "unknown";
883
+ if (finalMode !== "wal") {
884
+ db.close();
885
+ throw new Error(
886
+ `CRITICAL: Failed to set WAL journal mode after ${MAX_WAL_RETRIES} attempts. Database is in '${finalMode}' mode. Another process likely holds an EXCLUSIVE lock on ${path}. Refusing to open \u2014 concurrent writes in DELETE mode cause data loss. Kill other cleo/MCP processes and retry. (T5173)`
887
+ );
888
+ }
889
+ }
834
890
  }
835
891
  db.exec("PRAGMA foreign_keys=ON");
836
- db.exec("PRAGMA busy_timeout=5000");
837
892
  return db;
838
893
  }
839
894
  function createDrizzleCallback(db) {
@@ -894,13 +949,14 @@ __export(sqlite_exports, {
894
949
  getDbPath: () => getDbPath,
895
950
  getNativeDb: () => getNativeDb,
896
951
  getSchemaVersion: () => getSchemaVersion,
952
+ isSqliteBusy: () => isSqliteBusy,
897
953
  resetDbState: () => resetDbState,
898
954
  resolveMigrationsFolder: () => resolveMigrationsFolder,
899
955
  schema: () => schema_exports
900
956
  });
901
957
  import { existsSync as existsSync3, mkdirSync } from "node:fs";
902
958
  import { createRequire as createRequire2 } from "node:module";
903
- import { dirname as dirname2, join as join3 } from "node:path";
959
+ import { dirname as dirname2, join as join3, resolve as resolve3 } from "node:path";
904
960
  import { fileURLToPath } from "node:url";
905
961
  import { eq } from "drizzle-orm";
906
962
  import { readMigrationFiles } from "drizzle-orm/migrator";
@@ -932,6 +988,22 @@ async function getDb(cwd) {
932
988
  nativeDb.exec(
933
989
  `INSERT OR IGNORE INTO schema_meta (key, value) VALUES ('task_id_sequence', '{"counter":0,"lastId":"T000","checksum":"seed"}')`
934
990
  );
991
+ if (!_gitTrackingChecked) {
992
+ _gitTrackingChecked = true;
993
+ try {
994
+ const { execFileSync: execFileSync6 } = await import("node:child_process");
995
+ execFileSync6("git", ["ls-files", "--error-unmatch", dbPath], {
996
+ cwd: resolve3(dbPath, "..", ".."),
997
+ stdio: "pipe"
998
+ });
999
+ const log5 = getLogger("sqlite");
1000
+ log5.warn(
1001
+ { dbPath },
1002
+ "tasks.db is tracked by project git \u2014 this risks data loss on git operations. Run: git rm --cached .cleo/tasks.db (see ADR-013)"
1003
+ );
1004
+ } catch {
1005
+ }
1006
+ }
935
1007
  _db = db;
936
1008
  return db;
937
1009
  })();
@@ -952,6 +1024,11 @@ function tableExists(nativeDb, tableName) {
952
1024
  ).get(tableName);
953
1025
  return !!result;
954
1026
  }
1027
+ function isSqliteBusy(err) {
1028
+ if (!(err instanceof Error)) return false;
1029
+ const msg = err.message.toLowerCase();
1030
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1031
+ }
955
1032
  async function runMigrations(nativeDb, db) {
956
1033
  const migrationsFolder = resolveMigrationsFolder();
957
1034
  if (tableExists(nativeDb, "tasks") && !tableExists(nativeDb, "__drizzle_migrations")) {
@@ -971,16 +1048,33 @@ async function runMigrations(nativeDb, db) {
971
1048
  }
972
1049
  }
973
1050
  await migrate(db, async (queries) => {
974
- nativeDb.prepare("BEGIN").run();
975
- try {
976
- for (const query of queries) {
977
- nativeDb.prepare(query).run();
1051
+ let lastError;
1052
+ for (let attempt = 1; attempt <= MAX_MIGRATION_RETRIES; attempt++) {
1053
+ try {
1054
+ nativeDb.prepare("BEGIN IMMEDIATE").run();
1055
+ try {
1056
+ for (const query of queries) {
1057
+ nativeDb.prepare(query).run();
1058
+ }
1059
+ nativeDb.prepare("COMMIT").run();
1060
+ return;
1061
+ } catch (err) {
1062
+ nativeDb.prepare("ROLLBACK").run();
1063
+ throw err;
1064
+ }
1065
+ } catch (err) {
1066
+ if (!isSqliteBusy(err) || attempt === MAX_MIGRATION_RETRIES) {
1067
+ throw err;
1068
+ }
1069
+ lastError = err;
1070
+ const exponentialDelay = MIGRATION_RETRY_BASE_DELAY_MS * Math.pow(2, attempt - 1);
1071
+ const jitter = Math.random() * exponentialDelay * 0.5;
1072
+ const delay = Math.min(exponentialDelay + jitter, MIGRATION_RETRY_MAX_DELAY_MS);
1073
+ const buf = new SharedArrayBuffer(4);
1074
+ Atomics.wait(new Int32Array(buf), 0, 0, Math.round(delay));
978
1075
  }
979
- nativeDb.prepare("COMMIT").run();
980
- } catch (err) {
981
- nativeDb.prepare("ROLLBACK").run();
982
- throw err;
983
1076
  }
1077
+ throw lastError;
984
1078
  }, { migrationsFolder });
985
1079
  }
986
1080
  function closeDb() {
@@ -1021,13 +1115,14 @@ function dbExists(cwd) {
1021
1115
  function getNativeDb() {
1022
1116
  return _nativeDb;
1023
1117
  }
1024
- var _require2, DatabaseSync2, DB_FILENAME, SQLITE_SCHEMA_VERSION, SCHEMA_VERSION, _db, _nativeDb, _dbPath, _initPromise;
1118
+ var _require2, DatabaseSync2, DB_FILENAME, SQLITE_SCHEMA_VERSION, SCHEMA_VERSION, _db, _nativeDb, _dbPath, _initPromise, _gitTrackingChecked, MAX_MIGRATION_RETRIES, MIGRATION_RETRY_BASE_DELAY_MS, MIGRATION_RETRY_MAX_DELAY_MS;
1025
1119
  var init_sqlite = __esm({
1026
1120
  "src/store/sqlite.ts"() {
1027
1121
  "use strict";
1028
1122
  init_schema();
1029
1123
  init_paths();
1030
1124
  init_node_sqlite_adapter();
1125
+ init_logger();
1031
1126
  _require2 = createRequire2(import.meta.url);
1032
1127
  ({ DatabaseSync: DatabaseSync2 } = _require2("node:sqlite"));
1033
1128
  DB_FILENAME = "tasks.db";
@@ -1037,6 +1132,10 @@ var init_sqlite = __esm({
1037
1132
  _nativeDb = null;
1038
1133
  _dbPath = null;
1039
1134
  _initPromise = null;
1135
+ _gitTrackingChecked = false;
1136
+ MAX_MIGRATION_RETRIES = 5;
1137
+ MIGRATION_RETRY_BASE_DELAY_MS = 100;
1138
+ MIGRATION_RETRY_MAX_DELAY_MS = 2e3;
1040
1139
  }
1041
1140
  });
1042
1141
 
@@ -1803,6 +1902,30 @@ async function loadDependenciesForTasks(db, tasks2, validationIds) {
1803
1902
  }
1804
1903
  }
1805
1904
  }
1905
+ async function loadRelationsForTasks(db, tasks2) {
1906
+ if (tasks2.length === 0) return;
1907
+ const taskIds = tasks2.map((t) => t.id);
1908
+ const allRels = await db.select().from(taskRelations).where(inArray(taskRelations.taskId, taskIds)).all();
1909
+ const relMap = /* @__PURE__ */ new Map();
1910
+ for (const rel of allRels) {
1911
+ let arr = relMap.get(rel.taskId);
1912
+ if (!arr) {
1913
+ arr = [];
1914
+ relMap.set(rel.taskId, arr);
1915
+ }
1916
+ arr.push({
1917
+ taskId: rel.relatedTo,
1918
+ type: rel.relationType,
1919
+ reason: rel.reason ?? void 0
1920
+ });
1921
+ }
1922
+ for (const task of tasks2) {
1923
+ const relations = relMap.get(task.id);
1924
+ if (relations && relations.length > 0) {
1925
+ task.relates = relations;
1926
+ }
1927
+ }
1928
+ }
1806
1929
  var init_db_helpers = __esm({
1807
1930
  "src/store/db-helpers.ts"() {
1808
1931
  "use strict";
@@ -1851,6 +1974,7 @@ async function createSqliteDataAccessor(cwd) {
1851
1974
  const tasks2 = taskRows.map(rowToTask);
1852
1975
  if (tasks2.length > 0) {
1853
1976
  await loadDependenciesForTasks(db, tasks2);
1977
+ await loadRelationsForTasks(db, tasks2);
1854
1978
  }
1855
1979
  const projectMeta = await getMetaValue(cwd, "project_meta") ?? DEFAULT_PROJECT_META;
1856
1980
  const workState = await getMetaValue(cwd, "focus_state") ?? DEFAULT_WORK_STATE;
@@ -1926,6 +2050,7 @@ async function createSqliteDataAccessor(cwd) {
1926
2050
  ...activeRows.map((r) => r.id)
1927
2051
  ]);
1928
2052
  await loadDependenciesForTasks(db, archivedTasks, allKnownIds);
2053
+ await loadRelationsForTasks(db, archivedTasks);
1929
2054
  }
1930
2055
  return {
1931
2056
  archivedTasks,
@@ -1992,6 +2117,14 @@ async function createSqliteDataAccessor(cwd) {
1992
2117
  await upsertTask(db, row);
1993
2118
  await updateDependencies(db, task.id, task.depends ?? []);
1994
2119
  },
2120
+ async addRelation(taskId, relatedTo, relationType, reason) {
2121
+ const db = await getDb(cwd);
2122
+ const validTypes = ["related", "blocks", "duplicates", "absorbs", "fixes", "extends", "supersedes"];
2123
+ if (!validTypes.includes(relationType)) {
2124
+ throw new Error(`Invalid relation type: ${relationType}. Valid types: ${validTypes.join(", ")}`);
2125
+ }
2126
+ await db.insert(taskRelations).values({ taskId, relatedTo, relationType, reason: reason ?? null }).onConflictDoNothing().run();
2127
+ },
1995
2128
  async archiveSingleTask(taskId, fields) {
1996
2129
  const db = await getDb(cwd);
1997
2130
  const rows = await db.select({ id: tasks.id }).from(tasks).where(eq3(tasks.id, taskId)).all();
@@ -2066,9 +2199,9 @@ import { readFile as readFile2, writeFile } from "node:fs/promises";
2066
2199
  import { existsSync as existsSync4 } from "node:fs";
2067
2200
  import { execFile } from "node:child_process";
2068
2201
  import { promisify } from "node:util";
2069
- import { join as join5, resolve as resolve3 } from "node:path";
2202
+ import { join as join5, resolve as resolve4 } from "node:path";
2070
2203
  function makeCleoGitEnv(cleoDir) {
2071
- const abs = resolve3(cleoDir);
2204
+ const abs = resolve4(cleoDir);
2072
2205
  return {
2073
2206
  ...process.env,
2074
2207
  GIT_DIR: join5(abs, ".git"),
@@ -2078,7 +2211,7 @@ function makeCleoGitEnv(cleoDir) {
2078
2211
  async function cleoGitCommand(args, cleoDir) {
2079
2212
  try {
2080
2213
  const result = await execFileAsync("git", args, {
2081
- cwd: resolve3(cleoDir),
2214
+ cwd: resolve4(cleoDir),
2082
2215
  // absolute cwd so relative paths in args resolve correctly
2083
2216
  env: makeCleoGitEnv(cleoDir),
2084
2217
  timeout: 1e4
@@ -2294,6 +2427,7 @@ var init_sqlite_backup = __esm({
2294
2427
  // src/core/sequence/index.ts
2295
2428
  var sequence_exports = {};
2296
2429
  __export(sequence_exports, {
2430
+ allocateNextTaskId: () => allocateNextTaskId,
2297
2431
  checkSequence: () => checkSequence,
2298
2432
  repairSequence: () => repairSequence,
2299
2433
  showSequence: () => showSequence
@@ -2480,7 +2614,56 @@ async function repairSequence(cwd, accessor) {
2480
2614
  message: `Sequence repaired: ${oldCounter} -> ${newCounter}`
2481
2615
  };
2482
2616
  }
2483
- var SEQUENCE_META_KEY;
2617
+ async function allocateNextTaskId(cwd, retryCount = 0) {
2618
+ await getDb(cwd);
2619
+ const nativeDb = getNativeDb();
2620
+ if (!nativeDb) {
2621
+ throw new CleoError(3 /* FILE_ERROR */, "Native database not available for atomic ID allocation");
2622
+ }
2623
+ nativeDb.prepare("BEGIN IMMEDIATE").run();
2624
+ try {
2625
+ nativeDb.prepare(`
2626
+ UPDATE schema_meta
2627
+ SET value = json_set(value,
2628
+ '$.counter', json_extract(value, '$.counter') + 1,
2629
+ '$.lastId', 'T' || printf('%03d', json_extract(value, '$.counter') + 1),
2630
+ '$.checksum', 'alloc-' || strftime('%s','now')
2631
+ )
2632
+ WHERE key = 'task_id_sequence'
2633
+ `).run();
2634
+ const row = nativeDb.prepare(`
2635
+ SELECT json_extract(value, '$.counter') AS counter
2636
+ FROM schema_meta WHERE key = 'task_id_sequence'
2637
+ `).get();
2638
+ if (!row) {
2639
+ throw new CleoError(3 /* FILE_ERROR */, "Sequence counter not found after increment");
2640
+ }
2641
+ const newId = `T${String(row.counter).padStart(3, "0")}`;
2642
+ const existing = nativeDb.prepare(
2643
+ "SELECT id FROM tasks WHERE id = ?"
2644
+ ).get(newId);
2645
+ if (existing) {
2646
+ nativeDb.prepare("ROLLBACK").run();
2647
+ if (retryCount >= MAX_ALLOC_RETRIES) {
2648
+ throw new CleoError(
2649
+ 22 /* ID_COLLISION */,
2650
+ `Failed to allocate unique task ID after ${MAX_ALLOC_RETRIES} retries (last attempted: ${newId})`
2651
+ );
2652
+ }
2653
+ await repairSequence(cwd);
2654
+ return allocateNextTaskId(cwd, retryCount + 1);
2655
+ }
2656
+ nativeDb.prepare("COMMIT").run();
2657
+ return newId;
2658
+ } catch (err) {
2659
+ try {
2660
+ nativeDb.prepare("ROLLBACK").run();
2661
+ } catch {
2662
+ }
2663
+ throw err;
2664
+ }
2665
+ }
2666
+ var SEQUENCE_META_KEY, MAX_ALLOC_RETRIES;
2484
2667
  var init_sequence = __esm({
2485
2668
  "src/core/sequence/index.ts"() {
2486
2669
  "use strict";
@@ -2491,6 +2674,7 @@ var init_sequence = __esm({
2491
2674
  init_sqlite_data_accessor();
2492
2675
  init_data_accessor();
2493
2676
  SEQUENCE_META_KEY = "task_id_sequence";
2677
+ MAX_ALLOC_RETRIES = 3;
2494
2678
  }
2495
2679
  });
2496
2680
 
@@ -2803,6 +2987,11 @@ var init_safety_data_accessor = __esm({
2803
2987
  this.getSafetyOptions()
2804
2988
  );
2805
2989
  }
2990
+ // ---- Relations (pass-through to inner, T5168) ----
2991
+ async addRelation(taskId, relatedTo, relationType, reason) {
2992
+ if (!this.inner.addRelation) return;
2993
+ await this.inner.addRelation(taskId, relatedTo, relationType, reason);
2994
+ }
2806
2995
  // ---- Metadata (pass-through to inner) ----
2807
2996
  async getMetaValue(key) {
2808
2997
  return this.inner.getMetaValue?.(key) ?? null;
@@ -2838,10 +3027,57 @@ var init_data_accessor = __esm({
2838
3027
  }
2839
3028
  });
2840
3029
 
2841
- // src/mcp/engine/store.ts
2842
- import { readFileSync as readFileSync4, writeFileSync, renameSync as renameSync3, mkdirSync as mkdirSync3, existsSync as existsSync8, readdirSync as readdirSync2, unlinkSync as unlinkSync2 } from "fs";
2843
- import { join as join8, dirname as dirname5, basename as basename2 } from "path";
3030
+ // src/store/file-utils.ts
3031
+ import { readFileSync as readFileSync4, writeFileSync, renameSync as renameSync3, existsSync as existsSync8, unlinkSync as unlinkSync2, mkdirSync as mkdirSync3, readdirSync as readdirSync2 } from "node:fs";
3032
+ import { join as join8, dirname as dirname5, basename as basename2 } from "node:path";
3033
+ import { randomBytes as randomBytes2 } from "node:crypto";
2844
3034
  import * as lockfile2 from "proper-lockfile";
3035
+ function rotateBackup(filePath) {
3036
+ const dir = dirname5(filePath);
3037
+ const name = basename2(filePath);
3038
+ const backupDir = join8(dir, ".backups");
3039
+ if (!existsSync8(backupDir)) {
3040
+ mkdirSync3(backupDir, { recursive: true });
3041
+ }
3042
+ for (let i = MAX_BACKUPS; i >= 1; i--) {
3043
+ const current = join8(backupDir, `${name}.${i}`);
3044
+ if (i === MAX_BACKUPS) {
3045
+ try {
3046
+ unlinkSync2(current);
3047
+ } catch {
3048
+ }
3049
+ } else {
3050
+ const next = join8(backupDir, `${name}.${i + 1}`);
3051
+ try {
3052
+ if (existsSync8(current)) renameSync3(current, next);
3053
+ } catch {
3054
+ }
3055
+ }
3056
+ }
3057
+ try {
3058
+ const content = readFileSync4(filePath, "utf-8");
3059
+ writeFileSync(join8(backupDir, `${name}.1`), content, "utf-8");
3060
+ } catch {
3061
+ }
3062
+ }
3063
+ function writeJsonFileAtomic(filePath, data, indent = 2) {
3064
+ const dir = dirname5(filePath);
3065
+ const tempPath = join8(dir, `.${basename2(filePath)}.${randomBytes2(6).toString("hex")}.tmp`);
3066
+ const content = JSON.stringify(data, null, indent) + "\n";
3067
+ writeFileSync(tempPath, content, "utf-8");
3068
+ try {
3069
+ if (existsSync8(filePath)) {
3070
+ rotateBackup(filePath);
3071
+ }
3072
+ renameSync3(tempPath, filePath);
3073
+ } catch (error) {
3074
+ try {
3075
+ unlinkSync2(tempPath);
3076
+ } catch {
3077
+ }
3078
+ throw error;
3079
+ }
3080
+ }
2845
3081
  function readJsonFile(filePath) {
2846
3082
  try {
2847
3083
  const content = readFileSync4(filePath, "utf-8");
@@ -2853,12 +3089,95 @@ function readJsonFile(filePath) {
2853
3089
  throw error;
2854
3090
  }
2855
3091
  }
3092
+ function readLogFileEntries(filePath) {
3093
+ let content;
3094
+ try {
3095
+ content = readFileSync4(filePath, "utf-8").trim();
3096
+ } catch (error) {
3097
+ if (error.code === "ENOENT") return [];
3098
+ throw error;
3099
+ }
3100
+ if (!content) return [];
3101
+ try {
3102
+ const parsed = JSON.parse(content);
3103
+ if (Array.isArray(parsed)) return parsed;
3104
+ if (parsed && typeof parsed === "object" && Array.isArray(parsed.entries)) {
3105
+ return parsed.entries;
3106
+ }
3107
+ return [parsed];
3108
+ } catch {
3109
+ }
3110
+ const entries = [];
3111
+ if (content.startsWith("{")) {
3112
+ let depth = 0;
3113
+ let inString = false;
3114
+ let escaped = false;
3115
+ let jsonEnd = -1;
3116
+ for (let i = 0; i < content.length; i++) {
3117
+ const ch = content[i];
3118
+ if (escaped) {
3119
+ escaped = false;
3120
+ continue;
3121
+ }
3122
+ if (ch === "\\" && inString) {
3123
+ escaped = true;
3124
+ continue;
3125
+ }
3126
+ if (ch === '"') {
3127
+ inString = !inString;
3128
+ continue;
3129
+ }
3130
+ if (inString) continue;
3131
+ if (ch === "{") depth++;
3132
+ else if (ch === "}") {
3133
+ depth--;
3134
+ if (depth === 0) {
3135
+ jsonEnd = i + 1;
3136
+ break;
3137
+ }
3138
+ }
3139
+ }
3140
+ if (jsonEnd > 0) {
3141
+ try {
3142
+ const initial = JSON.parse(content.substring(0, jsonEnd));
3143
+ if (initial && Array.isArray(initial.entries)) entries.push(...initial.entries);
3144
+ } catch {
3145
+ }
3146
+ const remainder = content.substring(jsonEnd).trim();
3147
+ if (remainder) {
3148
+ for (const line of remainder.split("\n")) {
3149
+ const l = line.trim();
3150
+ if (!l || !l.startsWith("{")) continue;
3151
+ try {
3152
+ entries.push(JSON.parse(l));
3153
+ } catch {
3154
+ }
3155
+ }
3156
+ }
3157
+ }
3158
+ } else {
3159
+ for (const line of content.split("\n")) {
3160
+ const l = line.trim();
3161
+ if (!l || !l.startsWith("{")) continue;
3162
+ try {
3163
+ entries.push(JSON.parse(l));
3164
+ } catch {
3165
+ }
3166
+ }
3167
+ }
3168
+ return entries;
3169
+ }
2856
3170
  function getDataPath(projectRoot, filename) {
2857
3171
  return join8(projectRoot, ".cleo", filename);
2858
3172
  }
2859
- var init_store = __esm({
2860
- "src/mcp/engine/store.ts"() {
3173
+ function resolveProjectRoot() {
3174
+ return process.env["CLEO_ROOT"] || process.cwd();
3175
+ }
3176
+ var MAX_BACKUPS;
3177
+ var init_file_utils = __esm({
3178
+ "src/store/file-utils.ts"() {
2861
3179
  "use strict";
3180
+ MAX_BACKUPS = 10;
2862
3181
  }
2863
3182
  });
2864
3183
 
@@ -3102,7 +3421,7 @@ var init_plan = __esm({
3102
3421
  "src/core/tasks/plan.ts"() {
3103
3422
  "use strict";
3104
3423
  init_data_accessor();
3105
- init_store();
3424
+ init_file_utils();
3106
3425
  init_deps_ready();
3107
3426
  PRIORITY_SCORE2 = {
3108
3427
  critical: 100,
@@ -3114,7 +3433,7 @@ var init_plan = __esm({
3114
3433
  });
3115
3434
 
3116
3435
  // src/core/sessions/decisions.ts
3117
- import { randomBytes as randomBytes2 } from "node:crypto";
3436
+ import { randomBytes as randomBytes3 } from "node:crypto";
3118
3437
  import { readFileSync as readFileSync5, appendFileSync, mkdirSync as mkdirSync4, existsSync as existsSync9 } from "node:fs";
3119
3438
  import { join as join9 } from "node:path";
3120
3439
  async function recordDecision(projectRoot, params) {
@@ -3130,7 +3449,7 @@ async function recordDecision(projectRoot, params) {
3130
3449
  }
3131
3450
  const decisionPath = join9(auditDir, "decisions.jsonl");
3132
3451
  const record = {
3133
- id: `dec-${randomBytes2(8).toString("hex")}`,
3452
+ id: `dec-${randomBytes3(8).toString("hex")}`,
3134
3453
  sessionId: params.sessionId,
3135
3454
  taskId: params.taskId,
3136
3455
  decision: params.decision,
@@ -3416,189 +3735,36 @@ async function computeChainPosition(projectRoot, sessionId) {
3416
3735
  let current = sessionId;
3417
3736
  let position = 1;
3418
3737
  const visited = /* @__PURE__ */ new Set();
3419
- while (true) {
3420
- visited.add(current);
3421
- const session = sessionMap.get(current);
3422
- if (!session?.previousSessionId || visited.has(session.previousSessionId)) break;
3423
- current = session.previousSessionId;
3424
- position++;
3425
- }
3426
- let length = position;
3427
- const startSession = sessionMap.get(sessionId);
3428
- let fwd = startSession?.nextSessionId;
3429
- while (fwd && !visited.has(fwd)) {
3430
- visited.add(fwd);
3431
- length++;
3432
- const s = sessionMap.get(fwd);
3433
- fwd = s?.nextSessionId ?? void 0;
3434
- }
3435
- return { position, length };
3436
- } catch {
3437
- return { position: 1, length: 1 };
3438
- }
3439
- }
3440
- var execFileAsync2;
3441
- var init_handoff = __esm({
3442
- "src/core/sessions/handoff.ts"() {
3443
- "use strict";
3444
- init_data_accessor();
3445
- init_errors();
3446
- init_exit_codes();
3447
- init_decisions();
3448
- execFileAsync2 = promisify2(execFile2);
3449
- }
3450
- });
3451
-
3452
- // src/store/file-utils.ts
3453
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync2, renameSync as renameSync4, existsSync as existsSync11, unlinkSync as unlinkSync3, mkdirSync as mkdirSync6 } from "node:fs";
3454
- import { join as join11, dirname as dirname6, basename as basename3 } from "node:path";
3455
- import { randomBytes as randomBytes5 } from "node:crypto";
3456
- function rotateBackup(filePath) {
3457
- const dir = dirname6(filePath);
3458
- const name = basename3(filePath);
3459
- const backupDir = join11(dir, ".backups");
3460
- if (!existsSync11(backupDir)) {
3461
- mkdirSync6(backupDir, { recursive: true });
3462
- }
3463
- for (let i = MAX_BACKUPS; i >= 1; i--) {
3464
- const current = join11(backupDir, `${name}.${i}`);
3465
- if (i === MAX_BACKUPS) {
3466
- try {
3467
- unlinkSync3(current);
3468
- } catch {
3469
- }
3470
- } else {
3471
- const next = join11(backupDir, `${name}.${i + 1}`);
3472
- try {
3473
- if (existsSync11(current)) renameSync4(current, next);
3474
- } catch {
3475
- }
3476
- }
3477
- }
3478
- try {
3479
- const content = readFileSync6(filePath, "utf-8");
3480
- writeFileSync2(join11(backupDir, `${name}.1`), content, "utf-8");
3481
- } catch {
3482
- }
3483
- }
3484
- function writeJsonFileAtomic(filePath, data, indent = 2) {
3485
- const dir = dirname6(filePath);
3486
- const tempPath = join11(dir, `.${basename3(filePath)}.${randomBytes5(6).toString("hex")}.tmp`);
3487
- const content = JSON.stringify(data, null, indent) + "\n";
3488
- writeFileSync2(tempPath, content, "utf-8");
3489
- try {
3490
- if (existsSync11(filePath)) {
3491
- rotateBackup(filePath);
3492
- }
3493
- renameSync4(tempPath, filePath);
3494
- } catch (error) {
3495
- try {
3496
- unlinkSync3(tempPath);
3497
- } catch {
3498
- }
3499
- throw error;
3500
- }
3501
- }
3502
- function readJsonFile2(filePath) {
3503
- try {
3504
- const content = readFileSync6(filePath, "utf-8");
3505
- return JSON.parse(content);
3506
- } catch (error) {
3507
- if (error.code === "ENOENT") {
3508
- return null;
3509
- }
3510
- throw error;
3511
- }
3512
- }
3513
- function readLogFileEntries2(filePath) {
3514
- let content;
3515
- try {
3516
- content = readFileSync6(filePath, "utf-8").trim();
3517
- } catch (error) {
3518
- if (error.code === "ENOENT") return [];
3519
- throw error;
3520
- }
3521
- if (!content) return [];
3522
- try {
3523
- const parsed = JSON.parse(content);
3524
- if (Array.isArray(parsed)) return parsed;
3525
- if (parsed && typeof parsed === "object" && Array.isArray(parsed.entries)) {
3526
- return parsed.entries;
3527
- }
3528
- return [parsed];
3529
- } catch {
3530
- }
3531
- const entries = [];
3532
- if (content.startsWith("{")) {
3533
- let depth = 0;
3534
- let inString = false;
3535
- let escaped = false;
3536
- let jsonEnd = -1;
3537
- for (let i = 0; i < content.length; i++) {
3538
- const ch = content[i];
3539
- if (escaped) {
3540
- escaped = false;
3541
- continue;
3542
- }
3543
- if (ch === "\\" && inString) {
3544
- escaped = true;
3545
- continue;
3546
- }
3547
- if (ch === '"') {
3548
- inString = !inString;
3549
- continue;
3550
- }
3551
- if (inString) continue;
3552
- if (ch === "{") depth++;
3553
- else if (ch === "}") {
3554
- depth--;
3555
- if (depth === 0) {
3556
- jsonEnd = i + 1;
3557
- break;
3558
- }
3559
- }
3560
- }
3561
- if (jsonEnd > 0) {
3562
- try {
3563
- const initial = JSON.parse(content.substring(0, jsonEnd));
3564
- if (initial && Array.isArray(initial.entries)) entries.push(...initial.entries);
3565
- } catch {
3566
- }
3567
- const remainder = content.substring(jsonEnd).trim();
3568
- if (remainder) {
3569
- for (const line of remainder.split("\n")) {
3570
- const l = line.trim();
3571
- if (!l || !l.startsWith("{")) continue;
3572
- try {
3573
- entries.push(JSON.parse(l));
3574
- } catch {
3575
- }
3576
- }
3577
- }
3578
- }
3579
- } else {
3580
- for (const line of content.split("\n")) {
3581
- const l = line.trim();
3582
- if (!l || !l.startsWith("{")) continue;
3583
- try {
3584
- entries.push(JSON.parse(l));
3585
- } catch {
3586
- }
3738
+ while (true) {
3739
+ visited.add(current);
3740
+ const session = sessionMap.get(current);
3741
+ if (!session?.previousSessionId || visited.has(session.previousSessionId)) break;
3742
+ current = session.previousSessionId;
3743
+ position++;
3744
+ }
3745
+ let length = position;
3746
+ const startSession = sessionMap.get(sessionId);
3747
+ let fwd = startSession?.nextSessionId;
3748
+ while (fwd && !visited.has(fwd)) {
3749
+ visited.add(fwd);
3750
+ length++;
3751
+ const s = sessionMap.get(fwd);
3752
+ fwd = s?.nextSessionId ?? void 0;
3587
3753
  }
3754
+ return { position, length };
3755
+ } catch {
3756
+ return { position: 1, length: 1 };
3588
3757
  }
3589
- return entries;
3590
- }
3591
- function getDataPath2(projectRoot, filename) {
3592
- return join11(projectRoot, ".cleo", filename);
3593
3758
  }
3594
- function resolveProjectRoot() {
3595
- return process.env["CLEO_ROOT"] || process.cwd();
3596
- }
3597
- var MAX_BACKUPS;
3598
- var init_file_utils = __esm({
3599
- "src/store/file-utils.ts"() {
3759
+ var execFileAsync2;
3760
+ var init_handoff = __esm({
3761
+ "src/core/sessions/handoff.ts"() {
3600
3762
  "use strict";
3601
- MAX_BACKUPS = 10;
3763
+ init_data_accessor();
3764
+ init_errors();
3765
+ init_exit_codes();
3766
+ init_decisions();
3767
+ execFileAsync2 = promisify2(execFile2);
3602
3768
  }
3603
3769
  });
3604
3770
 
@@ -3613,15 +3779,15 @@ __export(platform_exports, {
3613
3779
  dateDaysAgo: () => dateDaysAgo,
3614
3780
  detectPlatform: () => detectPlatform,
3615
3781
  generateRandomHex: () => generateRandomHex,
3616
- getDataPath: () => getDataPath2,
3782
+ getDataPath: () => getDataPath,
3617
3783
  getFileMtime: () => getFileMtime,
3618
3784
  getFileSize: () => getFileSize,
3619
3785
  getIsoTimestamp: () => getIsoTimestamp,
3620
3786
  getNodeUpgradeInstructions: () => getNodeUpgradeInstructions,
3621
3787
  getNodeVersionInfo: () => getNodeVersionInfo,
3622
3788
  isoToEpoch: () => isoToEpoch,
3623
- readJsonFile: () => readJsonFile2,
3624
- readLogFileEntries: () => readLogFileEntries2,
3789
+ readJsonFile: () => readJsonFile,
3790
+ readLogFileEntries: () => readLogFileEntries,
3625
3791
  requireTool: () => requireTool,
3626
3792
  resolveProjectRoot: () => resolveProjectRoot,
3627
3793
  sha256: () => sha256,
@@ -3629,8 +3795,8 @@ __export(platform_exports, {
3629
3795
  });
3630
3796
  import { execFileSync } from "node:child_process";
3631
3797
  import { tmpdir } from "node:os";
3632
- import { join as join12 } from "node:path";
3633
- import { existsSync as existsSync12, statSync as statSync2 } from "node:fs";
3798
+ import { join as join11 } from "node:path";
3799
+ import { existsSync as existsSync11, statSync as statSync2 } from "node:fs";
3634
3800
  import { createHash as createHash3, randomBytes as randomBytes6 } from "node:crypto";
3635
3801
  function detectPlatform() {
3636
3802
  switch (process.platform) {
@@ -3689,11 +3855,11 @@ function dateDaysAgo(days) {
3689
3855
  return d.toISOString();
3690
3856
  }
3691
3857
  function getFileSize(filePath) {
3692
- if (!existsSync12(filePath)) return 0;
3858
+ if (!existsSync11(filePath)) return 0;
3693
3859
  return statSync2(filePath).size;
3694
3860
  }
3695
3861
  function getFileMtime(filePath) {
3696
- if (!existsSync12(filePath)) return null;
3862
+ if (!existsSync11(filePath)) return null;
3697
3863
  return statSync2(filePath).mtime.toISOString();
3698
3864
  }
3699
3865
  function generateRandomHex(bytes = 6) {
@@ -3704,7 +3870,7 @@ function sha256(data) {
3704
3870
  }
3705
3871
  function createTempFilePath(prefix = "cleo-", suffix = ".tmp") {
3706
3872
  const random = generateRandomHex(8);
3707
- return join12(tmpdir(), `${prefix}${random}${suffix}`);
3873
+ return join11(tmpdir(), `${prefix}${random}${suffix}`);
3708
3874
  }
3709
3875
  function getNodeVersionInfo() {
3710
3876
  const version = process.version.replace("v", "");
@@ -3834,20 +4000,20 @@ __export(schema_integrity_exports, {
3834
4000
  checkSchemaIntegrity: () => checkSchemaIntegrity,
3835
4001
  readSchemaVersionFromFile: () => readSchemaVersionFromFile
3836
4002
  });
3837
- import { existsSync as existsSync22, readFileSync as readFileSync15 } from "node:fs";
3838
- import { join as join22, dirname as dirname7 } from "node:path";
4003
+ import { existsSync as existsSync21, readFileSync as readFileSync14 } from "node:fs";
4004
+ import { join as join21, dirname as dirname6 } from "node:path";
3839
4005
  import { fileURLToPath as fileURLToPath2 } from "node:url";
3840
4006
  function resolveSchemaPath(schemaName) {
3841
- const __dirname2 = dirname7(fileURLToPath2(import.meta.url));
4007
+ const __dirname2 = dirname6(fileURLToPath2(import.meta.url));
3842
4008
  const candidates = [
3843
4009
  // dist/core/validation/ → schemas/
3844
- join22(__dirname2, "..", "..", "..", "schemas", schemaName),
4010
+ join21(__dirname2, "..", "..", "..", "schemas", schemaName),
3845
4011
  // src/core/validation/ → schemas/ (ts-node / vitest)
3846
- join22(__dirname2, "..", "..", "schemas", schemaName),
4012
+ join21(__dirname2, "..", "..", "schemas", schemaName),
3847
4013
  // fallback: look relative to cwd
3848
- join22(process.cwd(), "schemas", schemaName)
4014
+ join21(process.cwd(), "schemas", schemaName)
3849
4015
  ];
3850
- return candidates.find(existsSync22) ?? null;
4016
+ return candidates.find(existsSync21) ?? null;
3851
4017
  }
3852
4018
  function readSchemaVersionFromFile(schemaName) {
3853
4019
  const path = resolveSchemaPath(schemaName);
@@ -3856,7 +4022,7 @@ function readSchemaVersionFromFile(schemaName) {
3856
4022
  }
3857
4023
  function readSchemaVersion(schemaPath) {
3858
4024
  try {
3859
- const raw = readFileSync15(schemaPath, "utf-8");
4025
+ const raw = readFileSync14(schemaPath, "utf-8");
3860
4026
  const schema = JSON.parse(raw);
3861
4027
  const version = schema["schemaVersion"] ?? schema["_meta"]?.["schemaVersion"];
3862
4028
  return typeof version === "string" ? version : null;
@@ -3866,7 +4032,7 @@ function readSchemaVersion(schemaPath) {
3866
4032
  }
3867
4033
  function checkFile(target, cleoDir) {
3868
4034
  const filePath = target.filePath(cleoDir);
3869
- if (!existsSync22(filePath)) {
4035
+ if (!existsSync21(filePath)) {
3870
4036
  if (target.required) {
3871
4037
  return {
3872
4038
  label: target.label,
@@ -3878,7 +4044,7 @@ function checkFile(target, cleoDir) {
3878
4044
  }
3879
4045
  let data;
3880
4046
  try {
3881
- data = JSON.parse(readFileSync15(filePath, "utf-8"));
4047
+ data = JSON.parse(readFileSync14(filePath, "utf-8"));
3882
4048
  } catch (err) {
3883
4049
  return {
3884
4050
  label: target.label,
@@ -3896,7 +4062,7 @@ function checkFile(target, cleoDir) {
3896
4062
  }
3897
4063
  let schema;
3898
4064
  try {
3899
- schema = JSON.parse(readFileSync15(schemaPath, "utf-8"));
4065
+ schema = JSON.parse(readFileSync14(schemaPath, "utf-8"));
3900
4066
  } catch (err) {
3901
4067
  return {
3902
4068
  label: target.label,
@@ -3960,28 +4126,28 @@ var init_schema_integrity = __esm({
3960
4126
  INTEGRITY_TARGETS = [
3961
4127
  {
3962
4128
  label: "config.json",
3963
- filePath: (d) => join22(d, "config.json"),
4129
+ filePath: (d) => join21(d, "config.json"),
3964
4130
  schemaName: "config.schema.json",
3965
4131
  required: true,
3966
4132
  versionKey: "schemaVersion"
3967
4133
  },
3968
4134
  {
3969
4135
  label: "project-info.json",
3970
- filePath: (d) => join22(d, "project-info.json"),
4136
+ filePath: (d) => join21(d, "project-info.json"),
3971
4137
  schemaName: "project-info.schema.json",
3972
4138
  required: false,
3973
4139
  versionKey: "schemaVersion"
3974
4140
  },
3975
4141
  {
3976
4142
  label: "project-context.json",
3977
- filePath: (d) => join22(d, "project-context.json"),
4143
+ filePath: (d) => join21(d, "project-context.json"),
3978
4144
  schemaName: "project-context.schema.json",
3979
4145
  required: false,
3980
4146
  versionKey: "schemaVersion"
3981
4147
  },
3982
4148
  {
3983
4149
  label: ".context-state.json",
3984
- filePath: (d) => join22(d, ".context-state.json"),
4150
+ filePath: (d) => join21(d, ".context-state.json"),
3985
4151
  schemaName: "context-state.schema.json",
3986
4152
  required: false,
3987
4153
  versionKey: "version"
@@ -3996,17 +4162,17 @@ __export(mcp_exports, {
3996
4162
  detectEnvMode: () => detectEnvMode,
3997
4163
  generateMcpServerEntry: () => generateMcpServerEntry
3998
4164
  });
3999
- import { readFileSync as readFileSync16 } from "node:fs";
4000
- import { join as join23 } from "node:path";
4165
+ import { readFileSync as readFileSync15 } from "node:fs";
4166
+ import { join as join22 } from "node:path";
4001
4167
  import { homedir as homedir3 } from "node:os";
4002
4168
  function detectEnvMode() {
4003
- const versionPath = join23(
4004
- process.env["CLEO_HOME"] ?? join23(homedir3(), ".cleo"),
4169
+ const versionPath = join22(
4170
+ process.env["CLEO_HOME"] ?? join22(homedir3(), ".cleo"),
4005
4171
  "VERSION"
4006
4172
  );
4007
4173
  let content;
4008
4174
  try {
4009
- content = readFileSync16(versionPath, "utf-8");
4175
+ content = readFileSync15(versionPath, "utf-8");
4010
4176
  } catch {
4011
4177
  return { mode: "unknown", source: null };
4012
4178
  }
@@ -4029,13 +4195,13 @@ function generateMcpServerEntry(env) {
4029
4195
  if (env.mode === "dev-ts" && env.source) {
4030
4196
  return {
4031
4197
  command: "node",
4032
- args: [join23(env.source, "dist", "mcp", "index.js")],
4198
+ args: [join22(env.source, "dist", "mcp", "index.js")],
4033
4199
  env: {}
4034
4200
  };
4035
4201
  }
4036
4202
  return {
4037
- command: "cleo-mcp",
4038
- args: [],
4203
+ command: "npx",
4204
+ args: ["-y", "@cleocode/cleo@latest", "mcp"],
4039
4205
  env: {}
4040
4206
  };
4041
4207
  }
@@ -4068,17 +4234,17 @@ __export(registry_exports, {
4068
4234
  readRegistryRequired: () => readRegistryRequired
4069
4235
  });
4070
4236
  import { createHash as createHash4 } from "node:crypto";
4071
- import { join as join24 } from "node:path";
4237
+ import { join as join23 } from "node:path";
4072
4238
  import { mkdir as mkdir4, access, readFile as readFile4 } from "node:fs/promises";
4073
4239
  import { z } from "zod";
4074
4240
  function getNexusHome() {
4075
- return process.env["NEXUS_HOME"] ?? join24(getCleoHome(), "nexus");
4241
+ return process.env["NEXUS_HOME"] ?? join23(getCleoHome(), "nexus");
4076
4242
  }
4077
4243
  function getNexusCacheDir() {
4078
- return process.env["NEXUS_CACHE_DIR"] ?? join24(getNexusHome(), "cache");
4244
+ return process.env["NEXUS_CACHE_DIR"] ?? join23(getNexusHome(), "cache");
4079
4245
  }
4080
4246
  function getRegistryPath() {
4081
- return process.env["NEXUS_REGISTRY_FILE"] ?? join24(getCleoHome(), "projects-registry.json");
4247
+ return process.env["NEXUS_REGISTRY_FILE"] ?? join23(getCleoHome(), "projects-registry.json");
4082
4248
  }
4083
4249
  function generateProjectHash(projectPath) {
4084
4250
  const hash = createHash4("sha256").update(projectPath).digest("hex");
@@ -4120,7 +4286,7 @@ async function nexusInit() {
4120
4286
  }
4121
4287
  async function isCleoProject(projectPath) {
4122
4288
  try {
4123
- await access(join24(projectPath, ".cleo", "todo.json"));
4289
+ await access(join23(projectPath, ".cleo", "todo.json"));
4124
4290
  return true;
4125
4291
  } catch {
4126
4292
  return false;
@@ -4130,9 +4296,9 @@ async function readProjectMeta(projectPath) {
4130
4296
  try {
4131
4297
  let raw;
4132
4298
  try {
4133
- raw = await readFile4(join24(projectPath, ".cleo", "tasks.json"), "utf-8");
4299
+ raw = await readFile4(join23(projectPath, ".cleo", "tasks.json"), "utf-8");
4134
4300
  } catch {
4135
- raw = await readFile4(join24(projectPath, ".cleo", "todo.json"), "utf-8");
4301
+ raw = await readFile4(join23(projectPath, ".cleo", "todo.json"), "utf-8");
4136
4302
  }
4137
4303
  const data = JSON.parse(raw);
4138
4304
  const tasks2 = data.tasks ?? [];
@@ -4318,10 +4484,10 @@ var project_detect_exports = {};
4318
4484
  __export(project_detect_exports, {
4319
4485
  detectProjectType: () => detectProjectType
4320
4486
  });
4321
- import { existsSync as existsSync23, readdirSync as readdirSync6 } from "node:fs";
4322
- import { join as join25 } from "node:path";
4487
+ import { existsSync as existsSync22, readdirSync as readdirSync6 } from "node:fs";
4488
+ import { join as join24 } from "node:path";
4323
4489
  function detectProjectType(projectDir) {
4324
- const exists = (f) => existsSync23(join25(projectDir, f));
4490
+ const exists = (f) => existsSync22(join24(projectDir, f));
4325
4491
  const info = {
4326
4492
  type: "unknown",
4327
4493
  testFramework: "unknown",
@@ -4392,6 +4558,7 @@ __export(validation_schemas_exports, {
4392
4558
  insertLifecyclePipelineSchema: () => insertLifecyclePipelineSchema,
4393
4559
  insertLifecycleStageSchema: () => insertLifecycleStageSchema,
4394
4560
  insertLifecycleTransitionSchema: () => insertLifecycleTransitionSchema,
4561
+ insertManifestEntrySchema: () => insertManifestEntrySchema,
4395
4562
  insertSchemaMetaSchema: () => insertSchemaMetaSchema,
4396
4563
  insertSessionSchema: () => insertSessionSchema,
4397
4564
  insertTaskDependencySchema: () => insertTaskDependencySchema,
@@ -4405,6 +4572,7 @@ __export(validation_schemas_exports, {
4405
4572
  selectLifecyclePipelineSchema: () => selectLifecyclePipelineSchema,
4406
4573
  selectLifecycleStageSchema: () => selectLifecycleStageSchema,
4407
4574
  selectLifecycleTransitionSchema: () => selectLifecycleTransitionSchema,
4575
+ selectManifestEntrySchema: () => selectManifestEntrySchema,
4408
4576
  selectSchemaMetaSchema: () => selectSchemaMetaSchema,
4409
4577
  selectSessionSchema: () => selectSessionSchema,
4410
4578
  selectTaskDependencySchema: () => selectTaskDependencySchema,
@@ -4418,7 +4586,7 @@ __export(validation_schemas_exports, {
4418
4586
  });
4419
4587
  import { createInsertSchema, createSelectSchema } from "drizzle-orm/zod";
4420
4588
  import { z as z2 } from "zod/v4";
4421
- var taskRefinements, insertTaskSchema, selectTaskSchema, insertTaskDependencySchema, selectTaskDependencySchema, insertTaskRelationSchema, selectTaskRelationSchema, insertSessionSchema, selectSessionSchema, sessionScopeSchema, sessionStatsSchema, sessionTaskWorkSchema, sessionSchema, insertWorkHistorySchema, selectWorkHistorySchema, insertLifecyclePipelineSchema, selectLifecyclePipelineSchema, insertLifecycleStageSchema, selectLifecycleStageSchema, insertLifecycleGateResultSchema, selectLifecycleGateResultSchema, insertLifecycleEvidenceSchema, selectLifecycleEvidenceSchema, insertLifecycleTransitionSchema, selectLifecycleTransitionSchema, insertSchemaMetaSchema, selectSchemaMetaSchema, insertAuditLogSchema, AuditLogInsertSchema, selectAuditLogSchema, AuditLogSelectSchema, insertArchitectureDecisionSchema, selectArchitectureDecisionSchema;
4589
+ var taskRefinements, insertTaskSchema, selectTaskSchema, insertTaskDependencySchema, selectTaskDependencySchema, insertTaskRelationSchema, selectTaskRelationSchema, insertSessionSchema, selectSessionSchema, sessionScopeSchema, sessionStatsSchema, sessionTaskWorkSchema, sessionSchema, insertWorkHistorySchema, selectWorkHistorySchema, insertLifecyclePipelineSchema, selectLifecyclePipelineSchema, insertLifecycleStageSchema, selectLifecycleStageSchema, insertLifecycleGateResultSchema, selectLifecycleGateResultSchema, insertLifecycleEvidenceSchema, selectLifecycleEvidenceSchema, insertLifecycleTransitionSchema, selectLifecycleTransitionSchema, insertSchemaMetaSchema, selectSchemaMetaSchema, insertAuditLogSchema, AuditLogInsertSchema, selectAuditLogSchema, AuditLogSelectSchema, insertArchitectureDecisionSchema, selectArchitectureDecisionSchema, insertManifestEntrySchema, selectManifestEntrySchema;
4422
4590
  var init_validation_schemas = __esm({
4423
4591
  "src/store/validation-schemas.ts"() {
4424
4592
  "use strict";
@@ -4511,6 +4679,8 @@ var init_validation_schemas = __esm({
4511
4679
  AuditLogSelectSchema = selectAuditLogSchema;
4512
4680
  insertArchitectureDecisionSchema = createInsertSchema(architectureDecisions);
4513
4681
  selectArchitectureDecisionSchema = createSelectSchema(architectureDecisions);
4682
+ insertManifestEntrySchema = createInsertSchema(manifestEntries);
4683
+ selectManifestEntrySchema = createSelectSchema(manifestEntries);
4514
4684
  }
4515
4685
  });
4516
4686
 
@@ -4684,8 +4854,8 @@ __export(session_grade_exports, {
4684
4854
  gradeSession: () => gradeSession,
4685
4855
  readGrades: () => readGrades
4686
4856
  });
4687
- import { join as join42 } from "node:path";
4688
- import { existsSync as existsSync41 } from "node:fs";
4857
+ import { join as join41 } from "node:path";
4858
+ import { existsSync as existsSync40 } from "node:fs";
4689
4859
  import { readFile as readFile6, appendFile, mkdir as mkdir6 } from "node:fs/promises";
4690
4860
  async function gradeSession(sessionId, cwd) {
4691
4861
  const sessionEntries = await queryAudit({ sessionId });
@@ -4865,9 +5035,9 @@ function detectDuplicateCreates(entries) {
4865
5035
  async function appendGradeResult(result, cwd) {
4866
5036
  try {
4867
5037
  const cleoDir = getCleoDirAbsolute(cwd);
4868
- const metricsDir = join42(cleoDir, "metrics");
5038
+ const metricsDir = join41(cleoDir, "metrics");
4869
5039
  await mkdir6(metricsDir, { recursive: true });
4870
- const gradesPath = join42(metricsDir, "GRADES.jsonl");
5040
+ const gradesPath = join41(metricsDir, "GRADES.jsonl");
4871
5041
  const line = JSON.stringify({ ...result, evaluator: "auto" }) + "\n";
4872
5042
  await appendFile(gradesPath, line, "utf8");
4873
5043
  } catch {
@@ -4876,8 +5046,8 @@ async function appendGradeResult(result, cwd) {
4876
5046
  async function readGrades(sessionId, cwd) {
4877
5047
  try {
4878
5048
  const cleoDir = getCleoDirAbsolute(cwd);
4879
- const gradesPath = join42(cleoDir, "metrics", "GRADES.jsonl");
4880
- if (!existsSync41(gradesPath)) return [];
5049
+ const gradesPath = join41(cleoDir, "metrics", "GRADES.jsonl");
5050
+ if (!existsSync40(gradesPath)) return [];
4881
5051
  const content = await readFile6(gradesPath, "utf8");
4882
5052
  const results = content.split("\n").filter((l) => l.trim()).map((l) => JSON.parse(l));
4883
5053
  return sessionId ? results.filter((r) => r.sessionId === sessionId) : results;
@@ -5241,7 +5411,7 @@ if (actualQueryCount < 1) {
5241
5411
  function registerQueryTool() {
5242
5412
  return {
5243
5413
  name: "cleo_query",
5244
- description: "CLEO read operations: task discovery, status checks, analysis, validation, and compliance metrics. Never modifies state.",
5414
+ description: 'CLEO read operations: task discovery, status checks, analysis, validation, and compliance metrics. Never modifies state. First call: use domain "admin", operation "help" to discover all available operations.',
5245
5415
  inputSchema: {
5246
5416
  type: "object",
5247
5417
  required: ["domain", "operation"],
@@ -5253,7 +5423,7 @@ function registerQueryTool() {
5253
5423
  },
5254
5424
  operation: {
5255
5425
  type: "string",
5256
- description: "Domain-specific read operation (see operation matrix)"
5426
+ description: "Domain-specific read operation. Call admin.help to see the full operation matrix. Common: tasks.find, tasks.show, tasks.next, session.status, admin.dash"
5257
5427
  },
5258
5428
  params: {
5259
5429
  type: "object",
@@ -5563,7 +5733,7 @@ if (actualMutateCount < 1) {
5563
5733
  function registerMutateTool() {
5564
5734
  return {
5565
5735
  name: "cleo_mutate",
5566
- description: "CLEO write operations: create, update, complete tasks; manage sessions; spawn agents; progress lifecycle; execute releases. Modifies state with validation.",
5736
+ description: 'CLEO write operations: create, update, complete tasks; manage sessions; spawn agents; progress lifecycle; execute releases. Modifies state with validation. Use cleo_query with domain "admin", operation "help" first to discover available operations.',
5567
5737
  inputSchema: {
5568
5738
  type: "object",
5569
5739
  required: ["domain", "operation"],
@@ -5575,7 +5745,7 @@ function registerMutateTool() {
5575
5745
  },
5576
5746
  operation: {
5577
5747
  type: "string",
5578
- description: "Domain-specific write operation (see operation matrix)"
5748
+ description: "Domain-specific write operation. Call cleo_query admin.help to see the full operation matrix. Common: tasks.add, tasks.update, tasks.complete, session.start, session.end"
5579
5749
  },
5580
5750
  params: {
5581
5751
  type: "object",
@@ -7813,7 +7983,6 @@ init_data_accessor();
7813
7983
 
7814
7984
  // src/dispatch/engines/_error.ts
7815
7985
  init_logger();
7816
- var logger = getLogger("engine");
7817
7986
  var STRING_TO_EXIT = {
7818
7987
  // General Errors (1-9)
7819
7988
  E_GENERAL: 1,
@@ -7942,6 +8111,7 @@ function logLevel(exitCode) {
7942
8111
  function engineError(code, message, options) {
7943
8112
  const exitCode = STRING_TO_EXIT[code] ?? 1;
7944
8113
  const level = logLevel(exitCode);
8114
+ const logger = getLogger("engine");
7945
8115
  logger[level]({ code, exitCode, ...options?.details && { details: options.details } }, message);
7946
8116
  return {
7947
8117
  success: false,
@@ -8564,6 +8734,7 @@ function validateHierarchyPlacement(parentId, tasks2, policy) {
8564
8734
  }
8565
8735
 
8566
8736
  // src/core/tasks/add.ts
8737
+ init_sequence();
8567
8738
  function validateTitle(title) {
8568
8739
  if (!title || title.trim().length === 0) {
8569
8740
  throw new CleoError(2 /* INVALID_INPUT */, "Task title is required");
@@ -8746,20 +8917,23 @@ async function addTask(options, cwd, accessor) {
8746
8917
  validateTitle(options.title);
8747
8918
  const data = accessor ? await accessor.loadTaskFile() : await readJsonRequired(taskPath);
8748
8919
  let archivedTasks = [];
8749
- try {
8750
- if (accessor) {
8751
- const archive = await accessor.loadArchive();
8752
- if (archive?.archivedTasks) {
8753
- archivedTasks = archive.archivedTasks;
8754
- }
8755
- } else {
8756
- const { readJson: readJson2 } = await Promise.resolve().then(() => (init_json(), json_exports));
8757
- const archive = await readJson2(archivePath);
8758
- if (archive?.archivedTasks) {
8759
- archivedTasks = archive.archivedTasks;
8920
+ const useSqliteAlloc = accessor?.engine === "sqlite" || !accessor && (await Promise.resolve().then(() => (init_sqlite(), sqlite_exports))).getNativeDb() !== null;
8921
+ if (!useSqliteAlloc) {
8922
+ try {
8923
+ if (accessor) {
8924
+ const archive = await accessor.loadArchive();
8925
+ if (archive?.archivedTasks) {
8926
+ archivedTasks = archive.archivedTasks;
8927
+ }
8928
+ } else {
8929
+ const { readJson: readJson2 } = await Promise.resolve().then(() => (init_json(), json_exports));
8930
+ const archive = await readJson2(archivePath);
8931
+ if (archive?.archivedTasks) {
8932
+ archivedTasks = archive.archivedTasks;
8933
+ }
8760
8934
  }
8935
+ } catch {
8761
8936
  }
8762
- } catch {
8763
8937
  }
8764
8938
  const status = options.status ?? "pending";
8765
8939
  const priority = normalizePriority(options.priority ?? "medium");
@@ -8837,9 +9011,14 @@ async function addTask(options, cwd, accessor) {
8837
9011
  if (duplicate) {
8838
9012
  return { task: duplicate, duplicate: true };
8839
9013
  }
8840
- const taskId = generateTaskId(data.tasks, archivedTasks);
8841
- if (data.tasks.some((t) => t.id === taskId)) {
8842
- throw new CleoError(22 /* ID_COLLISION */, `Generated ID ${taskId} already exists`);
9014
+ let taskId;
9015
+ if (useSqliteAlloc) {
9016
+ taskId = await allocateNextTaskId(cwd);
9017
+ } else {
9018
+ taskId = generateTaskId(data.tasks, archivedTasks);
9019
+ if (data.tasks.some((t) => t.id === taskId)) {
9020
+ throw new CleoError(22 /* ID_COLLISION */, `Generated ID ${taskId} already exists`);
9021
+ }
8843
9022
  }
8844
9023
  const now = (/* @__PURE__ */ new Date()).toISOString();
8845
9024
  const position = options.position ?? getNextPosition(parentId, data.tasks);
@@ -9193,6 +9372,291 @@ async function updateTask2(options, cwd, accessor) {
9193
9372
  return { task, changes };
9194
9373
  }
9195
9374
 
9375
+ // src/core/tasks/complete.ts
9376
+ init_json();
9377
+ init_errors();
9378
+ init_exit_codes();
9379
+ init_paths();
9380
+
9381
+ // src/core/tasks/dependency-check.ts
9382
+ function detectCircularDeps(taskId, tasks2) {
9383
+ const taskMap = new Map(tasks2.map((t) => [t.id, t]));
9384
+ const visited = /* @__PURE__ */ new Set();
9385
+ const recursionStack = /* @__PURE__ */ new Set();
9386
+ const path = [];
9387
+ function dfs(id) {
9388
+ visited.add(id);
9389
+ recursionStack.add(id);
9390
+ path.push(id);
9391
+ const task = taskMap.get(id);
9392
+ if (task?.depends) {
9393
+ for (const depId of task.depends) {
9394
+ if (!visited.has(depId)) {
9395
+ const cycle = dfs(depId);
9396
+ if (cycle.length > 0) return cycle;
9397
+ } else if (recursionStack.has(depId)) {
9398
+ const cycleStart = path.indexOf(depId);
9399
+ return [...path.slice(cycleStart), depId];
9400
+ }
9401
+ }
9402
+ }
9403
+ path.pop();
9404
+ recursionStack.delete(id);
9405
+ return [];
9406
+ }
9407
+ return dfs(taskId);
9408
+ }
9409
+ function getBlockedTasks(tasks2) {
9410
+ const completedIds = new Set(
9411
+ tasks2.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
9412
+ );
9413
+ return tasks2.filter((t) => {
9414
+ if (!t.depends?.length) return false;
9415
+ if (t.status === "done" || t.status === "cancelled") return false;
9416
+ return t.depends.some((depId) => !completedIds.has(depId));
9417
+ });
9418
+ }
9419
+ function getReadyTasks(tasks2) {
9420
+ const completedIds = new Set(
9421
+ tasks2.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
9422
+ );
9423
+ return tasks2.filter((t) => {
9424
+ if (t.status === "done" || t.status === "cancelled") return false;
9425
+ if (!t.depends?.length) return true;
9426
+ return t.depends.every((depId) => completedIds.has(depId));
9427
+ });
9428
+ }
9429
+ function getDependents(taskId, tasks2) {
9430
+ return tasks2.filter((t) => t.depends?.includes(taskId));
9431
+ }
9432
+ function getUnresolvedDeps(taskId, tasks2) {
9433
+ const task = tasks2.find((t) => t.id === taskId);
9434
+ if (!task?.depends?.length) return [];
9435
+ const completedIds = new Set(
9436
+ tasks2.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
9437
+ );
9438
+ return task.depends.filter((depId) => !completedIds.has(depId));
9439
+ }
9440
+ function validateDependencyRefs(tasks2) {
9441
+ const taskIds = new Set(tasks2.map((t) => t.id));
9442
+ const errors = [];
9443
+ for (const task of tasks2) {
9444
+ if (!task.depends?.length) continue;
9445
+ for (const depId of task.depends) {
9446
+ if (!taskIds.has(depId)) {
9447
+ errors.push({
9448
+ code: "E_DEP_NOT_FOUND",
9449
+ taskId: task.id,
9450
+ message: `Task ${task.id} depends on ${depId}, which does not exist`,
9451
+ relatedIds: [depId]
9452
+ });
9453
+ }
9454
+ }
9455
+ }
9456
+ return errors;
9457
+ }
9458
+ function validateDependencies(tasks2) {
9459
+ const errors = [];
9460
+ const warnings = [];
9461
+ errors.push(...validateDependencyRefs(tasks2));
9462
+ const visited = /* @__PURE__ */ new Set();
9463
+ for (const task of tasks2) {
9464
+ if (visited.has(task.id)) continue;
9465
+ if (!task.depends?.length) continue;
9466
+ const cycle = detectCircularDeps(task.id, tasks2);
9467
+ if (cycle.length > 0) {
9468
+ errors.push({
9469
+ code: "E_CIRCULAR_DEP",
9470
+ taskId: task.id,
9471
+ message: `Circular dependency detected: ${cycle.join(" -> ")}`,
9472
+ relatedIds: cycle
9473
+ });
9474
+ cycle.forEach((id) => visited.add(id));
9475
+ }
9476
+ }
9477
+ for (const task of tasks2) {
9478
+ if (task.depends?.includes(task.id)) {
9479
+ errors.push({
9480
+ code: "E_SELF_DEP",
9481
+ taskId: task.id,
9482
+ message: `Task ${task.id} depends on itself`
9483
+ });
9484
+ }
9485
+ }
9486
+ for (const task of tasks2) {
9487
+ if (task.status === "done" && task.depends?.length) {
9488
+ const unresolved = getUnresolvedDeps(task.id, tasks2);
9489
+ if (unresolved.length > 0) {
9490
+ warnings.push({
9491
+ code: "W_COMPLETED_WITH_UNMET_DEPS",
9492
+ taskId: task.id,
9493
+ message: `Completed task ${task.id} has unmet dependencies: ${unresolved.join(", ")}`
9494
+ });
9495
+ }
9496
+ }
9497
+ }
9498
+ return {
9499
+ valid: errors.length === 0,
9500
+ errors,
9501
+ warnings
9502
+ };
9503
+ }
9504
+ function getTransitiveBlockers(taskId, tasks2) {
9505
+ const taskMap = new Map(tasks2.map((t) => [t.id, t]));
9506
+ const task = taskMap.get(taskId);
9507
+ if (!task?.depends?.length) return [];
9508
+ const blockers = /* @__PURE__ */ new Set();
9509
+ const visited = /* @__PURE__ */ new Set();
9510
+ function walk(id) {
9511
+ if (visited.has(id)) return;
9512
+ visited.add(id);
9513
+ const t = taskMap.get(id);
9514
+ if (!t?.depends?.length) return;
9515
+ for (const depId of t.depends) {
9516
+ const dep = taskMap.get(depId);
9517
+ if (!dep) continue;
9518
+ if (dep.status === "done" || dep.status === "cancelled") continue;
9519
+ blockers.add(depId);
9520
+ walk(depId);
9521
+ }
9522
+ }
9523
+ walk(taskId);
9524
+ return [...blockers];
9525
+ }
9526
+ function getLeafBlockers(taskId, tasks2) {
9527
+ const blockerIds = getTransitiveBlockers(taskId, tasks2);
9528
+ if (blockerIds.length === 0) return [];
9529
+ const taskMap = new Map(tasks2.map((t) => [t.id, t]));
9530
+ const completedIds = new Set(
9531
+ tasks2.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
9532
+ );
9533
+ return blockerIds.filter((id) => {
9534
+ const t = taskMap.get(id);
9535
+ if (!t?.depends?.length) return true;
9536
+ return t.depends.every((depId) => completedIds.has(depId));
9537
+ });
9538
+ }
9539
+
9540
+ // src/core/tasks/complete.ts
9541
+ init_data_safety_central();
9542
+ async function completeTask(options, cwd, accessor) {
9543
+ const taskPath = getTaskPath(cwd);
9544
+ const logPath = getLogPath(cwd);
9545
+ const backupDir = getBackupDir(cwd);
9546
+ const data = accessor ? await accessor.loadTaskFile() : await readJsonRequired(taskPath);
9547
+ const taskIdx = data.tasks.findIndex((t) => t.id === options.taskId);
9548
+ if (taskIdx === -1) {
9549
+ throw new CleoError(
9550
+ 4 /* NOT_FOUND */,
9551
+ `Task not found: ${options.taskId}`,
9552
+ { fix: `Use 'cleo find "${options.taskId}"' to search` }
9553
+ );
9554
+ }
9555
+ const task = data.tasks[taskIdx];
9556
+ if (task.status === "done") {
9557
+ throw new CleoError(
9558
+ 17 /* TASK_COMPLETED */,
9559
+ `Task ${options.taskId} is already completed`
9560
+ );
9561
+ }
9562
+ if (task.depends?.length) {
9563
+ const incompleteDeps = task.depends.filter((depId) => {
9564
+ const dep = data.tasks.find((t) => t.id === depId);
9565
+ return dep && dep.status !== "done";
9566
+ });
9567
+ if (incompleteDeps.length > 0) {
9568
+ throw new CleoError(
9569
+ 5 /* DEPENDENCY_ERROR */,
9570
+ `Task ${options.taskId} has incomplete dependencies: ${incompleteDeps.join(", ")}`,
9571
+ { fix: `Complete dependencies first: ${incompleteDeps.map((d) => `cleo complete ${d}`).join(", ")}` }
9572
+ );
9573
+ }
9574
+ }
9575
+ const children = data.tasks.filter((t) => t.parentId === options.taskId);
9576
+ const incompleteChildren = children.filter((c) => c.status !== "done" && c.status !== "cancelled");
9577
+ if (incompleteChildren.length > 0 && task.type === "epic") {
9578
+ if (!task.noAutoComplete) {
9579
+ throw new CleoError(
9580
+ 16 /* HAS_CHILDREN */,
9581
+ `Epic ${options.taskId} has ${incompleteChildren.length} incomplete children: ${incompleteChildren.map((c) => c.id).join(", ")}`,
9582
+ { fix: `Complete children first or use 'cleo update ${options.taskId} --no-auto-complete'` }
9583
+ );
9584
+ }
9585
+ }
9586
+ const now = (/* @__PURE__ */ new Date()).toISOString();
9587
+ const before = { ...task };
9588
+ task.status = "done";
9589
+ task.completedAt = now;
9590
+ task.updatedAt = now;
9591
+ if (options.notes) {
9592
+ const timestampedNote = `${(/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC")}: ${options.notes}`;
9593
+ if (!task.notes) task.notes = [];
9594
+ task.notes.push(timestampedNote);
9595
+ }
9596
+ if (options.changeset) {
9597
+ if (!task.notes) task.notes = [];
9598
+ task.notes.push(`Changeset: ${options.changeset}`);
9599
+ }
9600
+ data.tasks[taskIdx] = task;
9601
+ const autoCompleted = [];
9602
+ if (task.parentId) {
9603
+ const parent = data.tasks.find((t) => t.id === task.parentId);
9604
+ if (parent && parent.type === "epic" && !parent.noAutoComplete) {
9605
+ const parentChildren = data.tasks.filter((t) => t.parentId === parent.id);
9606
+ const allDone = parentChildren.every((c) => c.status === "done" || c.status === "cancelled");
9607
+ if (allDone) {
9608
+ parent.status = "done";
9609
+ parent.completedAt = now;
9610
+ parent.updatedAt = now;
9611
+ autoCompleted.push(parent.id);
9612
+ }
9613
+ }
9614
+ }
9615
+ data._meta.checksum = computeChecksum(data.tasks);
9616
+ data.lastUpdated = now;
9617
+ if (accessor) {
9618
+ if (accessor.upsertSingleTask) {
9619
+ await accessor.upsertSingleTask(task);
9620
+ for (const parentId of autoCompleted) {
9621
+ const parent = data.tasks.find((t) => t.id === parentId);
9622
+ if (parent) await accessor.upsertSingleTask(parent);
9623
+ }
9624
+ } else {
9625
+ await safeSaveTaskFile(accessor, data, cwd);
9626
+ }
9627
+ await safeAppendLog(accessor, {
9628
+ id: `log-${Math.floor(Date.now() / 1e3)}-${(await import("node:crypto")).randomBytes(3).toString("hex")}`,
9629
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
9630
+ action: "task_completed",
9631
+ taskId: options.taskId,
9632
+ actor: "system",
9633
+ details: { title: task.title, previousStatus: before.status },
9634
+ before: null,
9635
+ after: { title: task.title, previousStatus: before.status }
9636
+ }, cwd);
9637
+ } else {
9638
+ await saveJson(taskPath, data, { backupDir });
9639
+ await logOperation(logPath, "task_completed", options.taskId, {
9640
+ title: task.title,
9641
+ previousStatus: before.status
9642
+ });
9643
+ }
9644
+ const dependents = getDependents(options.taskId, data.tasks);
9645
+ const unblockedTasks = [];
9646
+ for (const dep of dependents) {
9647
+ if (dep.status === "done" || dep.status === "cancelled") continue;
9648
+ const stillUnresolved = getUnresolvedDeps(dep.id, data.tasks);
9649
+ if (stillUnresolved.length === 0) {
9650
+ unblockedTasks.push({ id: dep.id, title: dep.title });
9651
+ }
9652
+ }
9653
+ return {
9654
+ task,
9655
+ ...autoCompleted.length > 0 && { autoCompleted },
9656
+ ...unblockedTasks.length > 0 && { unblockedTasks }
9657
+ };
9658
+ }
9659
+
9196
9660
  // src/core/tasks/delete.ts
9197
9661
  init_json();
9198
9662
  init_errors();
@@ -9498,6 +9962,16 @@ async function showTask(taskId, cwd, accessor) {
9498
9962
  title: dep?.title ?? "Unknown task"
9499
9963
  };
9500
9964
  });
9965
+ const unresolved = detail.dependencyStatus.filter(
9966
+ (d) => d.status !== "done" && d.status !== "cancelled"
9967
+ );
9968
+ if (unresolved.length > 0) {
9969
+ detail.unresolvedDeps = unresolved;
9970
+ }
9971
+ }
9972
+ const dependents = data.tasks.filter((t) => t.depends?.includes(taskId)).map((t) => t.id);
9973
+ if (dependents.length > 0) {
9974
+ detail.dependents = dependents;
9501
9975
  }
9502
9976
  const path = [taskId];
9503
9977
  let currentId = task.parentId;
@@ -9518,6 +9992,16 @@ async function showTask(taskId, cwd, accessor) {
9518
9992
  // src/core/tasks/list.ts
9519
9993
  init_json();
9520
9994
  init_paths();
9995
+ function toCompact(task) {
9996
+ return {
9997
+ id: task.id,
9998
+ title: task.title,
9999
+ status: task.status,
10000
+ priority: task.priority,
10001
+ type: task.type,
10002
+ parentId: task.parentId
10003
+ };
10004
+ }
9521
10005
  async function listTasks(options = {}, cwd, accessor) {
9522
10006
  const taskPath = getTaskPath(cwd);
9523
10007
  const data = accessor ? await accessor.loadTaskFile() : await readJsonRequired(taskPath);
@@ -9694,34 +10178,19 @@ async function findTasks(options, cwd, accessor) {
9694
10178
 
9695
10179
  // src/core/tasks/task-ops.ts
9696
10180
  init_data_accessor();
9697
- init_store();
10181
+ init_file_utils();
9698
10182
  init_status_registry();
9699
10183
  init_deps_ready();
9700
10184
  var PRIORITY_SCORE = {
9701
10185
  critical: 100,
9702
10186
  high: 75,
9703
- medium: 50,
9704
- low: 25
9705
- };
9706
- async function loadAllTasks2(projectRoot) {
9707
- const accessor = await getAccessor(projectRoot);
9708
- const data = await accessor.loadTaskFile();
9709
- return data.tasks;
9710
- }
9711
- function buildBlockingChain(task, taskMap, visited = /* @__PURE__ */ new Set()) {
9712
- const chain = [];
9713
- if (visited.has(task.id)) return chain;
9714
- visited.add(task.id);
9715
- if (task.depends) {
9716
- for (const depId of task.depends) {
9717
- const dep = taskMap.get(depId);
9718
- if (dep && dep.status !== "done" && dep.status !== "cancelled") {
9719
- chain.push(depId);
9720
- chain.push(...buildBlockingChain(dep, taskMap, visited));
9721
- }
9722
- }
9723
- }
9724
- return chain;
10187
+ medium: 50,
10188
+ low: 25
10189
+ };
10190
+ async function loadAllTasks2(projectRoot) {
10191
+ const accessor = await getAccessor(projectRoot);
10192
+ const data = await accessor.loadTaskFile();
10193
+ return data.tasks;
9725
10194
  }
9726
10195
  function buildTreeNode(task, childrenMap) {
9727
10196
  const children = (childrenMap.get(task.id) ?? []).map(
@@ -9735,6 +10204,25 @@ function buildTreeNode(task, childrenMap) {
9735
10204
  children
9736
10205
  };
9737
10206
  }
10207
+ function buildUpstreamTree(taskId, taskMap, visited = /* @__PURE__ */ new Set()) {
10208
+ const task = taskMap.get(taskId);
10209
+ if (!task?.depends?.length) return [];
10210
+ const nodes = [];
10211
+ for (const depId of task.depends) {
10212
+ if (visited.has(depId)) continue;
10213
+ visited.add(depId);
10214
+ const dep = taskMap.get(depId);
10215
+ if (!dep) continue;
10216
+ nodes.push({
10217
+ id: dep.id,
10218
+ title: dep.title,
10219
+ status: dep.status,
10220
+ type: dep.type,
10221
+ children: buildUpstreamTree(depId, taskMap, visited)
10222
+ });
10223
+ }
10224
+ return nodes;
10225
+ }
9738
10226
  function countNodes(nodes) {
9739
10227
  let count2 = nodes.length;
9740
10228
  for (const node of nodes) {
@@ -9827,20 +10315,21 @@ async function coreTaskBlockers(projectRoot, params) {
9827
10315
  return dep && dep.status !== "done" && dep.status !== "cancelled";
9828
10316
  })
9829
10317
  );
10318
+ const tasksAsTask = allTasks;
9830
10319
  const blockerInfos = [
9831
10320
  ...blockedTasks.map((t) => ({
9832
10321
  id: t.id,
9833
10322
  title: t.title,
9834
10323
  status: t.status,
9835
10324
  depends: t.depends,
9836
- blockingChain: analyze ? buildBlockingChain(t, taskMap) : []
10325
+ blockingChain: analyze ? getTransitiveBlockers(t.id, tasksAsTask) : []
9837
10326
  })),
9838
10327
  ...depBlockedTasks.filter((t) => !blockedTasks.some((bt) => bt.id === t.id)).map((t) => ({
9839
10328
  id: t.id,
9840
10329
  title: t.title,
9841
10330
  status: t.status,
9842
10331
  depends: t.depends,
9843
- blockingChain: analyze ? buildBlockingChain(t, taskMap) : []
10332
+ blockingChain: analyze ? getTransitiveBlockers(t.id, tasksAsTask) : []
9844
10333
  }))
9845
10334
  ];
9846
10335
  const blockerCounts = /* @__PURE__ */ new Map();
@@ -9922,7 +10411,10 @@ async function coreTaskRelatesAdd(projectRoot, taskId, relatedId, type, reason)
9922
10411
  } else {
9923
10412
  await accessor.saveTaskFile(current);
9924
10413
  }
9925
- return { from: taskId, to: relatedId, type, added: true };
10414
+ if (accessor.addRelation) {
10415
+ await accessor.addRelation(taskId, relatedId, type, reason);
10416
+ }
10417
+ return { from: taskId, to: relatedId, type, reason, added: true };
9926
10418
  }
9927
10419
  async function coreTaskAnalyze(projectRoot, taskId) {
9928
10420
  const allTasks = await loadAllTasks2(projectRoot);
@@ -10242,7 +10734,63 @@ async function coreTaskComplexityEstimate(projectRoot, params) {
10242
10734
  else size = "large";
10243
10735
  return { size, score, factors, dependencyDepth, subtaskCount, fileCount };
10244
10736
  }
10245
- async function coreTaskDepends(projectRoot, taskId, direction = "both") {
10737
+ async function coreTaskDepsOverview(projectRoot) {
10738
+ const allTasks = await loadAllTasks2(projectRoot);
10739
+ const tasksAsTask = allTasks;
10740
+ const tasksWithDeps = allTasks.filter((t) => t.depends && t.depends.length > 0);
10741
+ const blocked = getBlockedTasks(tasksAsTask);
10742
+ const ready = getReadyTasks(tasksAsTask);
10743
+ const validation = validateDependencies(tasksAsTask);
10744
+ return {
10745
+ totalTasks: allTasks.length,
10746
+ tasksWithDeps: tasksWithDeps.length,
10747
+ blockedTasks: blocked.map((t) => ({
10748
+ id: t.id,
10749
+ title: t.title,
10750
+ status: t.status,
10751
+ unblockedBy: (t.depends ?? []).filter((depId) => {
10752
+ const dep = allTasks.find((x) => x.id === depId);
10753
+ return dep && dep.status !== "done" && dep.status !== "cancelled";
10754
+ })
10755
+ })),
10756
+ readyTasks: ready.filter((t) => t.status !== "done" && t.status !== "cancelled").map((t) => ({
10757
+ id: t.id,
10758
+ title: t.title,
10759
+ status: t.status
10760
+ })),
10761
+ validation: {
10762
+ valid: validation.valid,
10763
+ errorCount: validation.errors.length,
10764
+ warningCount: validation.warnings.length
10765
+ }
10766
+ };
10767
+ }
10768
+ async function coreTaskDepsCycles(projectRoot) {
10769
+ const allTasks = await loadAllTasks2(projectRoot);
10770
+ const tasksAsTask = allTasks;
10771
+ const taskMap = new Map(allTasks.map((t) => [t.id, t]));
10772
+ const visited = /* @__PURE__ */ new Set();
10773
+ const cycles = [];
10774
+ for (const task of allTasks) {
10775
+ if (visited.has(task.id)) continue;
10776
+ if (!task.depends?.length) continue;
10777
+ const cycle = detectCircularDeps(task.id, tasksAsTask);
10778
+ if (cycle.length > 0) {
10779
+ cycles.push({
10780
+ path: cycle,
10781
+ // Deduplicate: detectCircularDeps returns [A,B,C,A] where
10782
+ // last element closes the cycle. Use Set for robustness.
10783
+ tasks: [...new Set(cycle)].map((id) => {
10784
+ const t = taskMap.get(id);
10785
+ return { id, title: t?.title ?? "unknown" };
10786
+ })
10787
+ });
10788
+ cycle.forEach((id) => visited.add(id));
10789
+ }
10790
+ }
10791
+ return { hasCycles: cycles.length > 0, cycles };
10792
+ }
10793
+ async function coreTaskDepends(projectRoot, taskId, direction = "both", options) {
10246
10794
  const allTasks = await loadAllTasks2(projectRoot);
10247
10795
  const task = allTasks.find((t) => t.id === taskId);
10248
10796
  if (!task) {
@@ -10266,7 +10814,31 @@ async function coreTaskDepends(projectRoot, taskId, direction = "both") {
10266
10814
  }
10267
10815
  }
10268
10816
  }
10269
- return { taskId, direction, upstream, downstream };
10817
+ const tasksAsTask = allTasks;
10818
+ const transitiveIds = getTransitiveBlockers(taskId, tasksAsTask);
10819
+ const unresolvedChain = transitiveIds.length;
10820
+ const leafIds = getLeafBlockers(taskId, tasksAsTask);
10821
+ const leafBlockers = leafIds.map((id) => {
10822
+ const t = taskMap.get(id);
10823
+ return { id: t.id, title: t.title, status: t.status };
10824
+ });
10825
+ const allDepsReady = unresolvedChain === 0;
10826
+ const hint = unresolvedChain > 0 ? `Run 'ct deps show ${taskId} --tree' for full dependency graph` : void 0;
10827
+ let upstreamTree;
10828
+ if (options?.tree) {
10829
+ upstreamTree = buildUpstreamTree(taskId, taskMap);
10830
+ }
10831
+ return {
10832
+ taskId,
10833
+ direction,
10834
+ upstream,
10835
+ downstream,
10836
+ unresolvedChain,
10837
+ leafBlockers,
10838
+ allDepsReady,
10839
+ ...hint && { hint },
10840
+ ...upstreamTree && { upstreamTree }
10841
+ };
10270
10842
  }
10271
10843
 
10272
10844
  // src/dispatch/engines/task-engine.ts
@@ -10300,17 +10872,25 @@ async function taskList(projectRoot, params) {
10300
10872
  status: params?.status,
10301
10873
  limit: params?.limit
10302
10874
  }, projectRoot, accessor);
10875
+ if (params?.compact) {
10876
+ return { success: true, data: { tasks: result.tasks.map((t) => toCompact(t)), total: result.total } };
10877
+ }
10303
10878
  return { success: true, data: { tasks: tasksToRecords(result.tasks), total: result.total } };
10304
10879
  } catch {
10305
10880
  return engineError("E_NOT_INITIALIZED", "Task database not initialized");
10306
10881
  }
10307
10882
  }
10308
- async function taskFind(projectRoot, query, limit) {
10883
+ async function taskFind(projectRoot, query, limit, options) {
10309
10884
  try {
10310
10885
  const accessor = await getAccessor(projectRoot);
10311
10886
  const findResult = await findTasks({
10312
10887
  query,
10313
- limit: limit ?? 20
10888
+ id: options?.id,
10889
+ exact: options?.exact,
10890
+ status: options?.status,
10891
+ includeArchive: options?.includeArchive,
10892
+ limit: limit ?? 20,
10893
+ offset: options?.offset
10314
10894
  }, projectRoot, accessor);
10315
10895
  const results = findResult.results.map((r) => ({
10316
10896
  id: r.id,
@@ -10413,14 +10993,33 @@ async function taskUpdate(projectRoot, taskId, updates) {
10413
10993
  }
10414
10994
  }
10415
10995
  async function taskComplete(projectRoot, taskId, notes) {
10416
- const showResult = await taskShow(projectRoot, taskId);
10417
- if (showResult.success && showResult.data?.task?.status === "done") {
10418
- return engineError("E_TASK_COMPLETED", `Task ${taskId} is already completed`);
10996
+ try {
10997
+ const accessor = await getAccessor(projectRoot);
10998
+ const result = await completeTask({ taskId, notes }, void 0, accessor);
10999
+ return {
11000
+ success: true,
11001
+ data: {
11002
+ task: result.task,
11003
+ ...result.autoCompleted && { autoCompleted: result.autoCompleted },
11004
+ ...result.unblockedTasks && { unblockedTasks: result.unblockedTasks }
11005
+ }
11006
+ };
11007
+ } catch (err) {
11008
+ const message = err.message;
11009
+ if (message.includes("already completed")) {
11010
+ return engineError("E_TASK_COMPLETED", message);
11011
+ }
11012
+ if (message.includes("not found")) {
11013
+ return engineError("E_NOT_FOUND", message);
11014
+ }
11015
+ if (message.includes("incomplete dependencies")) {
11016
+ return engineError("E_DEPENDENCY_ERROR", message);
11017
+ }
11018
+ if (message.includes("incomplete children")) {
11019
+ return engineError("E_HAS_CHILDREN", message);
11020
+ }
11021
+ return engineError("E_INTERNAL", message);
10419
11022
  }
10420
- return taskUpdate(projectRoot, taskId, {
10421
- status: "done",
10422
- notes: notes || void 0
10423
- });
10424
11023
  }
10425
11024
  async function taskDelete(projectRoot, taskId, force) {
10426
11025
  try {
@@ -10625,9 +11224,9 @@ async function taskComplexityEstimate(projectRoot, params) {
10625
11224
  return engineError("E_NOT_INITIALIZED", "Task database not initialized");
10626
11225
  }
10627
11226
  }
10628
- async function taskDepends(projectRoot, taskId, direction = "both") {
11227
+ async function taskDepends(projectRoot, taskId, direction = "both", tree) {
10629
11228
  try {
10630
- const result = await coreTaskDepends(projectRoot, taskId, direction);
11229
+ const result = await coreTaskDepends(projectRoot, taskId, direction, tree ? { tree } : void 0);
10631
11230
  return { success: true, data: result };
10632
11231
  } catch (err) {
10633
11232
  const message = err instanceof Error ? err.message : String(err);
@@ -10637,6 +11236,24 @@ async function taskDepends(projectRoot, taskId, direction = "both") {
10637
11236
  return engineError("E_NOT_INITIALIZED", "Task database not initialized");
10638
11237
  }
10639
11238
  }
11239
+ async function taskDepsOverview(projectRoot) {
11240
+ try {
11241
+ const result = await coreTaskDepsOverview(projectRoot);
11242
+ return { success: true, data: result };
11243
+ } catch (err) {
11244
+ const message = err instanceof Error ? err.message : String(err);
11245
+ return engineError("E_NOT_INITIALIZED", message);
11246
+ }
11247
+ }
11248
+ async function taskDepsCycles(projectRoot) {
11249
+ try {
11250
+ const result = await coreTaskDepsCycles(projectRoot);
11251
+ return { success: true, data: result };
11252
+ } catch (err) {
11253
+ const message = err instanceof Error ? err.message : String(err);
11254
+ return engineError("E_NOT_INITIALIZED", message);
11255
+ }
11256
+ }
10640
11257
  async function taskPlan(projectRoot) {
10641
11258
  const { coreTaskPlan: coreTaskPlan2 } = await Promise.resolve().then(() => (init_plan(), plan_exports));
10642
11259
  try {
@@ -10894,7 +11511,7 @@ init_decisions();
10894
11511
  init_data_accessor();
10895
11512
  init_errors();
10896
11513
  init_exit_codes();
10897
- import { randomBytes as randomBytes3 } from "node:crypto";
11514
+ import { randomBytes as randomBytes4 } from "node:crypto";
10898
11515
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync5, existsSync as existsSync10 } from "node:fs";
10899
11516
  import { join as join10 } from "node:path";
10900
11517
  async function recordAssumption(projectRoot, params) {
@@ -10914,7 +11531,7 @@ async function recordAssumption(projectRoot, params) {
10914
11531
  const taskData = await accessor.loadTaskFile();
10915
11532
  const current = taskData;
10916
11533
  const sessionId = params.sessionId || current._meta?.activeSession || "default";
10917
- const id = `asm-${randomBytes3(8).toString("hex")}`;
11534
+ const id = `asm-${randomBytes4(8).toString("hex")}`;
10918
11535
  const now = (/* @__PURE__ */ new Date()).toISOString();
10919
11536
  const record = {
10920
11537
  id,
@@ -10981,6 +11598,12 @@ async function computeBriefing(projectRoot, options = {}) {
10981
11598
  scopeTaskIds
10982
11599
  });
10983
11600
  const pipelineStage = computePipelineStage(current);
11601
+ const warnings = [];
11602
+ if (currentTaskInfo?.blockedBy?.length) {
11603
+ warnings.push(
11604
+ `Focused task ${currentTaskInfo.id} is blocked by: ${currentTaskInfo.blockedBy.join(", ")}`
11605
+ );
11606
+ }
10984
11607
  return {
10985
11608
  lastSession,
10986
11609
  currentTask: currentTaskInfo,
@@ -10988,7 +11611,8 @@ async function computeBriefing(projectRoot, options = {}) {
10988
11611
  openBugs,
10989
11612
  blockedTasks,
10990
11613
  activeEpics,
10991
- ...pipelineStage && { pipelineStage }
11614
+ ...pipelineStage && { pipelineStage },
11615
+ ...warnings.length > 0 && { warnings }
10992
11616
  };
10993
11617
  }
10994
11618
  function parseScope(scopeStr, current) {
@@ -11076,11 +11700,21 @@ function computeCurrentTask(current, taskMap) {
11076
11700
  if (!focusTaskId) return null;
11077
11701
  const task = taskMap.get(focusTaskId);
11078
11702
  if (!task) return null;
11079
- return {
11703
+ const info = {
11080
11704
  id: task.id,
11081
11705
  title: task.title,
11082
11706
  status: task.status
11083
11707
  };
11708
+ if (task.depends?.length) {
11709
+ const unresolved = task.depends.filter((depId) => {
11710
+ const dep = taskMap.get(depId);
11711
+ return dep && dep.status !== "done" && dep.status !== "cancelled";
11712
+ });
11713
+ if (unresolved.length > 0) {
11714
+ info.blockedBy = unresolved;
11715
+ }
11716
+ }
11717
+ return info;
11084
11718
  }
11085
11719
  function calculateLeverage2(taskId, taskMap) {
11086
11720
  let leverage = 0;
@@ -11227,11 +11861,11 @@ function computePipelineStage(current) {
11227
11861
  }
11228
11862
 
11229
11863
  // src/core/sessions/session-id.ts
11230
- import { randomBytes as randomBytes4 } from "node:crypto";
11864
+ import { randomBytes as randomBytes5 } from "node:crypto";
11231
11865
  function generateSessionId() {
11232
11866
  const now = /* @__PURE__ */ new Date();
11233
11867
  const ts = now.toISOString().replace(/[-:T]/g, "").substring(0, 14);
11234
- const hex = randomBytes4(3).toString("hex");
11868
+ const hex = randomBytes5(3).toString("hex");
11235
11869
  return `ses_${ts}_${hex}`;
11236
11870
  }
11237
11871
 
@@ -11269,6 +11903,14 @@ async function startTask(taskId, cwd, accessor) {
11269
11903
  { fix: `Use 'cleo find "${taskId}"' to search` }
11270
11904
  );
11271
11905
  }
11906
+ const unresolvedDeps = getUnresolvedDeps(taskId, data.tasks);
11907
+ if (unresolvedDeps.length > 0) {
11908
+ throw new CleoError(
11909
+ 5 /* DEPENDENCY_ERROR */,
11910
+ `Task ${taskId} is blocked by unresolved dependencies: ${unresolvedDeps.join(", ")}`,
11911
+ { fix: `Complete blockers first: ${unresolvedDeps.map((d) => `cleo complete ${d}`).join(", ")}` }
11912
+ );
11913
+ }
11272
11914
  const previousTask = data.focus?.currentTask ?? null;
11273
11915
  if (!data.focus) {
11274
11916
  data.focus = {};
@@ -11327,6 +11969,8 @@ async function stopTask(cwd, accessor) {
11327
11969
  }
11328
11970
 
11329
11971
  // src/dispatch/engines/session-engine.ts
11972
+ init_errors();
11973
+ init_exit_codes();
11330
11974
  async function sessionStatus(projectRoot) {
11331
11975
  try {
11332
11976
  const accessor = await getAccessor(projectRoot);
@@ -11396,6 +12040,12 @@ async function taskStart(projectRoot, taskId) {
11396
12040
  data: { taskId: result.taskId, previousTask: result.previousTask }
11397
12041
  };
11398
12042
  } catch (err) {
12043
+ if (err instanceof CleoError) {
12044
+ const stringCode = `E_${getExitCodeName(err.code)}`;
12045
+ return engineError(stringCode, err.message, {
12046
+ ...err.fix && { fix: err.fix }
12047
+ });
12048
+ }
11399
12049
  const message = err.message;
11400
12050
  const code = message.includes("not found") ? "E_NOT_FOUND" : "E_NOT_INITIALIZED";
11401
12051
  return engineError(code, message);
@@ -11860,8 +12510,8 @@ async function sessionChainShow(projectRoot, sessionId) {
11860
12510
  // src/dispatch/engines/system-engine.ts
11861
12511
  init_platform();
11862
12512
  init_data_accessor();
11863
- import { readFileSync as readFileSync14, existsSync as existsSync21, readdirSync as readdirSync5 } from "node:fs";
11864
- import { join as join21, basename as basename5 } from "node:path";
12513
+ import { readFileSync as readFileSync13, existsSync as existsSync20, readdirSync as readdirSync5 } from "node:fs";
12514
+ import { join as join20, basename as basename4 } from "node:path";
11865
12515
 
11866
12516
  // src/core/stats/index.ts
11867
12517
  init_json();
@@ -11989,14 +12639,14 @@ init_paths();
11989
12639
  // src/core/system/inject-generate.ts
11990
12640
  init_json();
11991
12641
  init_paths();
11992
- import { readFileSync as readFileSync7, existsSync as existsSync13 } from "node:fs";
11993
- import { join as join13 } from "node:path";
12642
+ import { readFileSync as readFileSync6, existsSync as existsSync12 } from "node:fs";
12643
+ import { join as join12 } from "node:path";
11994
12644
  async function generateInjection(projectRoot, accessor) {
11995
12645
  let version = "unknown";
11996
12646
  try {
11997
- const pkgPath = join13(projectRoot, "package.json");
11998
- if (existsSync13(pkgPath)) {
11999
- const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
12647
+ const pkgPath = join12(projectRoot, "package.json");
12648
+ if (existsSync12(pkgPath)) {
12649
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
12000
12650
  version = pkg.version || "unknown";
12001
12651
  }
12002
12652
  } catch {
@@ -12011,8 +12661,8 @@ async function generateInjection(projectRoot, accessor) {
12011
12661
  }
12012
12662
  try {
12013
12663
  const sessionsPath = getSessionsPath(projectRoot);
12014
- if (existsSync13(sessionsPath)) {
12015
- const sessionsData = JSON.parse(readFileSync7(sessionsPath, "utf-8"));
12664
+ if (existsSync12(sessionsPath)) {
12665
+ const sessionsData = JSON.parse(readFileSync6(sessionsPath, "utf-8"));
12016
12666
  const active = sessionsData.sessions?.find((s) => s.status === "active");
12017
12667
  if (active) {
12018
12668
  activeSessionName = active.name || active.id;
@@ -12093,8 +12743,8 @@ For full protocol details, load the **ct-cleo** skill: \`cleo_query({ domain: "s
12093
12743
  }
12094
12744
 
12095
12745
  // src/core/system/health.ts
12096
- import { existsSync as existsSync15, readFileSync as readFileSync9, statSync as statSync4 } from "node:fs";
12097
- import { join as join15 } from "node:path";
12746
+ import { existsSync as existsSync14, readFileSync as readFileSync8, statSync as statSync4 } from "node:fs";
12747
+ import { join as join14 } from "node:path";
12098
12748
  import { execFile as execFile3 } from "node:child_process";
12099
12749
  import { promisify as promisify3 } from "node:util";
12100
12750
 
@@ -12106,25 +12756,25 @@ init_platform();
12106
12756
 
12107
12757
  // src/core/migration/agent-outputs.ts
12108
12758
  import {
12109
- existsSync as existsSync14,
12110
- readFileSync as readFileSync8,
12111
- writeFileSync as writeFileSync3,
12112
- mkdirSync as mkdirSync7,
12759
+ existsSync as existsSync13,
12760
+ readFileSync as readFileSync7,
12761
+ writeFileSync as writeFileSync2,
12762
+ mkdirSync as mkdirSync6,
12113
12763
  readdirSync as readdirSync3,
12114
12764
  copyFileSync,
12115
12765
  statSync as statSync3,
12116
12766
  rmSync
12117
12767
  } from "node:fs";
12118
- import { join as join14 } from "node:path";
12768
+ import { join as join13 } from "node:path";
12119
12769
  var CANONICAL_DIR = ".cleo/agent-outputs";
12120
12770
  var MANIFEST_PATH_REWRITES = [
12121
12771
  [/claudedocs\/research-outputs\//g, `${CANONICAL_DIR}/`],
12122
12772
  [/claudedocs\/agent-outputs\//g, `${CANONICAL_DIR}/`]
12123
12773
  ];
12124
12774
  function detectLegacyAgentOutputs(projectRoot, cleoDir) {
12125
- const hasResearchOutputs = existsSync14(join14(projectRoot, "claudedocs", "research-outputs"));
12126
- const hasLegacyAgentOutputs = existsSync14(join14(projectRoot, "claudedocs", "agent-outputs"));
12127
- const hasCanonical = existsSync14(join14(cleoDir, "agent-outputs"));
12775
+ const hasResearchOutputs = existsSync13(join13(projectRoot, "claudedocs", "research-outputs"));
12776
+ const hasLegacyAgentOutputs = existsSync13(join13(projectRoot, "claudedocs", "agent-outputs"));
12777
+ const hasCanonical = existsSync13(join13(cleoDir, "agent-outputs"));
12128
12778
  const legacyPaths = [];
12129
12779
  if (hasResearchOutputs) legacyPaths.push("claudedocs/research-outputs/");
12130
12780
  if (hasLegacyAgentOutputs) legacyPaths.push("claudedocs/agent-outputs/");
@@ -12147,30 +12797,30 @@ function migrateAgentOutputs(projectRoot, cleoDir) {
12147
12797
  summary: "No legacy output directories found"
12148
12798
  };
12149
12799
  }
12150
- const newDir = join14(cleoDir, "agent-outputs");
12800
+ const newDir = join13(cleoDir, "agent-outputs");
12151
12801
  const hadCanonical = detection.hasCanonical;
12152
- mkdirSync7(newDir, { recursive: true });
12802
+ mkdirSync6(newDir, { recursive: true });
12153
12803
  let totalCopied = 0;
12154
12804
  const mergedManifestLines = [];
12155
12805
  const copiedFiles = /* @__PURE__ */ new Set();
12156
12806
  const legacySources = [
12157
- { path: join14(projectRoot, "claudedocs", "research-outputs"), exists: detection.hasResearchOutputs },
12158
- { path: join14(projectRoot, "claudedocs", "agent-outputs"), exists: detection.hasLegacyAgentOutputs }
12807
+ { path: join13(projectRoot, "claudedocs", "research-outputs"), exists: detection.hasResearchOutputs },
12808
+ { path: join13(projectRoot, "claudedocs", "agent-outputs"), exists: detection.hasLegacyAgentOutputs }
12159
12809
  ];
12160
12810
  for (const source of legacySources) {
12161
12811
  if (!source.exists) continue;
12162
12812
  totalCopied += copyDirContents(source.path, newDir, mergedManifestLines, copiedFiles);
12163
12813
  }
12164
- const manifestEntries = mergeManifests(newDir, hadCanonical, mergedManifestLines);
12814
+ const manifestEntries2 = mergeManifests(newDir, hadCanonical, mergedManifestLines);
12165
12815
  updateConfigPaths(cleoDir);
12166
12816
  const removed = removeLegacyDirs(projectRoot, detection);
12167
12817
  const parts = [`Migrated ${totalCopied} files \u2192 ${CANONICAL_DIR}/`];
12168
- if (manifestEntries > 0) parts.push(`merged ${manifestEntries} manifest entries`);
12818
+ if (manifestEntries2 > 0) parts.push(`merged ${manifestEntries2} manifest entries`);
12169
12819
  if (removed.length > 0) parts.push(`removed: ${removed.join(", ")}`);
12170
12820
  return {
12171
12821
  migrated: true,
12172
12822
  filesCopied: totalCopied,
12173
- manifestEntries,
12823
+ manifestEntries: manifestEntries2,
12174
12824
  removed,
12175
12825
  summary: parts.join("; ")
12176
12826
  };
@@ -12180,19 +12830,19 @@ function copyDirContents(srcDir, dstDir, manifestLines, copiedFiles) {
12180
12830
  const entries = readdirSync3(srcDir);
12181
12831
  for (const entry of entries) {
12182
12832
  if (entry === "MANIFEST.jsonl") {
12183
- collectManifestLines(join14(srcDir, entry), manifestLines);
12833
+ collectManifestLines(join13(srcDir, entry), manifestLines);
12184
12834
  continue;
12185
12835
  }
12186
- const srcPath = join14(srcDir, entry);
12187
- const dstPath = join14(dstDir, entry);
12836
+ const srcPath = join13(srcDir, entry);
12837
+ const dstPath = join13(dstDir, entry);
12188
12838
  try {
12189
12839
  const st = statSync3(srcPath);
12190
12840
  if (st.isDirectory()) {
12191
- mkdirSync7(dstPath, { recursive: true });
12841
+ mkdirSync6(dstPath, { recursive: true });
12192
12842
  for (const sf of readdirSync3(srcPath)) {
12193
12843
  if (!copiedFiles.has(sf)) {
12194
12844
  try {
12195
- copyFileSync(join14(srcPath, sf), join14(dstPath, sf));
12845
+ copyFileSync(join13(srcPath, sf), join13(dstPath, sf));
12196
12846
  copiedFiles.add(sf);
12197
12847
  count2++;
12198
12848
  } catch {
@@ -12211,7 +12861,7 @@ function copyDirContents(srcDir, dstDir, manifestLines, copiedFiles) {
12211
12861
  }
12212
12862
  function collectManifestLines(manifestPath, out) {
12213
12863
  try {
12214
- const content = readFileSync8(manifestPath, "utf-8");
12864
+ const content = readFileSync7(manifestPath, "utf-8");
12215
12865
  for (const line of content.split("\n")) {
12216
12866
  if (!line.trim()) continue;
12217
12867
  let rewritten = line;
@@ -12224,11 +12874,11 @@ function collectManifestLines(manifestPath, out) {
12224
12874
  }
12225
12875
  }
12226
12876
  function mergeManifests(newDir, hadCanonical, legacyLines) {
12227
- const manifestPath = join14(newDir, "MANIFEST.jsonl");
12877
+ const manifestPath = join13(newDir, "MANIFEST.jsonl");
12228
12878
  const existingLines = [];
12229
- if (hadCanonical && existsSync14(manifestPath)) {
12879
+ if (hadCanonical && existsSync13(manifestPath)) {
12230
12880
  try {
12231
- const existing = readFileSync8(manifestPath, "utf-8");
12881
+ const existing = readFileSync7(manifestPath, "utf-8");
12232
12882
  for (const line of existing.split("\n")) {
12233
12883
  if (line.trim()) existingLines.push(line);
12234
12884
  }
@@ -12255,15 +12905,15 @@ function mergeManifests(newDir, hadCanonical, legacyLines) {
12255
12905
  finalLines.push(line);
12256
12906
  }
12257
12907
  if (finalLines.length > 0) {
12258
- writeFileSync3(manifestPath, finalLines.join("\n") + "\n");
12908
+ writeFileSync2(manifestPath, finalLines.join("\n") + "\n");
12259
12909
  }
12260
12910
  return finalLines.length;
12261
12911
  }
12262
12912
  function updateConfigPaths(cleoDir) {
12263
- const configPath = join14(cleoDir, "config.json");
12264
- if (!existsSync14(configPath)) return;
12913
+ const configPath = join13(cleoDir, "config.json");
12914
+ if (!existsSync13(configPath)) return;
12265
12915
  try {
12266
- const config = JSON.parse(readFileSync8(configPath, "utf-8"));
12916
+ const config = JSON.parse(readFileSync7(configPath, "utf-8"));
12267
12917
  const currentDir = config.agentOutputs?.directory ?? config.agentOutputs ?? config.research?.outputDir;
12268
12918
  if (currentDir && currentDir !== CANONICAL_DIR) {
12269
12919
  if (typeof config.agentOutputs === "object") {
@@ -12277,7 +12927,7 @@ function updateConfigPaths(cleoDir) {
12277
12927
  delete config.research;
12278
12928
  }
12279
12929
  }
12280
- writeFileSync3(configPath, JSON.stringify(config, null, 2));
12930
+ writeFileSync2(configPath, JSON.stringify(config, null, 2));
12281
12931
  }
12282
12932
  } catch {
12283
12933
  }
@@ -12286,20 +12936,20 @@ function removeLegacyDirs(projectRoot, detection) {
12286
12936
  const removed = [];
12287
12937
  if (detection.hasResearchOutputs) {
12288
12938
  try {
12289
- rmSync(join14(projectRoot, "claudedocs", "research-outputs"), { recursive: true, force: true });
12939
+ rmSync(join13(projectRoot, "claudedocs", "research-outputs"), { recursive: true, force: true });
12290
12940
  removed.push("claudedocs/research-outputs/");
12291
12941
  } catch {
12292
12942
  }
12293
12943
  }
12294
12944
  if (detection.hasLegacyAgentOutputs) {
12295
12945
  try {
12296
- rmSync(join14(projectRoot, "claudedocs", "agent-outputs"), { recursive: true, force: true });
12946
+ rmSync(join13(projectRoot, "claudedocs", "agent-outputs"), { recursive: true, force: true });
12297
12947
  removed.push("claudedocs/agent-outputs/");
12298
12948
  } catch {
12299
12949
  }
12300
12950
  }
12301
- const claudedocsDir = join14(projectRoot, "claudedocs");
12302
- if (existsSync14(claudedocsDir)) {
12951
+ const claudedocsDir = join13(projectRoot, "claudedocs");
12952
+ if (existsSync13(claudedocsDir)) {
12303
12953
  try {
12304
12954
  if (readdirSync3(claudedocsDir).length === 0) {
12305
12955
  rmSync(claudedocsDir, { recursive: true, force: true });
@@ -12316,15 +12966,15 @@ init_data_accessor();
12316
12966
  var execAsync = promisify3(execFile3);
12317
12967
  var STALE_JSON_FILES = ["todo.json", "sessions.json", "todo-archive.json"];
12318
12968
  function getSystemHealth(projectRoot, opts) {
12319
- const cleoDir = join15(projectRoot, ".cleo");
12969
+ const cleoDir = join14(projectRoot, ".cleo");
12320
12970
  const checks = [];
12321
- if (existsSync15(cleoDir)) {
12971
+ if (existsSync14(cleoDir)) {
12322
12972
  checks.push({ name: "cleo_dir", status: "pass", message: ".cleo directory exists" });
12323
12973
  } else {
12324
12974
  checks.push({ name: "cleo_dir", status: "fail", message: ".cleo directory not found" });
12325
12975
  }
12326
- const dbPath = join15(cleoDir, "tasks.db");
12327
- if (existsSync15(dbPath)) {
12976
+ const dbPath = join14(cleoDir, "tasks.db");
12977
+ if (existsSync14(dbPath)) {
12328
12978
  try {
12329
12979
  const dbSize = statSync4(dbPath).size;
12330
12980
  if (dbSize > 0) {
@@ -12338,10 +12988,10 @@ function getSystemHealth(projectRoot, opts) {
12338
12988
  } else {
12339
12989
  checks.push({ name: "tasks_db", status: "fail", message: "tasks.db not found" });
12340
12990
  }
12341
- const configPath = join15(cleoDir, "config.json");
12342
- if (existsSync15(configPath)) {
12991
+ const configPath = join14(cleoDir, "config.json");
12992
+ if (existsSync14(configPath)) {
12343
12993
  try {
12344
- JSON.parse(readFileSync9(configPath, "utf-8"));
12994
+ JSON.parse(readFileSync8(configPath, "utf-8"));
12345
12995
  checks.push({ name: "config_json", status: "pass", message: "config.json is valid JSON" });
12346
12996
  } catch {
12347
12997
  checks.push({ name: "config_json", status: "warn", message: "config.json is not valid JSON" });
@@ -12349,8 +12999,8 @@ function getSystemHealth(projectRoot, opts) {
12349
12999
  } else {
12350
13000
  checks.push({ name: "config_json", status: "warn", message: "config.json not found" });
12351
13001
  }
12352
- if (existsSync15(dbPath)) {
12353
- const staleFiles = STALE_JSON_FILES.filter((f) => existsSync15(join15(cleoDir, f)));
13002
+ if (existsSync14(dbPath)) {
13003
+ const staleFiles = STALE_JSON_FILES.filter((f) => existsSync14(join14(cleoDir, f)));
12354
13004
  if (staleFiles.length > 0) {
12355
13005
  checks.push({
12356
13006
  name: "stale_json",
@@ -12360,14 +13010,14 @@ function getSystemHealth(projectRoot, opts) {
12360
13010
  }
12361
13011
  }
12362
13012
  if (opts?.detailed) {
12363
- const logPath = join15(cleoDir, "todo-log.jsonl");
12364
- if (existsSync15(logPath)) {
13013
+ const logPath = join14(cleoDir, "todo-log.jsonl");
13014
+ if (existsSync14(logPath)) {
12365
13015
  checks.push({ name: "log_file", status: "pass", message: "todo-log.jsonl exists" });
12366
13016
  } else {
12367
13017
  checks.push({ name: "log_file", status: "warn", message: "todo-log.jsonl not found" });
12368
13018
  }
12369
- const backupDir = join15(cleoDir, ".backups");
12370
- if (existsSync15(backupDir)) {
13019
+ const backupDir = join14(cleoDir, ".backups");
13020
+ if (existsSync14(backupDir)) {
12371
13021
  checks.push({ name: "backups_dir", status: "pass", message: ".backups directory exists" });
12372
13022
  } else {
12373
13023
  checks.push({ name: "backups_dir", status: "pass", message: "No backups directory (created on first write)" });
@@ -12375,9 +13025,9 @@ function getSystemHealth(projectRoot, opts) {
12375
13025
  }
12376
13026
  let version = "unknown";
12377
13027
  try {
12378
- const pkgPath = join15(projectRoot, "package.json");
12379
- if (existsSync15(pkgPath)) {
12380
- const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
13028
+ const pkgPath = join14(projectRoot, "package.json");
13029
+ if (existsSync14(pkgPath)) {
13030
+ const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
12381
13031
  version = pkg.version || "unknown";
12382
13032
  }
12383
13033
  } catch {
@@ -12396,34 +13046,34 @@ init_paths();
12396
13046
  // src/core/system/backup.ts
12397
13047
  init_errors();
12398
13048
  init_exit_codes();
12399
- import { readFileSync as readFileSync10, writeFileSync as writeFileSync4, existsSync as existsSync16, mkdirSync as mkdirSync8 } from "node:fs";
12400
- import { join as join16 } from "node:path";
13049
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync3, existsSync as existsSync15, mkdirSync as mkdirSync7 } from "node:fs";
13050
+ import { join as join15 } from "node:path";
12401
13051
  function createBackup2(projectRoot, opts) {
12402
- const cleoDir = join16(projectRoot, ".cleo");
13052
+ const cleoDir = join15(projectRoot, ".cleo");
12403
13053
  const btype = opts?.type || "snapshot";
12404
13054
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
12405
13055
  const backupId = `${btype}-${timestamp.replace(/[:.]/g, "-")}`;
12406
- const backupDir = join16(cleoDir, "backups", btype);
12407
- if (!existsSync16(backupDir)) {
12408
- mkdirSync8(backupDir, { recursive: true });
13056
+ const backupDir = join15(cleoDir, "backups", btype);
13057
+ if (!existsSync15(backupDir)) {
13058
+ mkdirSync7(backupDir, { recursive: true });
12409
13059
  }
12410
13060
  const filesToBackup = ["todo.json", "todo-archive.json", "sessions.json", "config.json", "todo-log.jsonl"];
12411
13061
  const backedUp = [];
12412
13062
  for (const file of filesToBackup) {
12413
- const src = join16(cleoDir, file);
12414
- if (existsSync16(src)) {
12415
- const dest = join16(backupDir, `${file}.${backupId}`);
13063
+ const src = join15(cleoDir, file);
13064
+ if (existsSync15(src)) {
13065
+ const dest = join15(backupDir, `${file}.${backupId}`);
12416
13066
  try {
12417
- const content = readFileSync10(src, "utf-8");
12418
- writeFileSync4(dest, content, "utf-8");
13067
+ const content = readFileSync9(src, "utf-8");
13068
+ writeFileSync3(dest, content, "utf-8");
12419
13069
  backedUp.push(file);
12420
13070
  } catch {
12421
13071
  }
12422
13072
  }
12423
13073
  }
12424
- const metaPath = join16(backupDir, `${backupId}.meta.json`);
13074
+ const metaPath = join15(backupDir, `${backupId}.meta.json`);
12425
13075
  try {
12426
- writeFileSync4(metaPath, JSON.stringify({
13076
+ writeFileSync3(metaPath, JSON.stringify({
12427
13077
  backupId,
12428
13078
  type: btype,
12429
13079
  timestamp,
@@ -12438,15 +13088,15 @@ function restoreBackup(projectRoot, params) {
12438
13088
  if (!params.backupId) {
12439
13089
  throw new CleoError(2 /* INVALID_INPUT */, "backupId is required");
12440
13090
  }
12441
- const cleoDir = join16(projectRoot, ".cleo");
13091
+ const cleoDir = join15(projectRoot, ".cleo");
12442
13092
  const backupTypes = ["snapshot", "safety", "migration"];
12443
13093
  let metaPath = null;
12444
13094
  let backupDir = null;
12445
13095
  for (const btype of backupTypes) {
12446
- const candidateMeta = join16(cleoDir, "backups", btype, `${params.backupId}.meta.json`);
12447
- if (existsSync16(candidateMeta)) {
13096
+ const candidateMeta = join15(cleoDir, "backups", btype, `${params.backupId}.meta.json`);
13097
+ if (existsSync15(candidateMeta)) {
12448
13098
  metaPath = candidateMeta;
12449
- backupDir = join16(cleoDir, "backups", btype);
13099
+ backupDir = join15(cleoDir, "backups", btype);
12450
13100
  break;
12451
13101
  }
12452
13102
  }
@@ -12455,17 +13105,17 @@ function restoreBackup(projectRoot, params) {
12455
13105
  }
12456
13106
  let meta;
12457
13107
  try {
12458
- meta = JSON.parse(readFileSync10(metaPath, "utf-8"));
13108
+ meta = JSON.parse(readFileSync9(metaPath, "utf-8"));
12459
13109
  } catch {
12460
13110
  throw new CleoError(3 /* FILE_ERROR */, "Failed to read backup metadata");
12461
13111
  }
12462
13112
  const restored = [];
12463
13113
  for (const file of meta.files ?? []) {
12464
- const backupFile = join16(backupDir, `${file}.${params.backupId}`);
12465
- if (existsSync16(backupFile)) {
13114
+ const backupFile = join15(backupDir, `${file}.${params.backupId}`);
13115
+ if (existsSync15(backupFile)) {
12466
13116
  try {
12467
- const content = readFileSync10(backupFile, "utf-8");
12468
- writeFileSync4(join16(cleoDir, file), content, "utf-8");
13117
+ const content = readFileSync9(backupFile, "utf-8");
13118
+ writeFileSync3(join15(cleoDir, file), content, "utf-8");
12469
13119
  restored.push(file);
12470
13120
  } catch {
12471
13121
  }
@@ -12482,14 +13132,14 @@ function restoreBackup(projectRoot, params) {
12482
13132
  // src/core/system/migrate.ts
12483
13133
  init_errors();
12484
13134
  init_exit_codes();
12485
- import { readFileSync as readFileSync11, existsSync as existsSync17 } from "node:fs";
12486
- import { join as join17 } from "node:path";
13135
+ import { readFileSync as readFileSync10, existsSync as existsSync16 } from "node:fs";
13136
+ import { join as join16 } from "node:path";
12487
13137
  function getMigrationStatus(projectRoot, opts) {
12488
- const taskPath = join17(projectRoot, ".cleo", "tasks.json");
13138
+ const taskPath = join16(projectRoot, ".cleo", "tasks.json");
12489
13139
  let currentVersion = "unknown";
12490
- if (existsSync17(taskPath)) {
13140
+ if (existsSync16(taskPath)) {
12491
13141
  try {
12492
- const taskFile = JSON.parse(readFileSync11(taskPath, "utf-8"));
13142
+ const taskFile = JSON.parse(readFileSync10(taskPath, "utf-8"));
12493
13143
  currentVersion = taskFile._meta?.schemaVersion ?? taskFile.version ?? "unknown";
12494
13144
  } catch {
12495
13145
  throw new CleoError(3 /* FILE_ERROR */, "Failed to read tasks.json");
@@ -12509,21 +13159,21 @@ function getMigrationStatus(projectRoot, opts) {
12509
13159
  // src/core/system/cleanup.ts
12510
13160
  init_errors();
12511
13161
  init_exit_codes();
12512
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync5, existsSync as existsSync18, readdirSync as readdirSync4, unlinkSync as unlinkSync4 } from "node:fs";
12513
- import { join as join18 } from "node:path";
13162
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync4, existsSync as existsSync17, readdirSync as readdirSync4, unlinkSync as unlinkSync3 } from "node:fs";
13163
+ import { join as join17 } from "node:path";
12514
13164
  function cleanupSystem(projectRoot, params) {
12515
13165
  if (!params.target) {
12516
13166
  throw new CleoError(2 /* INVALID_INPUT */, "target is required (sessions|backups|logs|archive)");
12517
13167
  }
12518
- const cleoDir = join18(projectRoot, ".cleo");
13168
+ const cleoDir = join17(projectRoot, ".cleo");
12519
13169
  const dryRun = params.dryRun ?? false;
12520
13170
  const items = [];
12521
13171
  switch (params.target) {
12522
13172
  case "sessions": {
12523
- const sessPath = join18(cleoDir, "sessions.json");
12524
- if (existsSync18(sessPath)) {
13173
+ const sessPath = join17(cleoDir, "sessions.json");
13174
+ if (existsSync17(sessPath)) {
12525
13175
  try {
12526
- const data = JSON.parse(readFileSync12(sessPath, "utf-8"));
13176
+ const data = JSON.parse(readFileSync11(sessPath, "utf-8"));
12527
13177
  const sessions2 = data.sessions ?? [];
12528
13178
  const stale = sessions2.filter((s) => {
12529
13179
  if (s.status !== "active") return false;
@@ -12539,7 +13189,7 @@ function cleanupSystem(projectRoot, params) {
12539
13189
  if (!dryRun && stale.length > 0) {
12540
13190
  const staleIds = new Set(stale.map((s) => s.id));
12541
13191
  data.sessions = sessions2.filter((s) => !staleIds.has(s.id));
12542
- writeFileSync5(sessPath, JSON.stringify(data, null, 2), "utf-8");
13192
+ writeFileSync4(sessPath, JSON.stringify(data, null, 2), "utf-8");
12543
13193
  }
12544
13194
  } catch {
12545
13195
  }
@@ -12547,24 +13197,24 @@ function cleanupSystem(projectRoot, params) {
12547
13197
  break;
12548
13198
  }
12549
13199
  case "backups": {
12550
- const backupBaseDir = join18(cleoDir, "backups");
12551
- if (existsSync18(backupBaseDir)) {
13200
+ const backupBaseDir = join17(cleoDir, "backups");
13201
+ if (existsSync17(backupBaseDir)) {
12552
13202
  for (const typeDir of readdirSync4(backupBaseDir)) {
12553
- const fullDir = join18(backupBaseDir, typeDir);
13203
+ const fullDir = join17(backupBaseDir, typeDir);
12554
13204
  try {
12555
13205
  for (const file of readdirSync4(fullDir)) {
12556
13206
  if (file.endsWith(".meta.json")) {
12557
- const metaFilePath = join18(fullDir, file);
13207
+ const metaFilePath = join17(fullDir, file);
12558
13208
  try {
12559
- const meta = JSON.parse(readFileSync12(metaFilePath, "utf-8"));
13209
+ const meta = JSON.parse(readFileSync11(metaFilePath, "utf-8"));
12560
13210
  if (params.olderThan && meta.timestamp < params.olderThan) {
12561
13211
  items.push(file.replace(".meta.json", ""));
12562
13212
  if (!dryRun) {
12563
- unlinkSync4(metaFilePath);
13213
+ unlinkSync3(metaFilePath);
12564
13214
  for (const bf of readdirSync4(fullDir)) {
12565
13215
  if (bf.includes(meta.backupId)) {
12566
13216
  try {
12567
- unlinkSync4(join18(fullDir, bf));
13217
+ unlinkSync3(join17(fullDir, bf));
12568
13218
  } catch {
12569
13219
  }
12570
13220
  }
@@ -12583,13 +13233,13 @@ function cleanupSystem(projectRoot, params) {
12583
13233
  }
12584
13234
  case "logs": {
12585
13235
  const auditPattern = /^audit-log-.*\.json$/;
12586
- if (existsSync18(cleoDir)) {
13236
+ if (existsSync17(cleoDir)) {
12587
13237
  for (const file of readdirSync4(cleoDir)) {
12588
13238
  if (auditPattern.test(file)) {
12589
13239
  items.push(file);
12590
13240
  if (!dryRun) {
12591
13241
  try {
12592
- unlinkSync4(join18(cleoDir, file));
13242
+ unlinkSync3(join17(cleoDir, file));
12593
13243
  } catch {
12594
13244
  }
12595
13245
  }
@@ -12607,17 +13257,17 @@ function cleanupSystem(projectRoot, params) {
12607
13257
  // src/core/system/safestop.ts
12608
13258
  init_errors();
12609
13259
  init_exit_codes();
12610
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync6, existsSync as existsSync19 } from "node:fs";
12611
- import { join as join19 } from "node:path";
13260
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync5, existsSync as existsSync18 } from "node:fs";
13261
+ import { join as join18 } from "node:path";
12612
13262
  function safestop(projectRoot, opts) {
12613
13263
  const dryRun = opts?.dryRun ?? false;
12614
13264
  const reason = opts?.reason ?? "Manual safestop";
12615
13265
  let sessionEnded = false;
12616
13266
  if (!dryRun && !opts?.noSessionEnd) {
12617
- const sessPath = join19(projectRoot, ".cleo", "sessions.json");
12618
- if (existsSync19(sessPath)) {
13267
+ const sessPath = join18(projectRoot, ".cleo", "sessions.json");
13268
+ if (existsSync18(sessPath)) {
12619
13269
  try {
12620
- const data = JSON.parse(readFileSync13(sessPath, "utf-8"));
13270
+ const data = JSON.parse(readFileSync12(sessPath, "utf-8"));
12621
13271
  const sessions2 = data.sessions ?? [];
12622
13272
  let changed = false;
12623
13273
  for (const s of sessions2) {
@@ -12630,7 +13280,7 @@ function safestop(projectRoot, opts) {
12630
13280
  }
12631
13281
  }
12632
13282
  if (changed) {
12633
- writeFileSync6(sessPath, JSON.stringify(data, null, 2), "utf-8");
13283
+ writeFileSync5(sessPath, JSON.stringify(data, null, 2), "utf-8");
12634
13284
  }
12635
13285
  } catch {
12636
13286
  }
@@ -12643,9 +13293,9 @@ function safestop(projectRoot, opts) {
12643
13293
  init_sequence();
12644
13294
 
12645
13295
  // src/core/system/runtime.ts
12646
- import { existsSync as existsSync20 } from "node:fs";
13296
+ import { existsSync as existsSync19 } from "node:fs";
12647
13297
  import { readFile as readFile3 } from "node:fs/promises";
12648
- import { basename as basename4, join as join20 } from "node:path";
13298
+ import { basename as basename3, join as join19 } from "node:path";
12649
13299
  import { homedir as homedir2 } from "node:os";
12650
13300
  import { execFile as execFile4 } from "node:child_process";
12651
13301
  import { promisify as promisify4 } from "node:util";
@@ -12671,16 +13321,16 @@ function detectFromDataRoot(dataRoot) {
12671
13321
  function getExpectedNaming(channel) {
12672
13322
  switch (channel) {
12673
13323
  case "dev":
12674
- return { cli: "cleo-dev", mcp: "cleo-mcp-dev", server: "cleo-dev" };
13324
+ return { cli: "cleo-dev", mcp: "cleo-dev mcp", server: "cleo-dev" };
12675
13325
  case "beta":
12676
- return { cli: "cleo-beta", mcp: "cleo-mcp-beta", server: "cleo-beta" };
13326
+ return { cli: "cleo-beta", mcp: "cleo-beta mcp", server: "cleo-beta" };
12677
13327
  default:
12678
- return { cli: "cleo", mcp: "cleo-mcp", server: "cleo" };
13328
+ return { cli: "cleo", mcp: "cleo mcp", server: "cleo" };
12679
13329
  }
12680
13330
  }
12681
13331
  async function parseVersionFile(dataRoot) {
12682
- const versionPath = join20(dataRoot, "VERSION");
12683
- if (!existsSync20(versionPath)) return null;
13332
+ const versionPath = join19(dataRoot, "VERSION");
13333
+ if (!existsSync19(versionPath)) return null;
12684
13334
  let content;
12685
13335
  try {
12686
13336
  content = await readFile3(versionPath, "utf-8");
@@ -12709,9 +13359,9 @@ async function parseVersionFile(dataRoot) {
12709
13359
  async function getPackageInfo(sourceDir) {
12710
13360
  const candidates = [];
12711
13361
  if (sourceDir && sourceDir !== "unknown" && sourceDir !== "npm") {
12712
- candidates.push(join20(sourceDir, "package.json"));
13362
+ candidates.push(join19(sourceDir, "package.json"));
12713
13363
  }
12714
- candidates.push(join20(process.cwd(), "package.json"));
13364
+ candidates.push(join19(process.cwd(), "package.json"));
12715
13365
  for (const candidate of candidates) {
12716
13366
  try {
12717
13367
  const raw = await readFile3(candidate, "utf-8");
@@ -12735,9 +13385,9 @@ async function resolveBinaryPath(name) {
12735
13385
  }
12736
13386
  async function getRuntimeDiagnostics(options) {
12737
13387
  const scriptPath = process.argv[1] ?? "";
12738
- const invocationName = basename4(scriptPath || process.argv0 || "cleo");
13388
+ const invocationName = basename3(scriptPath || process.argv0 || "cleo");
12739
13389
  const envChannel = normalizeChannel(process.env["CLEO_CHANNEL"]);
12740
- const dataRoot = process.env["CLEO_HOME"] ?? join20(homedir2(), ".cleo");
13390
+ const dataRoot = process.env["CLEO_HOME"] ?? join19(homedir2(), ".cleo");
12741
13391
  const versionInfo = await parseVersionFile(dataRoot);
12742
13392
  const packageInfo = await getPackageInfo(versionInfo?.source);
12743
13393
  const channel = envChannel ?? detectFromInvocation(invocationName) ?? normalizeChannel(versionInfo?.version.includes("-beta") ? "beta" : void 0) ?? detectFromDataRoot(dataRoot) ?? normalizeChannel(versionInfo?.mode.startsWith("dev") ? "dev" : void 0) ?? "stable";
@@ -12775,10 +13425,7 @@ async function getRuntimeDiagnostics(options) {
12775
13425
  "cleo",
12776
13426
  "ct",
12777
13427
  "cleo-dev",
12778
- "cleo-beta",
12779
- "cleo-mcp",
12780
- "cleo-mcp-dev",
12781
- "cleo-mcp-beta"
13428
+ "cleo-beta"
12782
13429
  ];
12783
13430
  const entries = await Promise.all(binNames.map(async (name) => [name, await resolveBinaryPath(name)]));
12784
13431
  result.binaries = Object.fromEntries(entries.map(([name, path]) => [name, path ?? "not found"]));
@@ -12955,10 +13602,10 @@ async function systemLog(projectRoot, filters) {
12955
13602
  }
12956
13603
  async function queryAuditLogSqlite(projectRoot, filters) {
12957
13604
  try {
12958
- const { join: join46 } = await import("node:path");
12959
- const { existsSync: existsSync45 } = await import("node:fs");
12960
- const dbPath = join46(projectRoot, ".cleo", "tasks.db");
12961
- if (!existsSync45(dbPath)) return null;
13605
+ const { join: join45 } = await import("node:path");
13606
+ const { existsSync: existsSync44 } = await import("node:fs");
13607
+ const dbPath = join45(projectRoot, ".cleo", "tasks.db");
13608
+ if (!existsSync44(dbPath)) return null;
12962
13609
  const { getDb: getDb2 } = await Promise.resolve().then(() => (init_sqlite(), sqlite_exports));
12963
13610
  const { auditLog: auditLog2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
12964
13611
  const { sql: sql4 } = await import("drizzle-orm");
@@ -13023,8 +13670,8 @@ async function queryAuditLogSqlite(projectRoot, filters) {
13023
13670
  }
13024
13671
  }
13025
13672
  function queryAuditLogJsonl(projectRoot, filters) {
13026
- const logPath = getDataPath2(projectRoot, "todo-log.jsonl");
13027
- const raw = readLogFileEntries2(logPath);
13673
+ const logPath = getDataPath(projectRoot, "todo-log.jsonl");
13674
+ const raw = readLogFileEntries(logPath);
13028
13675
  let entries = raw;
13029
13676
  if (filters?.operation) {
13030
13677
  entries = entries.filter((e) => e.operation === filters.operation);
@@ -13053,34 +13700,34 @@ function queryAuditLogJsonl(projectRoot, filters) {
13053
13700
  }
13054
13701
  function systemContext(projectRoot, params) {
13055
13702
  try {
13056
- const cleoDir = join21(projectRoot, ".cleo");
13703
+ const cleoDir = join20(projectRoot, ".cleo");
13057
13704
  let stateFile;
13058
13705
  if (params?.session) {
13059
- const sessionFile = join21(cleoDir, "context-states", `context-state-${params.session}.json`);
13060
- stateFile = existsSync21(sessionFile) ? sessionFile : join21(cleoDir, ".context-state.json");
13706
+ const sessionFile = join20(cleoDir, "context-states", `context-state-${params.session}.json`);
13707
+ stateFile = existsSync20(sessionFile) ? sessionFile : join20(cleoDir, ".context-state.json");
13061
13708
  } else {
13062
- const currentSessionPath = join21(cleoDir, ".current-session");
13063
- if (existsSync21(currentSessionPath)) {
13064
- const currentSession = readFileSync14(currentSessionPath, "utf-8").trim();
13709
+ const currentSessionPath = join20(cleoDir, ".current-session");
13710
+ if (existsSync20(currentSessionPath)) {
13711
+ const currentSession = readFileSync13(currentSessionPath, "utf-8").trim();
13065
13712
  if (currentSession) {
13066
- const sessionFile = join21(cleoDir, "context-states", `context-state-${currentSession}.json`);
13067
- stateFile = existsSync21(sessionFile) ? sessionFile : join21(cleoDir, ".context-state.json");
13713
+ const sessionFile = join20(cleoDir, "context-states", `context-state-${currentSession}.json`);
13714
+ stateFile = existsSync20(sessionFile) ? sessionFile : join20(cleoDir, ".context-state.json");
13068
13715
  } else {
13069
- stateFile = join21(cleoDir, ".context-state.json");
13716
+ stateFile = join20(cleoDir, ".context-state.json");
13070
13717
  }
13071
13718
  } else {
13072
- stateFile = join21(cleoDir, ".context-state.json");
13719
+ stateFile = join20(cleoDir, ".context-state.json");
13073
13720
  }
13074
13721
  }
13075
13722
  const sessions2 = [];
13076
- const statesDir = join21(cleoDir, "context-states");
13077
- if (existsSync21(statesDir)) {
13723
+ const statesDir = join20(cleoDir, "context-states");
13724
+ if (existsSync20(statesDir)) {
13078
13725
  for (const file of readdirSync5(statesDir)) {
13079
13726
  if (file.startsWith("context-state-") && file.endsWith(".json")) {
13080
13727
  try {
13081
- const state = JSON.parse(readFileSync14(join21(statesDir, file), "utf-8"));
13728
+ const state = JSON.parse(readFileSync13(join20(statesDir, file), "utf-8"));
13082
13729
  sessions2.push({
13083
- file: basename5(file),
13730
+ file: basename4(file),
13084
13731
  sessionId: state.sessionId ?? null,
13085
13732
  percentage: state.contextWindow?.percentage ?? 0,
13086
13733
  status: state.status ?? "unknown",
@@ -13091,10 +13738,10 @@ function systemContext(projectRoot, params) {
13091
13738
  }
13092
13739
  }
13093
13740
  }
13094
- const singletonFile = join21(cleoDir, ".context-state.json");
13095
- if (existsSync21(singletonFile)) {
13741
+ const singletonFile = join20(cleoDir, ".context-state.json");
13742
+ if (existsSync20(singletonFile)) {
13096
13743
  try {
13097
- const state = JSON.parse(readFileSync14(singletonFile, "utf-8"));
13744
+ const state = JSON.parse(readFileSync13(singletonFile, "utf-8"));
13098
13745
  sessions2.push({
13099
13746
  file: ".context-state.json",
13100
13747
  sessionId: state.sessionId ?? "global",
@@ -13105,7 +13752,7 @@ function systemContext(projectRoot, params) {
13105
13752
  } catch {
13106
13753
  }
13107
13754
  }
13108
- if (!existsSync21(stateFile)) {
13755
+ if (!existsSync20(stateFile)) {
13109
13756
  return {
13110
13757
  success: true,
13111
13758
  data: {
@@ -13121,7 +13768,7 @@ function systemContext(projectRoot, params) {
13121
13768
  };
13122
13769
  }
13123
13770
  try {
13124
- const state = JSON.parse(readFileSync14(stateFile, "utf-8"));
13771
+ const state = JSON.parse(readFileSync13(stateFile, "utf-8"));
13125
13772
  const timestamp = state.timestamp;
13126
13773
  const staleMs = state.staleAfterMs ?? 5e3;
13127
13774
  const percentage = state.contextWindow?.percentage ?? 0;
@@ -13316,8 +13963,8 @@ init_json();
13316
13963
  init_json();
13317
13964
  init_paths();
13318
13965
  import { chmod, mkdir as mkdir5, access as access2, writeFile as writeFile3, readFile as readFile5, copyFile as copyFile2, symlink, lstat, unlink as unlink3 } from "node:fs/promises";
13319
- import { constants as fsConstants, existsSync as existsSync24, readFileSync as readFileSync17, readdirSync as readdirSync7 } from "node:fs";
13320
- import { join as join26, resolve as resolve4, basename as basename6, dirname as dirname8 } from "node:path";
13966
+ import { constants as fsConstants, existsSync as existsSync23, readFileSync as readFileSync16, readdirSync as readdirSync7 } from "node:fs";
13967
+ import { join as join25, resolve as resolve5, basename as basename5, dirname as dirname7 } from "node:path";
13321
13968
  import { fileURLToPath as fileURLToPath3 } from "node:url";
13322
13969
  import { createHash as createHash5 } from "node:crypto";
13323
13970
  import { homedir as homedir4 } from "node:os";
@@ -13333,7 +13980,7 @@ async function fileExists(path) {
13333
13980
  }
13334
13981
  }
13335
13982
  async function stripCLEOBlocks(filePath) {
13336
- if (!existsSync24(filePath)) return;
13983
+ if (!existsSync23(filePath)) return;
13337
13984
  const content = await readFile5(filePath, "utf8");
13338
13985
  const stripped = content.replace(
13339
13986
  /\n?<!-- CLEO:START -->[\s\S]*?<!-- CLEO:END -->\n?/g,
@@ -13369,31 +14016,67 @@ function createDefaultConfig() {
13369
14016
  }
13370
14017
  function getPackageRoot() {
13371
14018
  const thisFile = fileURLToPath3(import.meta.url);
13372
- return resolve4(dirname8(thisFile), "..", "..");
14019
+ return resolve5(dirname7(thisFile), "..", "..");
13373
14020
  }
13374
14021
  function getGitignoreContent() {
13375
14022
  try {
13376
14023
  const packageRoot = getPackageRoot();
13377
- const templatePath = join26(packageRoot, "templates", "cleo-gitignore");
13378
- if (existsSync24(templatePath)) {
13379
- return readFileSync17(templatePath, "utf-8");
14024
+ const templatePath = join25(packageRoot, "templates", "cleo-gitignore");
14025
+ if (existsSync23(templatePath)) {
14026
+ return readFileSync16(templatePath, "utf-8");
13380
14027
  }
13381
14028
  } catch {
13382
14029
  }
13383
14030
  return CLEO_GITIGNORE_FALLBACK;
13384
14031
  }
13385
- var CLEO_GITIGNORE_FALLBACK = `# CLEO Project Data - Selective Git Tracking
13386
- # Tracked via STATE_FILES: config.json, project-info.json, project-context.json, adrs/, agent-outputs/
13387
- # agent-outputs/ is intentionally tracked (research outputs, ADR evidence)
13388
- logs/
13389
- audit-log*.json
13390
- tasks.db
13391
- tasks.db-shm
13392
- tasks.db-wal
13393
- .git/
14032
+ var CLEO_GITIGNORE_FALLBACK = `# .cleo/.gitignore \u2014 Deny-by-default for CLEO project data
14033
+ # Ignore everything, then explicitly allow only tracked files.
14034
+
14035
+ # Step 1: Ignore everything
14036
+ *
14037
+
14038
+ # Allow list
14039
+ !.gitignore
14040
+ !config.json
14041
+ !project-context.json
14042
+ !project-info.json
14043
+ !setup-otel.sh
14044
+ !DATA-SAFETY-IMPLEMENTATION-SUMMARY.md
14045
+ !schemas/
14046
+ !schemas/**
14047
+ !templates/
14048
+ !templates/**
14049
+ !adrs/
14050
+ !adrs/**
14051
+ !rcasd/
14052
+ !rcasd/**
14053
+ !agent-outputs/
14054
+ !agent-outputs/**
14055
+
14056
+ # Explicit deny safety net
14057
+ *.db
14058
+ *.db-shm
14059
+ *.db-wal
14060
+ *.db-journal
14061
+ log.json
14062
+ tasks-log.jsonl
14063
+ todo-log.jsonl
14064
+ bypass-log.json
14065
+ qa-log.json
14066
+ .deps-cache/
14067
+ .context-alert-state.json
14068
+ .context-state*.json
14069
+ context-states/
14070
+ .git-checkpoint-state
14071
+ .migration-state.json
14072
+ migrations.json
14073
+ sync/
14074
+ metrics/
14075
+ .backups/
14076
+ backups/
13394
14077
  `;
13395
14078
  async function removeCleoFromRootGitignore(projectRoot) {
13396
- const rootGitignorePath = join26(projectRoot, ".gitignore");
14079
+ const rootGitignorePath = join25(projectRoot, ".gitignore");
13397
14080
  if (!await fileExists(rootGitignorePath)) {
13398
14081
  return false;
13399
14082
  }
@@ -13414,8 +14097,8 @@ function generateProjectHash2(projectPath) {
13414
14097
  }
13415
14098
  function getCleoVersion() {
13416
14099
  try {
13417
- const pkgPath = join26(getPackageRoot(), "package.json");
13418
- const pkg = JSON.parse(readFileSync17(pkgPath, "utf-8"));
14100
+ const pkgPath = join25(getPackageRoot(), "package.json");
14101
+ const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
13419
14102
  return pkg.version ?? "0.0.0";
13420
14103
  } catch {
13421
14104
  return "0.0.0";
@@ -13423,9 +14106,9 @@ function getCleoVersion() {
13423
14106
  }
13424
14107
  function getInjectionTemplateContent() {
13425
14108
  const packageRoot = getPackageRoot();
13426
- const packageTemplatePath = join26(packageRoot, "templates", "CLEO-INJECTION.md");
13427
- if (existsSync24(packageTemplatePath)) {
13428
- return readFileSync17(packageTemplatePath, "utf-8");
14109
+ const packageTemplatePath = join25(packageRoot, "templates", "CLEO-INJECTION.md");
14110
+ if (existsSync23(packageTemplatePath)) {
14111
+ return readFileSync16(packageTemplatePath, "utf-8");
13429
14112
  }
13430
14113
  return null;
13431
14114
  }
@@ -13438,27 +14121,27 @@ async function initCoreFiles(cleoDir, _projectName, force, created, skipped) {
13438
14121
  await saveJson(configPath, createDefaultConfig());
13439
14122
  created.push("config.json");
13440
14123
  }
13441
- const legacySequencePath = join26(cleoDir, ".sequence");
14124
+ const legacySequencePath = join25(cleoDir, ".sequence");
13442
14125
  try {
13443
14126
  await unlink3(legacySequencePath);
13444
14127
  } catch {
13445
14128
  }
13446
- const legacySequenceJsonPath = join26(cleoDir, ".sequence.json");
14129
+ const legacySequenceJsonPath = join25(cleoDir, ".sequence.json");
13447
14130
  try {
13448
14131
  await unlink3(legacySequenceJsonPath);
13449
14132
  } catch {
13450
14133
  }
13451
- const backupDir = join26(cleoDir, "backups");
13452
- await mkdir5(join26(backupDir, "operational"), { recursive: true });
13453
- await mkdir5(join26(backupDir, "safety"), { recursive: true });
14134
+ const backupDir = join25(cleoDir, "backups");
14135
+ await mkdir5(join25(backupDir, "operational"), { recursive: true });
14136
+ await mkdir5(join25(backupDir, "safety"), { recursive: true });
13454
14137
  try {
13455
14138
  const { getDb: getDb2 } = await Promise.resolve().then(() => (init_sqlite(), sqlite_exports));
13456
- await getDb2(join26(cleoDir, ".."));
14139
+ await getDb2(join25(cleoDir, ".."));
13457
14140
  created.push("tasks.db");
13458
14141
  } catch (err) {
13459
14142
  created.push(`tasks.db (deferred: ${err instanceof Error ? err.message : String(err)})`);
13460
14143
  }
13461
- const gitignorePath = join26(cleoDir, ".gitignore");
14144
+ const gitignorePath = join25(cleoDir, ".gitignore");
13462
14145
  if (await fileExists(gitignorePath) && !force) {
13463
14146
  skipped.push(".gitignore");
13464
14147
  } else {
@@ -13467,8 +14150,8 @@ async function initCoreFiles(cleoDir, _projectName, force, created, skipped) {
13467
14150
  }
13468
14151
  }
13469
14152
  async function initCleoGitRepo(cleoDir, created, warnings) {
13470
- const cleoGitDir = join26(cleoDir, ".git");
13471
- if (existsSync24(cleoGitDir)) {
14153
+ const cleoGitDir = join25(cleoDir, ".git");
14154
+ if (existsSync23(cleoGitDir)) {
13472
14155
  return;
13473
14156
  }
13474
14157
  const gitEnv = {
@@ -13486,11 +14169,11 @@ async function initCleoGitRepo(cleoDir, created, warnings) {
13486
14169
  }
13487
14170
  }
13488
14171
  async function initSchemas(cleoDir, force, created, warnings) {
13489
- const schemasDir = join26(cleoDir, "schemas");
14172
+ const schemasDir = join25(cleoDir, "schemas");
13490
14173
  await mkdir5(schemasDir, { recursive: true });
13491
14174
  const packageRoot = getPackageRoot();
13492
- const sourceSchemaDir = join26(packageRoot, "schemas");
13493
- if (!existsSync24(sourceSchemaDir)) {
14175
+ const sourceSchemaDir = join25(packageRoot, "schemas");
14176
+ if (!existsSync23(sourceSchemaDir)) {
13494
14177
  warnings.push("schemas/ directory not found in package root, skipping schema installation");
13495
14178
  return;
13496
14179
  }
@@ -13501,9 +14184,9 @@ async function initSchemas(cleoDir, force, created, warnings) {
13501
14184
  ];
13502
14185
  let copiedCount = 0;
13503
14186
  for (const schemaFile of coreSchemas) {
13504
- const sourcePath = join26(sourceSchemaDir, schemaFile);
13505
- const destPath = join26(schemasDir, schemaFile);
13506
- if (!existsSync24(sourcePath)) {
14187
+ const sourcePath = join25(sourceSchemaDir, schemaFile);
14188
+ const destPath = join25(schemasDir, schemaFile);
14189
+ if (!existsSync23(sourcePath)) {
13507
14190
  continue;
13508
14191
  }
13509
14192
  if (await fileExists(destPath) && !force) {
@@ -13521,27 +14204,27 @@ async function initSchemas(cleoDir, force, created, warnings) {
13521
14204
  }
13522
14205
  }
13523
14206
  async function initGitHooks(projRoot, force, created, warnings) {
13524
- const gitHooksDir = join26(projRoot, ".git", "hooks");
13525
- if (!existsSync24(join26(projRoot, ".git"))) {
14207
+ const gitHooksDir = join25(projRoot, ".git", "hooks");
14208
+ if (!existsSync23(join25(projRoot, ".git"))) {
13526
14209
  warnings.push("No .git/ directory found, skipping git hook installation");
13527
14210
  return;
13528
14211
  }
13529
14212
  await mkdir5(gitHooksDir, { recursive: true });
13530
14213
  const packageRoot = getPackageRoot();
13531
- const sourceDir = join26(packageRoot, "templates", "git-hooks");
13532
- if (!existsSync24(sourceDir)) {
14214
+ const sourceDir = join25(packageRoot, "templates", "git-hooks");
14215
+ if (!existsSync23(sourceDir)) {
13533
14216
  warnings.push("templates/git-hooks/ not found in package root, skipping git hook installation");
13534
14217
  return;
13535
14218
  }
13536
14219
  const hooks = ["commit-msg", "pre-commit"];
13537
14220
  let installedCount = 0;
13538
14221
  for (const hook of hooks) {
13539
- const sourcePath = join26(sourceDir, hook);
13540
- const destPath = join26(gitHooksDir, hook);
13541
- if (!existsSync24(sourcePath)) {
14222
+ const sourcePath = join25(sourceDir, hook);
14223
+ const destPath = join25(gitHooksDir, hook);
14224
+ if (!existsSync23(sourcePath)) {
13542
14225
  continue;
13543
14226
  }
13544
- if (existsSync24(destPath) && !force) {
14227
+ if (existsSync23(destPath) && !force) {
13545
14228
  continue;
13546
14229
  }
13547
14230
  try {
@@ -13557,7 +14240,7 @@ async function initGitHooks(projRoot, force, created, warnings) {
13557
14240
  }
13558
14241
  }
13559
14242
  async function initProjectInfo(cleoDir, projectRoot, force, created, skipped) {
13560
- const projectInfoPath = join26(cleoDir, "project-info.json");
14243
+ const projectInfoPath = join25(cleoDir, "project-info.json");
13561
14244
  if (await fileExists(projectInfoPath) && !force) {
13562
14245
  skipped.push("project-info.json");
13563
14246
  return;
@@ -13606,40 +14289,40 @@ async function initInjection(projectRoot, created, warnings) {
13606
14289
  return;
13607
14290
  }
13608
14291
  for (const provider of providers) {
13609
- const instructFile = join26(projectRoot, provider.pathProject, provider.instructFile);
14292
+ const instructFile = join25(projectRoot, provider.pathProject, provider.instructFile);
13610
14293
  await stripCLEOBlocks(instructFile);
13611
14294
  }
13612
- await stripCLEOBlocks(join26(projectRoot, "AGENTS.md"));
14295
+ await stripCLEOBlocks(join25(projectRoot, "AGENTS.md"));
13613
14296
  const injectionContent = buildInjectionContent2({ references: ["@AGENTS.md"] });
13614
14297
  const results = await injectAll2(providers, projectRoot, "project", injectionContent);
13615
14298
  const injected = [];
13616
14299
  for (const [filePath, action] of results) {
13617
- const fileName = basename6(filePath);
14300
+ const fileName = basename5(filePath);
13618
14301
  injected.push(`${fileName} (${action})`);
13619
14302
  }
13620
14303
  if (injected.length > 0) {
13621
14304
  created.push(`injection: ${injected.join(", ")}`);
13622
14305
  }
13623
- const agentsMdPath = join26(projectRoot, "AGENTS.md");
14306
+ const agentsMdPath = join25(projectRoot, "AGENTS.md");
13624
14307
  const agentsMdLines = ["@~/.cleo/templates/CLEO-INJECTION.md"];
13625
- const projectContextPath = join26(projectRoot, ".cleo", "project-context.json");
13626
- if (existsSync24(projectContextPath)) {
14308
+ const projectContextPath = join25(projectRoot, ".cleo", "project-context.json");
14309
+ if (existsSync23(projectContextPath)) {
13627
14310
  agentsMdLines.push("@.cleo/project-context.json");
13628
14311
  }
13629
14312
  const agentsAction = await inject(agentsMdPath, agentsMdLines.join("\n"));
13630
14313
  created.push(`AGENTS.md CLEO content (${agentsAction})`);
13631
14314
  const content = getInjectionTemplateContent();
13632
14315
  if (content) {
13633
- const globalTemplatesDir = join26(getCleoHome(), "templates");
14316
+ const globalTemplatesDir = join25(getCleoHome(), "templates");
13634
14317
  await mkdir5(globalTemplatesDir, { recursive: true });
13635
- const globalPath = join26(globalTemplatesDir, "CLEO-INJECTION.md");
13636
- if (!existsSync24(globalPath)) {
14318
+ const globalPath = join25(globalTemplatesDir, "CLEO-INJECTION.md");
14319
+ if (!existsSync23(globalPath)) {
13637
14320
  await writeFile3(globalPath, content);
13638
14321
  }
13639
14322
  }
13640
14323
  try {
13641
- const globalAgentsDir = join26(homedir4(), ".agents");
13642
- const globalAgentsMd = join26(globalAgentsDir, "AGENTS.md");
14324
+ const globalAgentsDir = join25(homedir4(), ".agents");
14325
+ const globalAgentsMd = join25(globalAgentsDir, "AGENTS.md");
13643
14326
  await mkdir5(globalAgentsDir, { recursive: true });
13644
14327
  await inject(globalAgentsMd, "@~/.cleo/templates/CLEO-INJECTION.md");
13645
14328
  } catch {
@@ -13650,13 +14333,13 @@ async function initInjection(projectRoot, created, warnings) {
13650
14333
  }
13651
14334
  async function initAgentDefinition(created, warnings) {
13652
14335
  const packageRoot = getPackageRoot();
13653
- const agentSourceDir = join26(packageRoot, "agents", "cleo-subagent");
13654
- if (!existsSync24(agentSourceDir)) {
14336
+ const agentSourceDir = join25(packageRoot, "agents", "cleo-subagent");
14337
+ if (!existsSync23(agentSourceDir)) {
13655
14338
  warnings.push("agents/cleo-subagent/ not found in package, skipping agent definition install");
13656
14339
  return;
13657
14340
  }
13658
- const globalAgentsDir = join26(homedir4(), ".agents", "agents", "cleo-subagent");
13659
- await mkdir5(dirname8(globalAgentsDir), { recursive: true });
14341
+ const globalAgentsDir = join25(homedir4(), ".agents", "agents", "cleo-subagent");
14342
+ await mkdir5(dirname7(globalAgentsDir), { recursive: true });
13660
14343
  try {
13661
14344
  try {
13662
14345
  const stat2 = await lstat(globalAgentsDir);
@@ -13672,7 +14355,7 @@ async function initAgentDefinition(created, warnings) {
13672
14355
  await mkdir5(globalAgentsDir, { recursive: true });
13673
14356
  const files = readdirSync7(agentSourceDir);
13674
14357
  for (const file of files) {
13675
- await copyFile2(join26(agentSourceDir, file), join26(globalAgentsDir, file));
14358
+ await copyFile2(join25(agentSourceDir, file), join25(globalAgentsDir, file));
13676
14359
  }
13677
14360
  created.push("agent: cleo-subagent (copied)");
13678
14361
  } catch (copyErr) {
@@ -13724,12 +14407,12 @@ async function initCoreSkills(created, warnings) {
13724
14407
  let ctSkillsRoot = null;
13725
14408
  try {
13726
14409
  const packageRoot = getPackageRoot();
13727
- const bundledPath = join26(packageRoot, "packages", "ct-skills");
13728
- if (existsSync24(join26(bundledPath, "skills.json"))) {
14410
+ const bundledPath = join25(packageRoot, "packages", "ct-skills");
14411
+ if (existsSync23(join25(bundledPath, "skills.json"))) {
13729
14412
  ctSkillsRoot = bundledPath;
13730
14413
  } else {
13731
- const ctSkillsPath = join26(packageRoot, "node_modules", "@cleocode", "ct-skills");
13732
- if (existsSync24(join26(ctSkillsPath, "skills.json"))) {
14414
+ const ctSkillsPath = join25(packageRoot, "node_modules", "@cleocode", "ct-skills");
14415
+ if (existsSync23(join25(ctSkillsPath, "skills.json"))) {
13733
14416
  ctSkillsRoot = ctSkillsPath;
13734
14417
  }
13735
14418
  }
@@ -13744,14 +14427,14 @@ async function initCoreSkills(created, warnings) {
13744
14427
  } catch {
13745
14428
  warnings.push("Failed to register skill library with CAAMP");
13746
14429
  }
13747
- const catalogPath = join26(ctSkillsRoot, "skills.json");
13748
- const catalog2 = JSON.parse(readFileSync17(catalogPath, "utf-8"));
14430
+ const catalogPath = join25(ctSkillsRoot, "skills.json");
14431
+ const catalog2 = JSON.parse(readFileSync16(catalogPath, "utf-8"));
13749
14432
  const skills = catalog2.skills ?? [];
13750
14433
  const coreSkills = skills.filter((s) => s.tier <= 2);
13751
14434
  const installed = [];
13752
14435
  for (const skill of coreSkills) {
13753
- const skillSourceDir = dirname8(join26(ctSkillsRoot, skill.path));
13754
- if (!existsSync24(skillSourceDir)) {
14436
+ const skillSourceDir = dirname7(join25(ctSkillsRoot, skill.path));
14437
+ if (!existsSync23(skillSourceDir)) {
13755
14438
  continue;
13756
14439
  }
13757
14440
  try {
@@ -13786,7 +14469,7 @@ async function initProjectDetect(cleoDir, projectRoot, created, warnings) {
13786
14469
  try {
13787
14470
  const { detectProjectType: detectProjectType2 } = await Promise.resolve().then(() => (init_project_detect(), project_detect_exports));
13788
14471
  const info = detectProjectType2(projectRoot);
13789
- const contextPath = join26(cleoDir, "project-context.json");
14472
+ const contextPath = join25(cleoDir, "project-context.json");
13790
14473
  const context = {
13791
14474
  ...info,
13792
14475
  detectedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -13859,8 +14542,8 @@ async function initProject(opts = {}) {
13859
14542
  async function getVersion(projectRoot) {
13860
14543
  const root = projectRoot ?? getProjectRoot();
13861
14544
  const versionPaths = [
13862
- join26(root, "VERSION"),
13863
- join26(root, "..", "VERSION")
14545
+ join25(root, "VERSION"),
14546
+ join25(root, "..", "VERSION")
13864
14547
  ];
13865
14548
  for (const versionPath of versionPaths) {
13866
14549
  try {
@@ -13872,7 +14555,7 @@ async function getVersion(projectRoot) {
13872
14555
  } catch {
13873
14556
  }
13874
14557
  }
13875
- const pkg = await readJson(join26(root, "package.json"));
14558
+ const pkg = await readJson(join25(root, "package.json"));
13876
14559
  if (pkg?.version) {
13877
14560
  return { version: pkg.version };
13878
14561
  }
@@ -13915,8 +14598,8 @@ async function getVersion2(projectRoot) {
13915
14598
  init_paths();
13916
14599
  init_file_utils();
13917
14600
  init_schema();
13918
- import { existsSync as existsSync25, mkdirSync as mkdirSync9, readdirSync as readdirSync8 } from "node:fs";
13919
- import { join as join27 } from "node:path";
14601
+ import { existsSync as existsSync24, mkdirSync as mkdirSync8, readdirSync as readdirSync8 } from "node:fs";
14602
+ import { join as join26 } from "node:path";
13920
14603
 
13921
14604
  // src/core/lifecycle/stages.ts
13922
14605
  var PIPELINE_STAGES = [
@@ -14062,7 +14745,7 @@ var DEFAULT_LIFECYCLE_DATA_DIR = "rcasd";
14062
14745
  function resolveLifecycleDir(epicId, cwd) {
14063
14746
  const cleoDir = getCleoDirAbsolute(cwd);
14064
14747
  for (const dirName of LIFECYCLE_DATA_DIRS) {
14065
- if (existsSync25(join27(cleoDir, dirName, epicId))) {
14748
+ if (existsSync24(join26(cleoDir, dirName, epicId))) {
14066
14749
  return dirName;
14067
14750
  }
14068
14751
  }
@@ -14071,18 +14754,18 @@ function resolveLifecycleDir(epicId, cwd) {
14071
14754
  function getRcsdDir(epicId, cwd) {
14072
14755
  const cleoDir = getCleoDirAbsolute(cwd);
14073
14756
  const dirName = resolveLifecycleDir(epicId, cwd);
14074
- return join27(cleoDir, dirName, epicId);
14757
+ return join26(cleoDir, dirName, epicId);
14075
14758
  }
14076
14759
  function getRcsdManifestPath(epicId, cwd) {
14077
- return join27(getRcsdDir(epicId, cwd), "_manifest.json");
14760
+ return join26(getRcsdDir(epicId, cwd), "_manifest.json");
14078
14761
  }
14079
14762
  function readManifestSync(epicId, cwd) {
14080
- return readJsonFile2(getRcsdManifestPath(epicId, cwd));
14763
+ return readJsonFile(getRcsdManifestPath(epicId, cwd));
14081
14764
  }
14082
14765
  function writeManifestSync(epicId, manifest, cwd) {
14083
14766
  const dir = getRcsdDir(epicId, cwd);
14084
- if (!existsSync25(dir)) {
14085
- mkdirSync9(dir, { recursive: true });
14767
+ if (!existsSync24(dir)) {
14768
+ mkdirSync8(dir, { recursive: true });
14086
14769
  }
14087
14770
  writeJsonFileAtomic(getRcsdManifestPath(epicId, cwd), manifest);
14088
14771
  }
@@ -14536,16 +15219,16 @@ init_platform();
14536
15219
  // src/core/validation/validate-ops.ts
14537
15220
  init_paths();
14538
15221
  init_data_accessor();
14539
- import { readFileSync as readFileSync19, existsSync as existsSync27, appendFileSync as appendFileSync3, mkdirSync as mkdirSync10 } from "node:fs";
15222
+ import { readFileSync as readFileSync18, existsSync as existsSync26, appendFileSync as appendFileSync3, mkdirSync as mkdirSync9 } from "node:fs";
14540
15223
  import { execFileSync as execFileSync2 } from "node:child_process";
14541
- import { join as join29, dirname as dirname9, resolve as resolve5 } from "node:path";
15224
+ import { join as join28, dirname as dirname8, resolve as resolve6 } from "node:path";
14542
15225
 
14543
- // src/mcp/engine/schema-validator.ts
15226
+ // src/core/validation/schema-validator.ts
14544
15227
  init_validation_schemas();
14545
15228
  import AjvModule2 from "ajv";
14546
15229
  import addFormatsModule2 from "ajv-formats";
14547
- import { readFileSync as readFileSync18, existsSync as existsSync26 } from "fs";
14548
- import { join as join28 } from "path";
15230
+ import { readFileSync as readFileSync17, existsSync as existsSync25 } from "fs";
15231
+ import { join as join27 } from "path";
14549
15232
  var Ajv2 = AjvModule2.default || AjvModule2;
14550
15233
  var addFormats2 = addFormatsModule2.default || addFormatsModule2;
14551
15234
  var schemaCache2 = /* @__PURE__ */ new Map();
@@ -14569,14 +15252,14 @@ function resolveSchemaPath2(schemaType) {
14569
15252
  const filename = `${schemaType}.schema.json`;
14570
15253
  const projectRoot = process.env.CLEO_ROOT || process.cwd();
14571
15254
  const paths = [
14572
- join28(projectRoot, "schemas", filename),
14573
- join28(__dirname, "..", "..", "..", "schemas", filename),
14574
- // relative from dist/mcp/engine/
14575
- join28(__dirname, "..", "..", "schemas", filename)
14576
- // relative from dist/mcp/
15255
+ join27(projectRoot, "schemas", filename),
15256
+ join27(__dirname, "..", "..", "..", "schemas", filename),
15257
+ // relative from dist/core/validation/
15258
+ join27(__dirname, "..", "..", "schemas", filename)
15259
+ // relative from dist/core/
14577
15260
  ];
14578
15261
  for (const p of paths) {
14579
- if (existsSync26(p)) {
15262
+ if (existsSync25(p)) {
14580
15263
  return p;
14581
15264
  }
14582
15265
  }
@@ -14592,7 +15275,7 @@ function getValidator(schemaType) {
14592
15275
  return null;
14593
15276
  }
14594
15277
  try {
14595
- const schemaContent = readFileSync18(schemaPath, "utf-8");
15278
+ const schemaContent = readFileSync17(schemaPath, "utf-8");
14596
15279
  const schema = JSON.parse(schemaContent);
14597
15280
  const ajv = getAjv2();
14598
15281
  const validate = ajv.compile(schema);
@@ -14630,7 +15313,7 @@ function validateSchema(schemaType, data) {
14630
15313
  return { valid: false, errors };
14631
15314
  }
14632
15315
 
14633
- // src/mcp/engine/validation-rules.ts
15316
+ // src/core/validation/validation-rules.ts
14634
15317
  function validateTitleDescription(title, description) {
14635
15318
  const violations = [];
14636
15319
  if (!title || title.trim().length === 0) {
@@ -14764,35 +15447,6 @@ function hasErrors(violations) {
14764
15447
  return violations.some((v) => v.severity === "error");
14765
15448
  }
14766
15449
 
14767
- // src/core/tasks/dependency-check.ts
14768
- function detectCircularDeps(taskId, tasks2) {
14769
- const taskMap = new Map(tasks2.map((t) => [t.id, t]));
14770
- const visited = /* @__PURE__ */ new Set();
14771
- const recursionStack = /* @__PURE__ */ new Set();
14772
- const path = [];
14773
- function dfs(id) {
14774
- visited.add(id);
14775
- recursionStack.add(id);
14776
- path.push(id);
14777
- const task = taskMap.get(id);
14778
- if (task?.depends) {
14779
- for (const depId of task.depends) {
14780
- if (!visited.has(depId)) {
14781
- const cycle = dfs(depId);
14782
- if (cycle.length > 0) return cycle;
14783
- } else if (recursionStack.has(depId)) {
14784
- const cycleStart = path.indexOf(depId);
14785
- return [...path.slice(cycleStart), depId];
14786
- }
14787
- }
14788
- }
14789
- path.pop();
14790
- recursionStack.delete(id);
14791
- return [];
14792
- }
14793
- return dfs(taskId);
14794
- }
14795
-
14796
15450
  // src/core/validation/validate-ops.ts
14797
15451
  init_json();
14798
15452
  init_status_registry();
@@ -14801,9 +15455,9 @@ init_status_registry();
14801
15455
  import { isNull as isNull2, sql as sql3 } from "drizzle-orm";
14802
15456
 
14803
15457
  // src/core/validation/validate-ops.ts
14804
- function readJsonFile3(filePath) {
15458
+ function readJsonFile2(filePath) {
14805
15459
  try {
14806
- const raw = readFileSync19(filePath, "utf-8");
15460
+ const raw = readFileSync18(filePath, "utf-8");
14807
15461
  return JSON.parse(raw);
14808
15462
  } catch {
14809
15463
  return null;
@@ -14818,11 +15472,11 @@ async function coreValidateSchema(type, data, projectRoot) {
14818
15472
  throw new Error(`Unknown schema type: ${type}. Valid types: ${validTypes.join(", ")}`);
14819
15473
  }
14820
15474
  if (type === "config") {
14821
- const filePath = join29(projectRoot, ".cleo", "config.json");
14822
- if (!existsSync27(filePath)) {
15475
+ const filePath = join28(projectRoot, ".cleo", "config.json");
15476
+ if (!existsSync26(filePath)) {
14823
15477
  throw new Error("File not found: .cleo/config.json");
14824
15478
  }
14825
- const configData = data ?? readJsonFile3(filePath);
15479
+ const configData = data ?? readJsonFile2(filePath);
14826
15480
  const result = validateSchema("config", configData);
14827
15481
  return { type, valid: result.valid, errors: result.errors, errorCount: result.errors.length };
14828
15482
  }
@@ -14956,7 +15610,7 @@ async function coreValidateProtocol(taskId, protocolType, projectRoot) {
14956
15610
  }
14957
15611
  function coreValidateManifest(projectRoot) {
14958
15612
  const manifestPath = getManifestPath(projectRoot);
14959
- if (!existsSync27(manifestPath)) {
15613
+ if (!existsSync26(manifestPath)) {
14960
15614
  return {
14961
15615
  valid: true,
14962
15616
  totalEntries: 0,
@@ -14966,7 +15620,7 @@ function coreValidateManifest(projectRoot) {
14966
15620
  message: "No manifest file found"
14967
15621
  };
14968
15622
  }
14969
- const content = readFileSync19(manifestPath, "utf-8");
15623
+ const content = readFileSync18(manifestPath, "utf-8");
14970
15624
  const lines = content.split("\n").filter((l) => l.trim());
14971
15625
  let validCount = 0;
14972
15626
  let invalidCount = 0;
@@ -15006,11 +15660,11 @@ function coreValidateOutput(filePath, taskId, projectRoot) {
15006
15660
  if (!filePath) {
15007
15661
  throw new Error("filePath is required");
15008
15662
  }
15009
- const fullPath = resolve5(projectRoot, filePath);
15010
- if (!existsSync27(fullPath)) {
15663
+ const fullPath = resolve6(projectRoot, filePath);
15664
+ if (!existsSync26(fullPath)) {
15011
15665
  throw new Error(`Output file not found: ${filePath}`);
15012
15666
  }
15013
- const content = readFileSync19(fullPath, "utf-8");
15667
+ const content = readFileSync18(fullPath, "utf-8");
15014
15668
  const issues = [];
15015
15669
  if (!content.includes("# ")) {
15016
15670
  issues.push({ code: "O_MISSING_TITLE", message: "Output file should have a markdown title", severity: "warning" });
@@ -15030,11 +15684,11 @@ function coreValidateOutput(filePath, taskId, projectRoot) {
15030
15684
  };
15031
15685
  }
15032
15686
  function parseComplianceEntries(projectRoot) {
15033
- const compliancePath = join29(projectRoot, ".cleo", "metrics", "COMPLIANCE.jsonl");
15034
- if (!existsSync27(compliancePath)) {
15687
+ const compliancePath = join28(projectRoot, ".cleo", "metrics", "COMPLIANCE.jsonl");
15688
+ if (!existsSync26(compliancePath)) {
15035
15689
  return [];
15036
15690
  }
15037
- const content = readFileSync19(compliancePath, "utf-8");
15691
+ const content = readFileSync18(compliancePath, "utf-8");
15038
15692
  const entries = [];
15039
15693
  for (const line of content.split("\n")) {
15040
15694
  const trimmed = line.trim();
@@ -15094,10 +15748,10 @@ function coreComplianceRecord(taskId, result, protocol, violations, projectRoot)
15094
15748
  if (!validResults.includes(result)) {
15095
15749
  throw new Error(`Invalid result: ${result}. Valid: ${validResults.join(", ")}`);
15096
15750
  }
15097
- const compliancePath = join29(projectRoot, ".cleo", "metrics", "COMPLIANCE.jsonl");
15098
- const dir = dirname9(compliancePath);
15099
- if (!existsSync27(dir)) {
15100
- mkdirSync10(dir, { recursive: true });
15751
+ const compliancePath = join28(projectRoot, ".cleo", "metrics", "COMPLIANCE.jsonl");
15752
+ const dir = dirname8(compliancePath);
15753
+ if (!existsSync26(dir)) {
15754
+ mkdirSync9(dir, { recursive: true });
15101
15755
  }
15102
15756
  const entry = {
15103
15757
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -15116,10 +15770,10 @@ function coreComplianceRecord(taskId, result, protocol, violations, projectRoot)
15116
15770
  };
15117
15771
  }
15118
15772
  function coreTestStatus(projectRoot) {
15119
- const testDir = join29(projectRoot, "tests");
15120
- const mcpTestDir = join29(projectRoot, "src", "mcp", "__tests__");
15121
- const hasBatsTests = existsSync27(testDir);
15122
- const hasMcpTests = existsSync27(mcpTestDir);
15773
+ const testDir = join28(projectRoot, "tests");
15774
+ const mcpTestDir = join28(projectRoot, "src", "mcp", "__tests__");
15775
+ const hasBatsTests = existsSync26(testDir);
15776
+ const hasMcpTests = existsSync26(mcpTestDir);
15123
15777
  return {
15124
15778
  batsTests: {
15125
15779
  available: hasBatsTests,
@@ -15261,8 +15915,8 @@ async function coreCoherenceCheck(projectRoot) {
15261
15915
  };
15262
15916
  }
15263
15917
  function coreTestRun(params, projectRoot) {
15264
- const hasVitest = existsSync27(join29(projectRoot, "node_modules", ".bin", "vitest"));
15265
- const hasBats = existsSync27(join29(projectRoot, "tests"));
15918
+ const hasVitest = existsSync26(join28(projectRoot, "node_modules", ".bin", "vitest"));
15919
+ const hasBats = existsSync26(join28(projectRoot, "tests"));
15266
15920
  if (!hasVitest && !hasBats) {
15267
15921
  return {
15268
15922
  ran: false,
@@ -15308,14 +15962,14 @@ function coreTestRun(params, projectRoot) {
15308
15962
  }
15309
15963
  }
15310
15964
  function coreTestCoverage(projectRoot) {
15311
- const coveragePath = join29(projectRoot, "coverage", "coverage-summary.json");
15312
- if (!existsSync27(coveragePath)) {
15965
+ const coveragePath = join28(projectRoot, "coverage", "coverage-summary.json");
15966
+ if (!existsSync26(coveragePath)) {
15313
15967
  return {
15314
15968
  available: false,
15315
15969
  message: "No coverage data found. Run tests with coverage first."
15316
15970
  };
15317
15971
  }
15318
- const coverageData = readJsonFile3(coveragePath);
15972
+ const coverageData = readJsonFile2(coveragePath);
15319
15973
  if (!coverageData) {
15320
15974
  throw new Error("Failed to read coverage data");
15321
15975
  }
@@ -15447,7 +16101,7 @@ function validateTestCoverage(projectRoot) {
15447
16101
  init_platform();
15448
16102
  init_paths();
15449
16103
  init_data_accessor();
15450
- import { readFileSync as readFileSync22, existsSync as existsSync30 } from "node:fs";
16104
+ import { readFileSync as readFileSync21, existsSync as existsSync29 } from "node:fs";
15451
16105
 
15452
16106
  // src/core/orchestration/index.ts
15453
16107
  init_json();
@@ -15576,7 +16230,7 @@ async function analyzeEpic(epicId, cwd, accessor) {
15576
16230
  completedTasks
15577
16231
  };
15578
16232
  }
15579
- async function getReadyTasks(epicId, cwd, accessor) {
16233
+ async function getReadyTasks2(epicId, cwd, accessor) {
15580
16234
  const data = accessor ? await accessor.loadTaskFile() : await readJsonRequired(getTaskPath(cwd));
15581
16235
  const childTasks = data.tasks.filter((t) => t.parentId === epicId);
15582
16236
  const completedIds = new Set(
@@ -15596,7 +16250,7 @@ async function getReadyTasks(epicId, cwd, accessor) {
15596
16250
  });
15597
16251
  }
15598
16252
  async function getNextTask(epicId, cwd, accessor) {
15599
- const readyTasks = await getReadyTasks(epicId, cwd, accessor);
16253
+ const readyTasks = await getReadyTasks2(epicId, cwd, accessor);
15600
16254
  const ready = readyTasks.filter((t) => t.ready);
15601
16255
  if (ready.length === 0) return null;
15602
16256
  return ready[0];
@@ -15957,27 +16611,27 @@ async function getUnblockOpportunities(cwd, accessor) {
15957
16611
  // src/core/orchestration/parallel.ts
15958
16612
  init_errors();
15959
16613
  init_exit_codes();
15960
- import { readFileSync as readFileSync20, writeFileSync as writeFileSync7, existsSync as existsSync28, mkdirSync as mkdirSync11 } from "node:fs";
15961
- import { join as join30, dirname as dirname10 } from "node:path";
16614
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync6, existsSync as existsSync27, mkdirSync as mkdirSync10 } from "node:fs";
16615
+ import { join as join29, dirname as dirname9 } from "node:path";
15962
16616
  init_json();
15963
16617
  init_paths();
15964
16618
  function getParallelStatePath(projectRoot) {
15965
- return join30(projectRoot, ".cleo", "parallel-state.json");
16619
+ return join29(projectRoot, ".cleo", "parallel-state.json");
15966
16620
  }
15967
16621
  function readParallelState(projectRoot) {
15968
16622
  const statePath = getParallelStatePath(projectRoot);
15969
- if (!existsSync28(statePath)) return { active: false };
16623
+ if (!existsSync27(statePath)) return { active: false };
15970
16624
  try {
15971
- return JSON.parse(readFileSync20(statePath, "utf-8"));
16625
+ return JSON.parse(readFileSync19(statePath, "utf-8"));
15972
16626
  } catch {
15973
16627
  return { active: false };
15974
16628
  }
15975
16629
  }
15976
16630
  function writeParallelState(state, projectRoot) {
15977
16631
  const statePath = getParallelStatePath(projectRoot);
15978
- const dir = dirname10(statePath);
15979
- if (!existsSync28(dir)) mkdirSync11(dir, { recursive: true });
15980
- writeFileSync7(statePath, JSON.stringify(state, null, 2), "utf-8");
16632
+ const dir = dirname9(statePath);
16633
+ if (!existsSync27(dir)) mkdirSync10(dir, { recursive: true });
16634
+ writeFileSync6(statePath, JSON.stringify(state, null, 2), "utf-8");
15981
16635
  }
15982
16636
  async function startParallelExecution(epicId, wave, cwd, accessor) {
15983
16637
  const projectRoot = cwd ?? process.cwd();
@@ -16112,8 +16766,8 @@ async function validateSpawnReadiness(taskId, cwd, accessor) {
16112
16766
  // src/core/orchestration/bootstrap.ts
16113
16767
  init_json();
16114
16768
  init_paths();
16115
- import { readFileSync as readFileSync21, existsSync as existsSync29 } from "node:fs";
16116
- import { join as join31 } from "node:path";
16769
+ import { readFileSync as readFileSync20, existsSync as existsSync28 } from "node:fs";
16770
+ import { join as join30 } from "node:path";
16117
16771
  async function buildBrainState(projectRoot, opts, accessor) {
16118
16772
  const speed = opts?.speed || "fast";
16119
16773
  const brain = {
@@ -16125,8 +16779,8 @@ async function buildBrainState(projectRoot, opts, accessor) {
16125
16779
  };
16126
16780
  try {
16127
16781
  const sessionsPath = getSessionsPath(projectRoot);
16128
- if (existsSync29(sessionsPath)) {
16129
- const sessionsData = JSON.parse(readFileSync21(sessionsPath, "utf-8"));
16782
+ if (existsSync28(sessionsPath)) {
16783
+ const sessionsData = JSON.parse(readFileSync20(sessionsPath, "utf-8"));
16130
16784
  const activeSession = (sessionsData.sessions ?? []).find(
16131
16785
  (s) => s.status === "active"
16132
16786
  );
@@ -16181,9 +16835,9 @@ async function buildBrainState(projectRoot, opts, accessor) {
16181
16835
  blockedBy: b.depends || []
16182
16836
  }));
16183
16837
  try {
16184
- const decisionLogPath = join31(projectRoot, ".cleo", "decision-log.jsonl");
16185
- if (existsSync29(decisionLogPath)) {
16186
- const content = readFileSync21(decisionLogPath, "utf-8").trim();
16838
+ const decisionLogPath = join30(projectRoot, ".cleo", "decision-log.jsonl");
16839
+ if (existsSync28(decisionLogPath)) {
16840
+ const content = readFileSync20(decisionLogPath, "utf-8").trim();
16187
16841
  if (content) {
16188
16842
  const entries = content.split("\n").filter((l) => l.trim()).map((l) => {
16189
16843
  try {
@@ -16203,9 +16857,9 @@ async function buildBrainState(projectRoot, opts, accessor) {
16203
16857
  } catch {
16204
16858
  }
16205
16859
  try {
16206
- const contextStatePath = join31(projectRoot, ".cleo", ".context-state.json");
16207
- if (existsSync29(contextStatePath)) {
16208
- const state = JSON.parse(readFileSync21(contextStatePath, "utf-8"));
16860
+ const contextStatePath = join30(projectRoot, ".cleo", ".context-state.json");
16861
+ if (existsSync28(contextStatePath)) {
16862
+ const state = JSON.parse(readFileSync20(contextStatePath, "utf-8"));
16209
16863
  const percentage = state.contextWindow?.percentage ?? 0;
16210
16864
  const factors = [];
16211
16865
  if (percentage > 80) factors.push("high_context_usage");
@@ -16354,7 +17008,7 @@ async function orchestrateReady(epicId, projectRoot) {
16354
17008
  try {
16355
17009
  const root = projectRoot || resolveProjectRoot();
16356
17010
  const accessor = await getAccessor(root);
16357
- const readyTasks = await getReadyTasks(epicId, root, accessor);
17011
+ const readyTasks = await getReadyTasks2(epicId, root, accessor);
16358
17012
  const ready = readyTasks.filter((t) => t.ready);
16359
17013
  return {
16360
17014
  success: true,
@@ -16393,7 +17047,7 @@ async function orchestrateNext(epicId, projectRoot) {
16393
17047
  }
16394
17048
  };
16395
17049
  }
16396
- const readyTasks = await getReadyTasks(epicId, root, accessor);
17050
+ const readyTasks = await getReadyTasks2(epicId, root, accessor);
16397
17051
  const ready = readyTasks.filter((t) => t.ready);
16398
17052
  return {
16399
17053
  success: true,
@@ -16433,11 +17087,11 @@ async function orchestrateContext(epicId, projectRoot) {
16433
17087
  }
16434
17088
  const estimatedTokens = taskCount * 100;
16435
17089
  const manifestPath = getManifestPath(root);
16436
- let manifestEntries = 0;
16437
- if (existsSync30(manifestPath)) {
17090
+ let manifestEntries2 = 0;
17091
+ if (existsSync29(manifestPath)) {
16438
17092
  try {
16439
- const content = readFileSync22(manifestPath, "utf-8");
16440
- manifestEntries = content.split("\n").filter((l) => l.trim()).length;
17093
+ const content = readFileSync21(manifestPath, "utf-8");
17094
+ manifestEntries2 = content.split("\n").filter((l) => l.trim()).length;
16441
17095
  } catch {
16442
17096
  }
16443
17097
  }
@@ -16446,7 +17100,7 @@ async function orchestrateContext(epicId, projectRoot) {
16446
17100
  data: {
16447
17101
  epicId: epicId || null,
16448
17102
  taskCount,
16449
- manifestEntries,
17103
+ manifestEntries: manifestEntries2,
16450
17104
  estimatedTokens,
16451
17105
  recommendation: estimatedTokens > 5e3 ? "Consider using manifest summaries instead of full task details" : "Context usage is within recommended limits",
16452
17106
  limits: {
@@ -16473,7 +17127,7 @@ async function orchestrateValidate(taskId, projectRoot) {
16473
17127
  return engineError("E_VALIDATION", err.message);
16474
17128
  }
16475
17129
  }
16476
- async function orchestrateSpawn(taskId, protocolType, projectRoot) {
17130
+ async function orchestrateSpawn(taskId, protocolType, projectRoot, tier) {
16477
17131
  if (!taskId) {
16478
17132
  return engineError("E_INVALID_INPUT", "taskId is required");
16479
17133
  }
@@ -16494,9 +17148,11 @@ async function orchestrateSpawn(taskId, protocolType, projectRoot) {
16494
17148
  spawnContext: {
16495
17149
  taskId: spawnContext.taskId,
16496
17150
  protocol: spawnContext.protocol,
16497
- protocolType: protocolType || spawnContext.protocol
17151
+ protocolType: protocolType || spawnContext.protocol,
17152
+ tier: tier ?? null
16498
17153
  },
16499
17154
  protocolType: protocolType || spawnContext.protocol,
17155
+ tier: tier ?? null,
16500
17156
  tokenResolution: spawnContext.tokenResolution
16501
17157
  }
16502
17158
  };
@@ -16519,7 +17175,7 @@ async function orchestrateStartup(epicId, projectRoot) {
16519
17175
  }
16520
17176
  const children = tasks2.filter((t) => t.parentId === epicId);
16521
17177
  const waves = computeWaves(children);
16522
- const readyTasks = await getReadyTasks(epicId, root, accessor);
17178
+ const readyTasks = await getReadyTasks2(epicId, root, accessor);
16523
17179
  const ready = readyTasks.filter((t) => t.ready);
16524
17180
  return {
16525
17181
  success: true,
@@ -16655,8 +17311,8 @@ async function orchestrateCheck(projectRoot) {
16655
17311
 
16656
17312
  // src/core/memory/engine-compat.ts
16657
17313
  init_paths();
16658
- import { readFileSync as readFileSync25, writeFileSync as writeFileSync10, appendFileSync as appendFileSync6, existsSync as existsSync33, mkdirSync as mkdirSync14 } from "node:fs";
16659
- import { resolve as resolve6, dirname as dirname11 } from "node:path";
17314
+ import { readFileSync as readFileSync24, writeFileSync as writeFileSync9, appendFileSync as appendFileSync6, existsSync as existsSync32, mkdirSync as mkdirSync13 } from "node:fs";
17315
+ import { resolve as resolve7, dirname as dirname10 } from "node:path";
16660
17316
 
16661
17317
  // src/core/memory/index.ts
16662
17318
  init_json();
@@ -16698,25 +17354,25 @@ function filterManifestEntries(entries, filter) {
16698
17354
 
16699
17355
  // src/core/memory/patterns.ts
16700
17356
  import { randomBytes as randomBytes7 } from "node:crypto";
16701
- import { readFileSync as readFileSync23, writeFileSync as writeFileSync8, appendFileSync as appendFileSync4, mkdirSync as mkdirSync12, existsSync as existsSync31 } from "node:fs";
16702
- import { join as join32 } from "node:path";
17357
+ import { readFileSync as readFileSync22, writeFileSync as writeFileSync7, appendFileSync as appendFileSync4, mkdirSync as mkdirSync11, existsSync as existsSync30 } from "node:fs";
17358
+ import { join as join31 } from "node:path";
16703
17359
  function getMemoryDir(projectRoot) {
16704
- const memDir = join32(projectRoot, ".cleo", "memory");
16705
- if (!existsSync31(memDir)) {
16706
- mkdirSync12(memDir, { recursive: true });
17360
+ const memDir = join31(projectRoot, ".cleo", "memory");
17361
+ if (!existsSync30(memDir)) {
17362
+ mkdirSync11(memDir, { recursive: true });
16707
17363
  }
16708
17364
  return memDir;
16709
17365
  }
16710
17366
  function getPatternsPath(projectRoot) {
16711
- return join32(getMemoryDir(projectRoot), "patterns.jsonl");
17367
+ return join31(getMemoryDir(projectRoot), "patterns.jsonl");
16712
17368
  }
16713
17369
  function generatePatternId() {
16714
17370
  return `P${randomBytes7(4).toString("hex")}`;
16715
17371
  }
16716
17372
  function readPatterns(projectRoot) {
16717
17373
  const path = getPatternsPath(projectRoot);
16718
- if (!existsSync31(path)) return [];
16719
- const content = readFileSync23(path, "utf-8").trim();
17374
+ if (!existsSync30(path)) return [];
17375
+ const content = readFileSync22(path, "utf-8").trim();
16720
17376
  if (!content) return [];
16721
17377
  const entries = [];
16722
17378
  for (const line of content.split("\n")) {
@@ -16753,7 +17409,7 @@ function storePattern(projectRoot, params) {
16753
17409
  }
16754
17410
  const path2 = getPatternsPath(projectRoot);
16755
17411
  const updated = existing.map((e) => JSON.stringify(e)).join("\n") + "\n";
16756
- writeFileSync8(path2, updated, "utf-8");
17412
+ writeFileSync7(path2, updated, "utf-8");
16757
17413
  return duplicate;
16758
17414
  }
16759
17415
  const entry = {
@@ -16825,25 +17481,25 @@ function patternStats(projectRoot) {
16825
17481
 
16826
17482
  // src/core/memory/learnings.ts
16827
17483
  import { randomBytes as randomBytes8 } from "node:crypto";
16828
- import { readFileSync as readFileSync24, writeFileSync as writeFileSync9, appendFileSync as appendFileSync5, mkdirSync as mkdirSync13, existsSync as existsSync32 } from "node:fs";
16829
- import { join as join33 } from "node:path";
17484
+ import { readFileSync as readFileSync23, writeFileSync as writeFileSync8, appendFileSync as appendFileSync5, mkdirSync as mkdirSync12, existsSync as existsSync31 } from "node:fs";
17485
+ import { join as join32 } from "node:path";
16830
17486
  function getMemoryDir2(projectRoot) {
16831
- const memDir = join33(projectRoot, ".cleo", "memory");
16832
- if (!existsSync32(memDir)) {
16833
- mkdirSync13(memDir, { recursive: true });
17487
+ const memDir = join32(projectRoot, ".cleo", "memory");
17488
+ if (!existsSync31(memDir)) {
17489
+ mkdirSync12(memDir, { recursive: true });
16834
17490
  }
16835
17491
  return memDir;
16836
17492
  }
16837
17493
  function getLearningsPath(projectRoot) {
16838
- return join33(getMemoryDir2(projectRoot), "learnings.jsonl");
17494
+ return join32(getMemoryDir2(projectRoot), "learnings.jsonl");
16839
17495
  }
16840
17496
  function generateLearningId() {
16841
17497
  return `L${randomBytes8(4).toString("hex")}`;
16842
17498
  }
16843
17499
  function readLearnings(projectRoot) {
16844
17500
  const path = getLearningsPath(projectRoot);
16845
- if (!existsSync32(path)) return [];
16846
- const content = readFileSync24(path, "utf-8").trim();
17501
+ if (!existsSync31(path)) return [];
17502
+ const content = readFileSync23(path, "utf-8").trim();
16847
17503
  if (!content) return [];
16848
17504
  const entries = [];
16849
17505
  for (const line of content.split("\n")) {
@@ -16882,7 +17538,7 @@ function storeLearning(projectRoot, params) {
16882
17538
  }
16883
17539
  const path2 = getLearningsPath(projectRoot);
16884
17540
  const updated = existing.map((e) => JSON.stringify(e)).join("\n") + "\n";
16885
- writeFileSync9(path2, updated, "utf-8");
17541
+ writeFileSync8(path2, updated, "utf-8");
16886
17542
  return duplicate;
16887
17543
  }
16888
17544
  const entry = {
@@ -16957,7 +17613,7 @@ function resolveRoot(projectRoot) {
16957
17613
  function readManifestEntries(projectRoot) {
16958
17614
  const manifestPath = getManifestPath2(projectRoot);
16959
17615
  try {
16960
- const content = readFileSync25(manifestPath, "utf-8");
17616
+ const content = readFileSync24(manifestPath, "utf-8");
16961
17617
  const entries = [];
16962
17618
  const lines = content.split("\n");
16963
17619
  for (const line of lines) {
@@ -16992,9 +17648,9 @@ function memoryShow(researchId, projectRoot) {
16992
17648
  const root = resolveRoot(projectRoot);
16993
17649
  let fileContent = null;
16994
17650
  try {
16995
- const filePath = resolve6(root, entry.file);
16996
- if (existsSync33(filePath)) {
16997
- fileContent = readFileSync25(filePath, "utf-8");
17651
+ const filePath = resolve7(root, entry.file);
17652
+ if (existsSync32(filePath)) {
17653
+ fileContent = readFileSync24(filePath, "utf-8");
16998
17654
  }
16999
17655
  } catch {
17000
17656
  }
@@ -17122,7 +17778,7 @@ function memoryLink(taskId, researchId, notes, projectRoot) {
17122
17778
  }
17123
17779
  entry.linked_tasks.push(taskId);
17124
17780
  const content = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
17125
- writeFileSync10(manifestPath, content, "utf-8");
17781
+ writeFileSync9(manifestPath, content, "utf-8");
17126
17782
  return { success: true, data: { taskId, researchId, linked: true, notes: notes || null } };
17127
17783
  }
17128
17784
  function memoryManifestAppend(entry, projectRoot) {
@@ -17142,9 +17798,9 @@ function memoryManifestAppend(entry, projectRoot) {
17142
17798
  return { success: false, error: { code: "E_VALIDATION_FAILED", message: `Invalid manifest entry: ${errors.join(", ")}` } };
17143
17799
  }
17144
17800
  const manifestPath = getManifestPath2(projectRoot);
17145
- const dir = dirname11(manifestPath);
17146
- if (!existsSync33(dir)) {
17147
- mkdirSync14(dir, { recursive: true });
17801
+ const dir = dirname10(manifestPath);
17802
+ if (!existsSync32(dir)) {
17803
+ mkdirSync13(dir, { recursive: true });
17148
17804
  }
17149
17805
  const serialized = JSON.stringify(entry);
17150
17806
  appendFileSync6(manifestPath, serialized + "\n", "utf-8");
@@ -17163,14 +17819,14 @@ function memoryManifestArchive(beforeDate, projectRoot) {
17163
17819
  if (toArchive.length === 0) {
17164
17820
  return { success: true, data: { archived: 0, remaining: entries.length, message: "No entries found before the specified date" } };
17165
17821
  }
17166
- const archiveDir = dirname11(archivePath);
17167
- if (!existsSync33(archiveDir)) {
17168
- mkdirSync14(archiveDir, { recursive: true });
17822
+ const archiveDir = dirname10(archivePath);
17823
+ if (!existsSync32(archiveDir)) {
17824
+ mkdirSync13(archiveDir, { recursive: true });
17169
17825
  }
17170
17826
  const archiveContent = toArchive.map((e) => JSON.stringify(e)).join("\n") + "\n";
17171
17827
  appendFileSync6(archivePath, archiveContent, "utf-8");
17172
17828
  const remainingContent = toKeep.length > 0 ? toKeep.map((e) => JSON.stringify(e)).join("\n") + "\n" : "";
17173
- writeFileSync10(manifestPath, remainingContent, "utf-8");
17829
+ writeFileSync9(manifestPath, remainingContent, "utf-8");
17174
17830
  return { success: true, data: { archived: toArchive.length, remaining: toKeep.length, archiveFile: getManifestArchivePath() } };
17175
17831
  }
17176
17832
  function memoryContradictions(projectRoot, params) {
@@ -17252,16 +17908,16 @@ function memoryInject(protocolType, params, projectRoot) {
17252
17908
  }
17253
17909
  const root = resolveRoot(projectRoot);
17254
17910
  const protocolLocations = [
17255
- resolve6(root, "protocols", `${protocolType}.md`),
17256
- resolve6(root, "skills", "_shared", `${protocolType}.md`),
17257
- resolve6(root, "agents", "cleo-subagent", "protocols", `${protocolType}.md`)
17911
+ resolve7(root, "protocols", `${protocolType}.md`),
17912
+ resolve7(root, "skills", "_shared", `${protocolType}.md`),
17913
+ resolve7(root, "agents", "cleo-subagent", "protocols", `${protocolType}.md`)
17258
17914
  ];
17259
17915
  let protocolContent = null;
17260
17916
  let protocolPath = null;
17261
17917
  for (const loc of protocolLocations) {
17262
- if (existsSync33(loc)) {
17918
+ if (existsSync32(loc)) {
17263
17919
  try {
17264
- protocolContent = readFileSync25(loc, "utf-8");
17920
+ protocolContent = readFileSync24(loc, "utf-8");
17265
17921
  protocolPath = loc.replace(root + "/", "");
17266
17922
  break;
17267
17923
  } catch {
@@ -17350,11 +18006,11 @@ init_data_accessor();
17350
18006
  // src/core/release/release-manifest.ts
17351
18007
  init_json();
17352
18008
  init_paths();
17353
- import { existsSync as existsSync34, mkdirSync as mkdirSync15 } from "node:fs";
18009
+ import { existsSync as existsSync33, mkdirSync as mkdirSync14 } from "node:fs";
17354
18010
  import { execFileSync as execFileSync3 } from "node:child_process";
17355
- import { dirname as dirname12, join as join34 } from "node:path";
18011
+ import { dirname as dirname11, join as join33 } from "node:path";
17356
18012
  function getReleasesPath(cwd) {
17357
- return join34(getCleoDirAbsolute(cwd), "releases.json");
18013
+ return join33(getCleoDirAbsolute(cwd), "releases.json");
17358
18014
  }
17359
18015
  async function readReleases(cwd) {
17360
18016
  const data = await readJson(getReleasesPath(cwd));
@@ -17362,9 +18018,9 @@ async function readReleases(cwd) {
17362
18018
  }
17363
18019
  async function writeReleases(index2, cwd) {
17364
18020
  const releasesPath = getReleasesPath(cwd);
17365
- const dir = dirname12(releasesPath);
17366
- if (!existsSync34(dir)) {
17367
- mkdirSync15(dir, { recursive: true });
18021
+ const dir = dirname11(releasesPath);
18022
+ if (!existsSync33(dir)) {
18023
+ mkdirSync14(dir, { recursive: true });
17368
18024
  }
17369
18025
  await saveJson(releasesPath, index2);
17370
18026
  }
@@ -17631,7 +18287,7 @@ async function rollbackRelease(version, reason, cwd) {
17631
18287
  };
17632
18288
  }
17633
18289
  async function readPushPolicy(cwd) {
17634
- const configPath = join34(getCleoDirAbsolute(cwd), "config.json");
18290
+ const configPath = join33(getCleoDirAbsolute(cwd), "config.json");
17635
18291
  const config = await readJson(configPath);
17636
18292
  if (!config) return void 0;
17637
18293
  const release2 = config.release;
@@ -17715,8 +18371,8 @@ async function hasManifestEntry(version, projectRoot) {
17715
18371
  }
17716
18372
  async function loadTasks2(projectRoot) {
17717
18373
  if (projectRoot) {
17718
- const taskPath = getDataPath2(projectRoot, "todo.json");
17719
- const taskData = readJsonFile2(taskPath);
18374
+ const taskPath = getDataPath(projectRoot, "todo.json");
18375
+ const taskData = readJsonFile(taskPath);
17720
18376
  return taskData?.tasks ?? [];
17721
18377
  }
17722
18378
  try {
@@ -17725,8 +18381,8 @@ async function loadTasks2(projectRoot) {
17725
18381
  return taskFile?.tasks ?? [];
17726
18382
  } catch {
17727
18383
  const root = resolveProjectRoot();
17728
- const taskPath = getDataPath2(root, "todo.json");
17729
- const taskData = readJsonFile2(taskPath);
18384
+ const taskPath = getDataPath(root, "todo.json");
18385
+ const taskData = readJsonFile(taskPath);
17730
18386
  return taskData?.tasks ?? [];
17731
18387
  }
17732
18388
  }
@@ -17846,8 +18502,8 @@ async function releasePush(version, remote, projectRoot, opts) {
17846
18502
 
17847
18503
  // src/dispatch/engines/template-parser.ts
17848
18504
  init_platform();
17849
- import { readFileSync as readFileSync26, readdirSync as readdirSync9, existsSync as existsSync35 } from "fs";
17850
- import { join as join35 } from "path";
18505
+ import { readFileSync as readFileSync25, readdirSync as readdirSync9, existsSync as existsSync34 } from "fs";
18506
+ import { join as join34 } from "path";
17851
18507
  import { parse as parseYaml } from "yaml";
17852
18508
  var SUFFIX_PATTERNS = ["_report", "_request", "_question"];
17853
18509
  function deriveSubcommand(filename) {
@@ -17862,8 +18518,8 @@ function deriveSubcommand(filename) {
17862
18518
  return firstWord.toLowerCase();
17863
18519
  }
17864
18520
  function parseTemplateFile(templateDir, filename) {
17865
- const filePath = join35(templateDir, filename);
17866
- const raw = readFileSync26(filePath, "utf-8");
18521
+ const filePath = join34(templateDir, filename);
18522
+ const raw = readFileSync25(filePath, "utf-8");
17867
18523
  const parsed = parseYaml(raw);
17868
18524
  const name = typeof parsed.name === "string" ? parsed.name : filename;
17869
18525
  const titlePrefix = typeof parsed.title === "string" ? parsed.title : "";
@@ -17912,8 +18568,8 @@ function parseTemplateFile(templateDir, filename) {
17912
18568
  };
17913
18569
  }
17914
18570
  function parseIssueTemplates(projectRoot) {
17915
- const templateDir = join35(projectRoot, ".github", "ISSUE_TEMPLATE");
17916
- if (!existsSync35(templateDir)) {
18571
+ const templateDir = join34(projectRoot, ".github", "ISSUE_TEMPLATE");
18572
+ if (!existsSync34(templateDir)) {
17917
18573
  return engineError("E_NOT_FOUND", `Issue template directory not found: ${templateDir}`);
17918
18574
  }
17919
18575
  let files;
@@ -17965,7 +18621,7 @@ async function generateTemplateConfig(projectRoot) {
17965
18621
  if (!result.success || !result.data) {
17966
18622
  return result.error ? { success: false, error: result.error } : engineError("E_PARSE_ERROR", "Failed to parse issue templates");
17967
18623
  }
17968
- const outputPath = getDataPath2(projectRoot, "issue-templates.json");
18624
+ const outputPath = getDataPath(projectRoot, "issue-templates.json");
17969
18625
  try {
17970
18626
  writeJsonFileAtomic(outputPath, result.data);
17971
18627
  } catch (error) {
@@ -17993,7 +18649,6 @@ function validateLabels2(labels, repoLabels) {
17993
18649
  }
17994
18650
 
17995
18651
  // src/dispatch/domains/tasks.ts
17996
- var logger2 = getLogger("domain:tasks");
17997
18652
  var TasksHandler = class {
17998
18653
  projectRoot;
17999
18654
  constructor() {
@@ -18015,14 +18670,26 @@ var TasksHandler = class {
18015
18670
  return this.wrapEngineResult(result, "query", "tasks", operation, startTime);
18016
18671
  }
18017
18672
  case "list": {
18018
- const result = await taskList(this.projectRoot, params);
18673
+ const result = await taskList(this.projectRoot, {
18674
+ parent: params?.parent,
18675
+ status: params?.status,
18676
+ limit: params?.limit,
18677
+ compact: params?.compact
18678
+ });
18019
18679
  return this.wrapEngineResult(result, "query", "tasks", operation, startTime);
18020
18680
  }
18021
18681
  case "find": {
18022
18682
  const result = await taskFind(
18023
18683
  this.projectRoot,
18024
18684
  params?.query,
18025
- params?.limit
18685
+ params?.limit,
18686
+ {
18687
+ id: params?.id,
18688
+ exact: params?.exact,
18689
+ status: params?.status,
18690
+ includeArchive: params?.includeArchive,
18691
+ offset: params?.offset
18692
+ }
18026
18693
  );
18027
18694
  return this.wrapEngineResult(result, "query", "tasks", operation, startTime);
18028
18695
  }
@@ -18044,12 +18711,22 @@ var TasksHandler = class {
18044
18711
  return this.wrapEngineResult(result, "query", "tasks", operation, startTime);
18045
18712
  }
18046
18713
  case "depends": {
18714
+ const action = params?.action;
18715
+ if (action === "overview") {
18716
+ const result2 = await taskDepsOverview(this.projectRoot);
18717
+ return this.wrapEngineResult(result2, "query", "tasks", operation, startTime);
18718
+ }
18719
+ if (action === "cycles") {
18720
+ const result2 = await taskDepsCycles(this.projectRoot);
18721
+ return this.wrapEngineResult(result2, "query", "tasks", operation, startTime);
18722
+ }
18047
18723
  const taskId = params?.taskId;
18048
18724
  if (!taskId) {
18049
- return this.errorResponse("query", "tasks", operation, "E_INVALID_INPUT", "taskId is required", startTime);
18725
+ return this.errorResponse("query", "tasks", operation, "E_INVALID_INPUT", "taskId is required (or use action: overview|cycles)", startTime);
18050
18726
  }
18051
18727
  const direction = params?.direction;
18052
- const result = await taskDepends(this.projectRoot, taskId, direction);
18728
+ const tree = params?.tree;
18729
+ const result = await taskDepends(this.projectRoot, taskId, direction, tree);
18053
18730
  return this.wrapEngineResult(result, "query", "tasks", operation, startTime);
18054
18731
  }
18055
18732
  case "analyze": {
@@ -18219,10 +18896,10 @@ var TasksHandler = class {
18219
18896
  }
18220
18897
  case "relates.add": {
18221
18898
  const taskId = params?.taskId;
18222
- const relatedId = params?.relatedId;
18899
+ const relatedId = params?.relatedId ?? params?.targetId;
18223
18900
  const type = params?.type;
18224
18901
  if (!taskId || !relatedId || !type) {
18225
- return this.errorResponse("mutate", "tasks", operation, "E_INVALID_INPUT", "taskId, relatedId, and type are required", startTime);
18902
+ return this.errorResponse("mutate", "tasks", operation, "E_INVALID_INPUT", "taskId, relatedId (or targetId), and type are required", startTime);
18226
18903
  }
18227
18904
  const result = await taskRelatesAdd(
18228
18905
  this.projectRoot,
@@ -18316,7 +18993,7 @@ var TasksHandler = class {
18316
18993
  }
18317
18994
  handleError(gateway, domain, operation, error, startTime) {
18318
18995
  const message = error instanceof Error ? error.message : String(error);
18319
- logger2.error({ gateway, domain, operation, err: error }, message);
18996
+ getLogger("domain:tasks").error({ gateway, domain, operation, err: error }, message);
18320
18997
  return {
18321
18998
  _meta: dispatchMeta(gateway, domain, operation, startTime),
18322
18999
  success: false,
@@ -18354,7 +19031,6 @@ function unbindSession() {
18354
19031
  }
18355
19032
 
18356
19033
  // src/dispatch/domains/session.ts
18357
- var logger3 = getLogger("domain:session");
18358
19034
  var SessionHandler = class {
18359
19035
  projectRoot;
18360
19036
  constructor() {
@@ -18475,7 +19151,7 @@ var SessionHandler = class {
18475
19151
  gradeMode: params?.grade ?? false
18476
19152
  });
18477
19153
  } catch {
18478
- logger3.warn({ sessionId: session.id }, "Session context already bound, skipping bindSession");
19154
+ getLogger("domain:session").warn({ sessionId: session.id }, "Session context already bound, skipping bindSession");
18479
19155
  }
18480
19156
  }
18481
19157
  return this.wrapEngineResult(result, "mutate", "session", operation, startTime);
@@ -18586,7 +19262,7 @@ var SessionHandler = class {
18586
19262
  }
18587
19263
  handleError(gateway, domain, operation, error, startTime) {
18588
19264
  const message = error instanceof Error ? error.message : String(error);
18589
- logger3.error({ gateway, domain, operation, err: error }, message);
19265
+ getLogger("domain:session").error({ gateway, domain, operation, err: error }, message);
18590
19266
  return {
18591
19267
  _meta: dispatchMeta(gateway, domain, operation, startTime),
18592
19268
  success: false,
@@ -18598,7 +19274,6 @@ var SessionHandler = class {
18598
19274
  // src/dispatch/domains/check.ts
18599
19275
  init_paths();
18600
19276
  init_logger();
18601
- var logger4 = getLogger("domain:check");
18602
19277
  var CheckHandler = class {
18603
19278
  projectRoot;
18604
19279
  constructor() {
@@ -18757,7 +19432,7 @@ var CheckHandler = class {
18757
19432
  }
18758
19433
  handleError(gateway, domain, operation, error, startTime) {
18759
19434
  const message = error instanceof Error ? error.message : String(error);
18760
- logger4.error({ gateway, domain, operation, err: error }, message);
19435
+ getLogger("domain:check").error({ gateway, domain, operation, err: error }, message);
18761
19436
  return {
18762
19437
  _meta: dispatchMeta(gateway, domain, operation, startTime),
18763
19438
  success: false,
@@ -18771,8 +19446,8 @@ init_paths();
18771
19446
  init_logger();
18772
19447
 
18773
19448
  // src/core/adrs/parse.ts
18774
- import { readFileSync as readFileSync27 } from "node:fs";
18775
- import { join as join36 } from "node:path";
19449
+ import { readFileSync as readFileSync26 } from "node:fs";
19450
+ import { join as join35 } from "node:path";
18776
19451
  function extractAdrId(filename) {
18777
19452
  const match = filename.match(/^(ADR-\d+)/);
18778
19453
  return match ? match[1] : filename.replace(".md", "");
@@ -18794,8 +19469,8 @@ function extractTitle(content) {
18794
19469
  return match ? match[1].trim() : "Untitled";
18795
19470
  }
18796
19471
  function parseAdrFile(filePath, projectRoot) {
18797
- const absolutePath = filePath.startsWith("/") ? filePath : join36(projectRoot, filePath);
18798
- const content = readFileSync27(absolutePath, "utf-8");
19472
+ const absolutePath = filePath.startsWith("/") ? filePath : join35(projectRoot, filePath);
19473
+ const content = readFileSync26(absolutePath, "utf-8");
18799
19474
  const filename = filePath.split("/").pop();
18800
19475
  return {
18801
19476
  id: extractAdrId(filename),
@@ -18806,27 +19481,27 @@ function parseAdrFile(filePath, projectRoot) {
18806
19481
  }
18807
19482
 
18808
19483
  // src/core/adrs/validate.ts
18809
- import { readFileSync as readFileSync28, readdirSync as readdirSync10, existsSync as existsSync36 } from "node:fs";
18810
- import { join as join37 } from "node:path";
19484
+ import { readFileSync as readFileSync27, readdirSync as readdirSync10, existsSync as existsSync35 } from "node:fs";
19485
+ import { join as join36 } from "node:path";
18811
19486
  import AjvModule3 from "ajv";
18812
19487
  var Ajv3 = AjvModule3.default ?? AjvModule3;
18813
19488
  async function validateAllAdrs(projectRoot) {
18814
- const adrsDir = join37(projectRoot, ".cleo", "adrs");
18815
- const schemaPath = join37(projectRoot, "schemas", "adr-frontmatter.schema.json");
18816
- if (!existsSync36(schemaPath)) {
19489
+ const adrsDir = join36(projectRoot, ".cleo", "adrs");
19490
+ const schemaPath = join36(projectRoot, "schemas", "adr-frontmatter.schema.json");
19491
+ if (!existsSync35(schemaPath)) {
18817
19492
  return {
18818
19493
  valid: false,
18819
19494
  errors: [{ file: "schemas/adr-frontmatter.schema.json", field: "schema", message: "Schema file not found" }],
18820
19495
  checked: 0
18821
19496
  };
18822
19497
  }
18823
- if (!existsSync36(adrsDir)) {
19498
+ if (!existsSync35(adrsDir)) {
18824
19499
  return { valid: true, errors: [], checked: 0 };
18825
19500
  }
18826
- const schema = JSON.parse(readFileSync28(schemaPath, "utf-8"));
19501
+ const schema = JSON.parse(readFileSync27(schemaPath, "utf-8"));
18827
19502
  const ajv = new Ajv3({ allErrors: true });
18828
19503
  const validate = ajv.compile(schema);
18829
- const files = readdirSync10(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).map((f) => join37(adrsDir, f));
19504
+ const files = readdirSync10(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).map((f) => join36(adrsDir, f));
18830
19505
  const errors = [];
18831
19506
  for (const filePath of files) {
18832
19507
  const record = parseAdrFile(filePath, projectRoot);
@@ -18847,8 +19522,8 @@ async function validateAllAdrs(projectRoot) {
18847
19522
  // src/core/adrs/sync.ts
18848
19523
  init_sqlite();
18849
19524
  init_schema();
18850
- import { readFileSync as readFileSync29, readdirSync as readdirSync11, writeFileSync as writeFileSync11, existsSync as existsSync37 } from "node:fs";
18851
- import { join as join38 } from "node:path";
19525
+ import { readFileSync as readFileSync28, readdirSync as readdirSync11, writeFileSync as writeFileSync10, existsSync as existsSync36 } from "node:fs";
19526
+ import { join as join37 } from "node:path";
18852
19527
  import { eq as eq7 } from "drizzle-orm";
18853
19528
  function extractAdrIdFromRef(ref) {
18854
19529
  const m = ref.match(/^(ADR-\d+)/);
@@ -18861,7 +19536,7 @@ function collectAdrFiles(dir) {
18861
19536
  const results = [];
18862
19537
  for (const entry of readdirSync11(dir, { withFileTypes: true })) {
18863
19538
  if (entry.isDirectory()) {
18864
- const sub = join38(dir, entry.name);
19539
+ const sub = join37(dir, entry.name);
18865
19540
  for (const f of readdirSync11(sub)) {
18866
19541
  if (f.endsWith(".md") && /^ADR-\d+/.test(f)) {
18867
19542
  results.push({ file: f, relPath: `${entry.name}/${f}` });
@@ -18874,23 +19549,23 @@ function collectAdrFiles(dir) {
18874
19549
  return results.sort((a, b) => a.file.localeCompare(b.file));
18875
19550
  }
18876
19551
  async function syncAdrsToDb(projectRoot) {
18877
- const adrsDir = join38(projectRoot, ".cleo", "adrs");
19552
+ const adrsDir = join37(projectRoot, ".cleo", "adrs");
18878
19553
  const result = { inserted: 0, updated: 0, skipped: 0, errors: [] };
18879
- if (!existsSync37(adrsDir)) {
19554
+ if (!existsSync36(adrsDir)) {
18880
19555
  return result;
18881
19556
  }
18882
19557
  const db = await getDb(projectRoot);
18883
19558
  const now = (/* @__PURE__ */ new Date()).toISOString();
18884
19559
  const allFiles = collectAdrFiles(adrsDir);
18885
19560
  const activeFiles = allFiles.filter((f) => !f.relPath.includes("/"));
18886
- const manifestEntries = [];
19561
+ const manifestEntries2 = [];
18887
19562
  for (const { file, relPath } of activeFiles) {
18888
19563
  try {
18889
- const filePath = join38(adrsDir, relPath);
19564
+ const filePath = join37(adrsDir, relPath);
18890
19565
  const record = parseAdrFile(filePath, projectRoot);
18891
19566
  const fm = record.frontmatter;
18892
19567
  const dbRelPath = `.cleo/adrs/${relPath}`;
18893
- const content = readFileSync29(filePath, "utf-8");
19568
+ const content = readFileSync28(filePath, "utf-8");
18894
19569
  const supersedesId = fm.Supersedes ? extractAdrIdFromRef(fm.Supersedes) : null;
18895
19570
  const supersededById = fm["Superseded By"] ? extractAdrIdFromRef(fm["Superseded By"]) : null;
18896
19571
  const amendsId = fm.Amends ? extractAdrIdFromRef(fm.Amends) : null;
@@ -18932,7 +19607,7 @@ async function syncAdrsToDb(projectRoot) {
18932
19607
  }
18933
19608
  for (const { relPath } of allFiles) {
18934
19609
  try {
18935
- const filePath = join38(adrsDir, relPath);
19610
+ const filePath = join37(adrsDir, relPath);
18936
19611
  const record = parseAdrFile(filePath, projectRoot);
18937
19612
  const fm = record.frontmatter;
18938
19613
  const entry = {
@@ -18955,28 +19630,28 @@ async function syncAdrsToDb(projectRoot) {
18955
19630
  if (fm.Summary) entry["summary"] = fm.Summary;
18956
19631
  if (fm.Keywords) entry["keywords"] = fm.Keywords.split(",").map((s) => s.trim()).filter(Boolean);
18957
19632
  if (fm.Topics) entry["topics"] = fm.Topics.split(",").map((s) => s.trim()).filter(Boolean);
18958
- manifestEntries.push(entry);
19633
+ manifestEntries2.push(entry);
18959
19634
  } catch {
18960
19635
  }
18961
19636
  }
18962
- writeFileSync11(
18963
- join38(adrsDir, "MANIFEST.jsonl"),
18964
- manifestEntries.map((e) => JSON.stringify(e)).join("\n") + "\n",
19637
+ writeFileSync10(
19638
+ join37(adrsDir, "MANIFEST.jsonl"),
19639
+ manifestEntries2.map((e) => JSON.stringify(e)).join("\n") + "\n",
18965
19640
  "utf-8"
18966
19641
  );
18967
19642
  return result;
18968
19643
  }
18969
19644
 
18970
19645
  // src/core/adrs/list.ts
18971
- import { readdirSync as readdirSync12, existsSync as existsSync38 } from "node:fs";
18972
- import { join as join39 } from "node:path";
19646
+ import { readdirSync as readdirSync12, existsSync as existsSync37 } from "node:fs";
19647
+ import { join as join38 } from "node:path";
18973
19648
  async function listAdrs(projectRoot, opts) {
18974
- const adrsDir = join39(projectRoot, ".cleo", "adrs");
18975
- if (!existsSync38(adrsDir)) {
19649
+ const adrsDir = join38(projectRoot, ".cleo", "adrs");
19650
+ if (!existsSync37(adrsDir)) {
18976
19651
  return { adrs: [], total: 0 };
18977
19652
  }
18978
19653
  const files = readdirSync12(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).sort();
18979
- const records = files.map((f) => parseAdrFile(join39(adrsDir, f), projectRoot));
19654
+ const records = files.map((f) => parseAdrFile(join38(adrsDir, f), projectRoot));
18980
19655
  const filtered = records.filter((r) => {
18981
19656
  if (opts?.status && r.frontmatter.Status !== opts.status) return false;
18982
19657
  if (opts?.since && r.frontmatter.Date < opts.since) return false;
@@ -18995,20 +19670,20 @@ async function listAdrs(projectRoot, opts) {
18995
19670
  }
18996
19671
 
18997
19672
  // src/core/adrs/show.ts
18998
- import { existsSync as existsSync39, readdirSync as readdirSync13 } from "node:fs";
18999
- import { join as join40 } from "node:path";
19673
+ import { existsSync as existsSync38, readdirSync as readdirSync13 } from "node:fs";
19674
+ import { join as join39 } from "node:path";
19000
19675
  async function showAdr(projectRoot, adrId) {
19001
- const adrsDir = join40(projectRoot, ".cleo", "adrs");
19002
- if (!existsSync39(adrsDir)) return null;
19676
+ const adrsDir = join39(projectRoot, ".cleo", "adrs");
19677
+ if (!existsSync38(adrsDir)) return null;
19003
19678
  const files = readdirSync13(adrsDir).filter((f) => f.startsWith(adrId) && f.endsWith(".md"));
19004
19679
  if (files.length === 0) return null;
19005
- const filePath = join40(adrsDir, files[0]);
19680
+ const filePath = join39(adrsDir, files[0]);
19006
19681
  return parseAdrFile(filePath, projectRoot);
19007
19682
  }
19008
19683
 
19009
19684
  // src/core/adrs/find.ts
19010
- import { readdirSync as readdirSync14, existsSync as existsSync40 } from "node:fs";
19011
- import { join as join41 } from "node:path";
19685
+ import { readdirSync as readdirSync14, existsSync as existsSync39 } from "node:fs";
19686
+ import { join as join40 } from "node:path";
19012
19687
  function normalise(s) {
19013
19688
  return s.toLowerCase().replace(/[^a-z0-9\s]/g, " ").replace(/\s+/g, " ").trim();
19014
19689
  }
@@ -19025,8 +19700,8 @@ function matchedTerms(target, terms) {
19025
19700
  return terms.filter((term) => t.includes(term));
19026
19701
  }
19027
19702
  async function findAdrs(projectRoot, query, opts) {
19028
- const adrsDir = join41(projectRoot, ".cleo", "adrs");
19029
- if (!existsSync40(adrsDir)) {
19703
+ const adrsDir = join40(projectRoot, ".cleo", "adrs");
19704
+ if (!existsSync39(adrsDir)) {
19030
19705
  return { adrs: [], query, total: 0 };
19031
19706
  }
19032
19707
  const files = readdirSync14(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).sort();
@@ -19035,7 +19710,7 @@ async function findAdrs(projectRoot, query, opts) {
19035
19710
  const filterKeywords = opts?.keywords ? parseTags(opts.keywords) : null;
19036
19711
  const results = [];
19037
19712
  for (const file of files) {
19038
- const record = parseAdrFile(join41(adrsDir, file), projectRoot);
19713
+ const record = parseAdrFile(join40(adrsDir, file), projectRoot);
19039
19714
  const fm = record.frontmatter;
19040
19715
  if (opts?.status && fm.Status !== opts.status) continue;
19041
19716
  if (filterTopics && filterTopics.length > 0) {
@@ -19098,7 +19773,6 @@ init_schema();
19098
19773
  import { eq as eq8, and as and2 } from "drizzle-orm";
19099
19774
 
19100
19775
  // src/dispatch/domains/admin.ts
19101
- var logger5 = getLogger("domain:admin");
19102
19776
  var AdminHandler = class {
19103
19777
  projectRoot;
19104
19778
  constructor() {
@@ -19181,6 +19855,14 @@ var AdminHandler = class {
19181
19855
  case "help": {
19182
19856
  const tier = typeof params?.tier === "number" ? params.tier : 0;
19183
19857
  const ops = OPERATIONS.filter((op) => op.tier <= tier);
19858
+ const getCostHint = (domain, op) => {
19859
+ const key = `${domain}.${op}`;
19860
+ const heavyOps = ["tasks.list", "tasks.tree", "admin.log", "admin.stats", "tasks.analyze"];
19861
+ const moderateOps = ["tasks.show", "tasks.blockers", "tasks.depends", "admin.health", "admin.dash", "admin.help"];
19862
+ if (heavyOps.includes(key)) return "heavy";
19863
+ if (moderateOps.includes(key)) return "moderate";
19864
+ return "minimal";
19865
+ };
19184
19866
  const tierGuidance = {
19185
19867
  0: "Tier 0: Core task and session operations (tasks, session, admin). 80% of use cases.",
19186
19868
  1: "Tier 1: + memory/research and check/validate operations. 15% of use cases.",
@@ -19192,14 +19874,22 @@ var AdminHandler = class {
19192
19874
  data: {
19193
19875
  tier,
19194
19876
  operationCount: ops.length,
19877
+ quickStart: tier === 0 ? [
19878
+ "cleo_query tasks.current \u2014 check active task (~100 tokens)",
19879
+ "cleo_query tasks.next \u2014 get suggestion (~300 tokens)",
19880
+ "cleo_query tasks.find {query} \u2014 search tasks (~200 tokens)",
19881
+ "cleo_mutate tasks.start {taskId} \u2014 begin work (~100 tokens)",
19882
+ "cleo_mutate tasks.complete {taskId} \u2014 finish task (~200 tokens)"
19883
+ ] : void 0,
19195
19884
  operations: ops.map((op) => ({
19196
19885
  gateway: op.gateway,
19197
19886
  domain: op.domain,
19198
19887
  operation: op.operation,
19199
- description: op.description
19888
+ description: op.description,
19889
+ costHint: getCostHint(op.domain, op.operation)
19200
19890
  })),
19201
19891
  guidance: tierGuidance[tier] ?? tierGuidance[0],
19202
- escalation: tier < 2 ? `For more operations: ct ops --tier ${tier + 1} or cleo_query({domain:"admin",operation:"help",params:{tier:${tier + 1}}})` : "Full operation set displayed."
19892
+ escalation: tier < 2 ? `For more operations: cleo_query({domain:"admin",operation:"help",params:{tier:${tier + 1}}})` : "Full operation set displayed."
19203
19893
  }
19204
19894
  };
19205
19895
  }
@@ -19450,7 +20140,7 @@ var AdminHandler = class {
19450
20140
  }
19451
20141
  handleError(gateway, domain, operation, error, startTime) {
19452
20142
  const message = error instanceof Error ? error.message : String(error);
19453
- logger5.error({ gateway, domain, operation, err: error }, message);
20143
+ getLogger("domain:admin").error({ gateway, domain, operation, err: error }, message);
19454
20144
  return {
19455
20145
  _meta: dispatchMeta(gateway, domain, operation, startTime),
19456
20146
  success: false,
@@ -19462,7 +20152,6 @@ var AdminHandler = class {
19462
20152
  // src/dispatch/domains/memory.ts
19463
20153
  init_paths();
19464
20154
  init_logger();
19465
- var logger6 = getLogger("domain:memory");
19466
20155
  var MemoryHandler = class {
19467
20156
  projectRoot;
19468
20157
  constructor() {
@@ -19695,7 +20384,7 @@ var MemoryHandler = class {
19695
20384
  }
19696
20385
  handleError(gateway, domain, operation, error, startTime) {
19697
20386
  const message = error instanceof Error ? error.message : String(error);
19698
- logger6.error({ gateway, domain, operation, err: error }, message);
20387
+ getLogger("domain:memory").error({ gateway, domain, operation, err: error }, message);
19699
20388
  return {
19700
20389
  _meta: dispatchMeta(gateway, domain, operation, startTime),
19701
20390
  success: false,
@@ -19707,7 +20396,6 @@ var MemoryHandler = class {
19707
20396
  // src/dispatch/domains/orchestrate.ts
19708
20397
  init_paths();
19709
20398
  init_logger();
19710
- var logger7 = getLogger("domain:orchestrate");
19711
20399
  var OrchestrateHandler = class {
19712
20400
  projectRoot;
19713
20401
  constructor() {
@@ -19842,7 +20530,8 @@ var OrchestrateHandler = class {
19842
20530
  );
19843
20531
  }
19844
20532
  const protocolType = params?.protocolType;
19845
- const result = await orchestrateSpawn(taskId, protocolType, this.projectRoot);
20533
+ const tier = params?.tier;
20534
+ const result = await orchestrateSpawn(taskId, protocolType, this.projectRoot, tier);
19846
20535
  return this.wrapEngineResult(result, "mutate", operation, startTime);
19847
20536
  }
19848
20537
  case "validate": {
@@ -19976,7 +20665,7 @@ var OrchestrateHandler = class {
19976
20665
  }
19977
20666
  handleError(gateway, operation, error, startTime) {
19978
20667
  const message = error instanceof Error ? error.message : String(error);
19979
- logger7.error({ gateway, domain: "orchestrate", operation, err: error }, message);
20668
+ getLogger("domain:orchestrate").error({ gateway, domain: "orchestrate", operation, err: error }, message);
19980
20669
  return this.errorResponse(
19981
20670
  gateway,
19982
20671
  operation,
@@ -19990,7 +20679,6 @@ var OrchestrateHandler = class {
19990
20679
  // src/dispatch/domains/pipeline.ts
19991
20680
  init_paths();
19992
20681
  init_logger();
19993
- var logger8 = getLogger("domain:pipeline");
19994
20682
  var PipelineHandler = class {
19995
20683
  projectRoot;
19996
20684
  constructor() {
@@ -20391,7 +21079,7 @@ var PipelineHandler = class {
20391
21079
  }
20392
21080
  handleError(gateway, operation, error, startTime) {
20393
21081
  const message = error instanceof Error ? error.message : String(error);
20394
- logger8.error({ gateway, domain: "pipeline", operation, err: error }, message);
21082
+ getLogger("domain:pipeline").error({ gateway, domain: "pipeline", operation, err: error }, message);
20395
21083
  return this.errorResponse(
20396
21084
  gateway,
20397
21085
  operation,
@@ -20454,8 +21142,8 @@ import { execFileSync as execFileSync5 } from "node:child_process";
20454
21142
 
20455
21143
  // src/core/issue/template-parser.ts
20456
21144
  init_paths();
20457
- import { existsSync as existsSync42, readFileSync as readFileSync30, readdirSync as readdirSync15, writeFileSync as writeFileSync12 } from "node:fs";
20458
- import { join as join43, basename as basename7 } from "node:path";
21145
+ import { existsSync as existsSync41, readFileSync as readFileSync29, readdirSync as readdirSync15, writeFileSync as writeFileSync11 } from "node:fs";
21146
+ import { join as join42, basename as basename6 } from "node:path";
20459
21147
  var TEMPLATE_DIR = ".github/ISSUE_TEMPLATE";
20460
21148
  var CACHE_FILE = "issue-templates.json";
20461
21149
  var SUBCOMMAND_MAP = {
@@ -20512,10 +21200,10 @@ function extractYamlArray(content, field) {
20512
21200
  return items;
20513
21201
  }
20514
21202
  function parseTemplateFile2(filePath) {
20515
- if (!existsSync42(filePath)) return null;
21203
+ if (!existsSync41(filePath)) return null;
20516
21204
  try {
20517
- const content = readFileSync30(filePath, "utf-8");
20518
- const fileName = basename7(filePath);
21205
+ const content = readFileSync29(filePath, "utf-8");
21206
+ const fileName = basename6(filePath);
20519
21207
  const stem = fileName.replace(/\.ya?ml$/, "");
20520
21208
  const name = extractYamlField(content, "name");
20521
21209
  const description = extractYamlField(content, "description");
@@ -20530,12 +21218,12 @@ function parseTemplateFile2(filePath) {
20530
21218
  }
20531
21219
  function parseIssueTemplates2(projectDir) {
20532
21220
  const dir = projectDir ?? getProjectRoot();
20533
- const templateDir = join43(dir, TEMPLATE_DIR);
20534
- if (!existsSync42(templateDir)) return [];
21221
+ const templateDir = join42(dir, TEMPLATE_DIR);
21222
+ if (!existsSync41(templateDir)) return [];
20535
21223
  const templates = [];
20536
21224
  for (const file of readdirSync15(templateDir)) {
20537
21225
  if (!file.endsWith(".yml") && !file.endsWith(".yaml")) continue;
20538
- const template = parseTemplateFile2(join43(templateDir, file));
21226
+ const template = parseTemplateFile2(join42(templateDir, file));
20539
21227
  if (template) templates.push(template);
20540
21228
  }
20541
21229
  return templates;
@@ -20544,10 +21232,10 @@ function getTemplateConfig(cwd) {
20544
21232
  const projectDir = cwd ?? getProjectRoot();
20545
21233
  const liveTemplates = parseIssueTemplates2(projectDir);
20546
21234
  if (liveTemplates.length > 0) return liveTemplates;
20547
- const cachePath = join43(getCleoDir(cwd), CACHE_FILE);
20548
- if (existsSync42(cachePath)) {
21235
+ const cachePath = join42(getCleoDir(cwd), CACHE_FILE);
21236
+ if (existsSync41(cachePath)) {
20549
21237
  try {
20550
- const cached = JSON.parse(readFileSync30(cachePath, "utf-8"));
21238
+ const cached = JSON.parse(readFileSync29(cachePath, "utf-8"));
20551
21239
  if (cached.templates?.length > 0) return cached.templates;
20552
21240
  } catch {
20553
21241
  }
@@ -20672,7 +21360,6 @@ import {
20672
21360
  injectAll,
20673
21361
  buildInjectionContent
20674
21362
  } from "@cleocode/caamp";
20675
- var logger9 = getLogger("domain:tools");
20676
21363
  var ToolsHandler = class {
20677
21364
  projectRoot;
20678
21365
  constructor() {
@@ -21307,7 +21994,7 @@ var ToolsHandler = class {
21307
21994
  }
21308
21995
  handleError(gateway, domain, operation, error, startTime) {
21309
21996
  const message = error instanceof Error ? error.message : String(error);
21310
- logger9.error({ gateway, domain, operation, err: error }, message);
21997
+ getLogger("domain:tools").error({ gateway, domain, operation, err: error }, message);
21311
21998
  return this.errorResponse(
21312
21999
  gateway,
21313
22000
  domain,
@@ -21321,11 +22008,10 @@ var ToolsHandler = class {
21321
22008
 
21322
22009
  // src/dispatch/domains/nexus.ts
21323
22010
  init_logger();
21324
- var logger10 = getLogger("domain:nexus");
21325
22011
  var NexusHandler = class {
21326
22012
  async query(operation, _params) {
21327
22013
  const startTime = Date.now();
21328
- logger10.warn({ operation }, `Nexus domain not yet implemented: ${operation}`);
22014
+ getLogger("domain:nexus").warn({ operation }, `Nexus domain not yet implemented: ${operation}`);
21329
22015
  return {
21330
22016
  _meta: dispatchMeta("query", "nexus", operation, startTime),
21331
22017
  success: false,
@@ -21334,7 +22020,7 @@ var NexusHandler = class {
21334
22020
  }
21335
22021
  async mutate(operation, _params) {
21336
22022
  const startTime = Date.now();
21337
- logger10.warn({ operation }, `Nexus domain not yet implemented: ${operation}`);
22023
+ getLogger("domain:nexus").warn({ operation }, `Nexus domain not yet implemented: ${operation}`);
21338
22024
  return {
21339
22025
  _meta: dispatchMeta("mutate", "nexus", operation, startTime),
21340
22026
  success: false,
@@ -21352,8 +22038,8 @@ init_logger();
21352
22038
 
21353
22039
  // src/core/sharing/index.ts
21354
22040
  import { readFile as readFile7, writeFile as writeFile4 } from "node:fs/promises";
21355
- import { existsSync as existsSync43, readdirSync as readdirSync16, statSync as statSync5 } from "node:fs";
21356
- import { join as join44, relative } from "node:path";
22041
+ import { existsSync as existsSync42, readdirSync as readdirSync16, statSync as statSync5 } from "node:fs";
22042
+ import { join as join43, relative } from "node:path";
21357
22043
  init_paths();
21358
22044
  var GITIGNORE_START = "# CLEO:SHARING:START - Auto-managed by cleo sharing sync";
21359
22045
  var GITIGNORE_END = "# CLEO:SHARING:END";
@@ -21381,7 +22067,7 @@ function collectCleoFiles(cleoDir) {
21381
22067
  const entries = readdirSync16(dir);
21382
22068
  for (const entry of entries) {
21383
22069
  if (entry === ".git") continue;
21384
- const fullPath = join44(dir, entry);
22070
+ const fullPath = join43(dir, entry);
21385
22071
  const relPath = relative(cleoDir, fullPath);
21386
22072
  try {
21387
22073
  const stat2 = statSync5(fullPath);
@@ -21441,7 +22127,7 @@ function generateGitignoreEntries(sharing) {
21441
22127
  async function syncGitignore(cwd) {
21442
22128
  const config = await loadConfig2(cwd);
21443
22129
  const projectRoot = getProjectRoot(cwd);
21444
- const gitignorePath = join44(projectRoot, ".gitignore");
22130
+ const gitignorePath = join43(projectRoot, ".gitignore");
21445
22131
  const entries = generateGitignoreEntries(config.sharing);
21446
22132
  const managedSection = [
21447
22133
  "",
@@ -21451,7 +22137,7 @@ async function syncGitignore(cwd) {
21451
22137
  ""
21452
22138
  ].join("\n");
21453
22139
  let content = "";
21454
- if (existsSync43(gitignorePath)) {
22140
+ if (existsSync42(gitignorePath)) {
21455
22141
  content = await readFile7(gitignorePath, "utf-8");
21456
22142
  }
21457
22143
  const startIdx = content.indexOf(GITIGNORE_START);
@@ -21476,8 +22162,8 @@ init_data_accessor();
21476
22162
  init_paths();
21477
22163
  import { createHash as createHash6 } from "node:crypto";
21478
22164
  import { readFile as readFile8, writeFile as writeFile5, mkdir as mkdir7 } from "node:fs/promises";
21479
- import { existsSync as existsSync44 } from "node:fs";
21480
- import { join as join45, dirname as dirname13 } from "node:path";
22165
+ import { existsSync as existsSync43 } from "node:fs";
22166
+ import { join as join44, dirname as dirname12 } from "node:path";
21481
22167
  var SNAPSHOT_FORMAT_VERSION = "1.0.0";
21482
22168
  function toSnapshotTask(task) {
21483
22169
  return {
@@ -21530,8 +22216,8 @@ async function exportSnapshot(cwd) {
21530
22216
  };
21531
22217
  }
21532
22218
  async function writeSnapshot(snapshot, outputPath) {
21533
- const dir = dirname13(outputPath);
21534
- if (!existsSync44(dir)) {
22219
+ const dir = dirname12(outputPath);
22220
+ if (!existsSync43(dir)) {
21535
22221
  await mkdir7(dir, { recursive: true });
21536
22222
  }
21537
22223
  await writeFile5(outputPath, JSON.stringify(snapshot, null, 2) + "\n");
@@ -21547,7 +22233,7 @@ async function readSnapshot(inputPath) {
21547
22233
  function getDefaultSnapshotPath(cwd) {
21548
22234
  const cleoDir = getCleoDirAbsolute(cwd);
21549
22235
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
21550
- return join45(cleoDir, "snapshots", `snapshot-${timestamp}.json`);
22236
+ return join44(cleoDir, "snapshots", `snapshot-${timestamp}.json`);
21551
22237
  }
21552
22238
  async function importSnapshot(snapshot, cwd) {
21553
22239
  const accessor = await getAccessor(cwd);
@@ -21614,10 +22300,10 @@ init_paths();
21614
22300
  init_git_checkpoint();
21615
22301
  import { execFile as execFile6 } from "node:child_process";
21616
22302
  import { promisify as promisify6 } from "node:util";
21617
- import { resolve as resolve7 } from "node:path";
22303
+ import { resolve as resolve8 } from "node:path";
21618
22304
  var execFileAsync4 = promisify6(execFile6);
21619
22305
  async function cleoGitExec(args, cleoDir) {
21620
- const abs = resolve7(cleoDir);
22306
+ const abs = resolve8(cleoDir);
21621
22307
  const result = await execFileAsync4("git", args, {
21622
22308
  cwd: abs,
21623
22309
  env: makeCleoGitEnv(cleoDir),
@@ -21804,7 +22490,6 @@ async function getSyncStatus(remote = "origin", cwd) {
21804
22490
  }
21805
22491
 
21806
22492
  // src/dispatch/domains/sharing.ts
21807
- var logger11 = getLogger("domain:sharing");
21808
22493
  var SharingHandler = class {
21809
22494
  projectRoot;
21810
22495
  constructor() {
@@ -21989,7 +22674,7 @@ var SharingHandler = class {
21989
22674
  }
21990
22675
  handleError(gateway, operation, error, startTime) {
21991
22676
  const message = error instanceof Error ? error.message : String(error);
21992
- logger11.error({ gateway, operation, err: error }, message);
22677
+ getLogger("domain:sharing").error({ gateway, operation, err: error }, message);
21993
22678
  return {
21994
22679
  _meta: dispatchMeta(gateway, "sharing", operation, startTime),
21995
22680
  success: false,
@@ -22052,7 +22737,7 @@ function createSessionResolver(cliSessionLookup) {
22052
22737
  // src/dispatch/lib/security.ts
22053
22738
  init_schema();
22054
22739
  init_status_registry();
22055
- import { resolve as resolve8, normalize, relative as relative2, isAbsolute } from "path";
22740
+ import { resolve as resolve9, normalize, relative as relative2, isAbsolute } from "path";
22056
22741
  var SecurityError = class extends Error {
22057
22742
  constructor(message, code = "E_SECURITY_VIOLATION", field) {
22058
22743
  super(message);
@@ -22128,12 +22813,12 @@ function sanitizePath(path, projectRoot) {
22128
22813
  "path"
22129
22814
  );
22130
22815
  }
22131
- const normalizedRoot = resolve8(projectRoot);
22816
+ const normalizedRoot = resolve9(projectRoot);
22132
22817
  let resolvedPath;
22133
22818
  if (isAbsolute(trimmedPath)) {
22134
22819
  resolvedPath = normalize(trimmedPath);
22135
22820
  } else {
22136
- resolvedPath = resolve8(normalizedRoot, trimmedPath);
22821
+ resolvedPath = resolve9(normalizedRoot, trimmedPath);
22137
22822
  }
22138
22823
  const relativePath = relative2(normalizedRoot, resolvedPath);
22139
22824
  if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
@@ -22182,6 +22867,27 @@ function validateEnum(value, allowed, fieldName) {
22182
22867
  }
22183
22868
  var ALL_VALID_STATUSES = [...TASK_STATUSES, ...MANIFEST_STATUSES];
22184
22869
  var VALID_PRIORITIES2 = TASK_PRIORITIES;
22870
+ var ARRAY_PARAMS = /* @__PURE__ */ new Set([
22871
+ "labels",
22872
+ "addLabels",
22873
+ "removeLabels",
22874
+ "depends",
22875
+ "addDepends",
22876
+ "removeDepends",
22877
+ "files",
22878
+ "acceptance",
22879
+ "findings",
22880
+ "sources",
22881
+ "tasks",
22882
+ "removeTasks",
22883
+ "taskIds"
22884
+ ]);
22885
+ function ensureArray(value, separator = ",") {
22886
+ if (value === void 0 || value === null) return void 0;
22887
+ if (Array.isArray(value)) return value.map((v) => typeof v === "string" ? v.trim() : String(v));
22888
+ if (typeof value === "string") return value.split(separator).map((s) => s.trim()).filter(Boolean);
22889
+ return [String(value)];
22890
+ }
22185
22891
  function sanitizeParams(params, projectRoot, context) {
22186
22892
  if (!params) {
22187
22893
  return params;
@@ -22241,6 +22947,11 @@ function sanitizeParams(params, projectRoot, context) {
22241
22947
  continue;
22242
22948
  }
22243
22949
  }
22950
+ for (const key of Object.keys(sanitized)) {
22951
+ if (ARRAY_PARAMS.has(key) && sanitized[key] !== void 0) {
22952
+ sanitized[key] = ensureArray(sanitized[key]);
22953
+ }
22954
+ }
22244
22955
  return sanitized;
22245
22956
  }
22246
22957
 
@@ -24175,6 +24886,142 @@ function createProtocolEnforcement(strictMode = true) {
24175
24886
 
24176
24887
  // src/dispatch/adapters/mcp.ts
24177
24888
  init_audit();
24889
+
24890
+ // src/dispatch/lib/projections.ts
24891
+ var PROJECTIONS = {
24892
+ minimal: {
24893
+ allowedDomains: ["tasks", "session", "admin"],
24894
+ excludeFields: ["notes", "history", "metadata._internal", "auditLog"],
24895
+ maxDepth: 2
24896
+ },
24897
+ standard: {
24898
+ allowedDomains: ["tasks", "session", "admin", "memory", "check", "pipeline", "tools", "validate"],
24899
+ excludeFields: ["metadata._internal", "auditLog"],
24900
+ maxDepth: 4
24901
+ },
24902
+ orchestrator: {
24903
+ allowedDomains: [
24904
+ "tasks",
24905
+ "session",
24906
+ "admin",
24907
+ "memory",
24908
+ "check",
24909
+ "pipeline",
24910
+ "orchestrate",
24911
+ "tools",
24912
+ "sharing",
24913
+ "nexus",
24914
+ "validate",
24915
+ "lifecycle",
24916
+ "release",
24917
+ "system"
24918
+ ],
24919
+ maxDepth: 8
24920
+ }
24921
+ };
24922
+ var VALID_TIERS = /* @__PURE__ */ new Set(["minimal", "standard", "orchestrator"]);
24923
+ function resolveTier(params, sessionScope) {
24924
+ const mvi = params?._mviTier;
24925
+ if (typeof mvi === "string" && VALID_TIERS.has(mvi)) return mvi;
24926
+ if (sessionScope?.type === "epic") return "orchestrator";
24927
+ return "standard";
24928
+ }
24929
+
24930
+ // src/dispatch/middleware/projection.ts
24931
+ function pruneDepth(data, maxDepth, currentDepth = 0) {
24932
+ if (data === null || data === void 0 || typeof data !== "object") {
24933
+ return data;
24934
+ }
24935
+ if (currentDepth >= maxDepth) {
24936
+ if (Array.isArray(data)) {
24937
+ return `[Array(${data.length})]`;
24938
+ }
24939
+ return "[Object]";
24940
+ }
24941
+ if (Array.isArray(data)) {
24942
+ return data.map((item) => pruneDepth(item, maxDepth, currentDepth + 1));
24943
+ }
24944
+ const result = {};
24945
+ for (const [key, value] of Object.entries(data)) {
24946
+ result[key] = pruneDepth(value, maxDepth, currentDepth + 1);
24947
+ }
24948
+ return result;
24949
+ }
24950
+ function applyProjection(data, config) {
24951
+ if (!data || typeof data !== "object") return data;
24952
+ let result = data;
24953
+ if (config.excludeFields?.length) {
24954
+ const obj = { ...data };
24955
+ for (const field of config.excludeFields) {
24956
+ const parts = field.split(".");
24957
+ if (parts.length === 1) {
24958
+ delete obj[parts[0]];
24959
+ } else {
24960
+ let current = obj;
24961
+ for (let i = 0; i < parts.length - 1; i++) {
24962
+ const val = current?.[parts[i]];
24963
+ if (val && typeof val === "object") {
24964
+ current[parts[i]] = { ...val };
24965
+ current = current[parts[i]];
24966
+ } else {
24967
+ current = void 0;
24968
+ break;
24969
+ }
24970
+ }
24971
+ if (current) {
24972
+ delete current[parts[parts.length - 1]];
24973
+ }
24974
+ }
24975
+ }
24976
+ result = obj;
24977
+ }
24978
+ if (config.maxDepth !== void 0) {
24979
+ result = pruneDepth(result, config.maxDepth);
24980
+ }
24981
+ return result;
24982
+ }
24983
+ function createProjectionMiddleware() {
24984
+ return async (req, next) => {
24985
+ const session = getBoundSession();
24986
+ const tier = resolveTier(req.params, session?.scope ?? null);
24987
+ const config = PROJECTIONS[tier];
24988
+ if (req.params) {
24989
+ delete req.params["_mviTier"];
24990
+ }
24991
+ if (req.source === "mcp" && req.domain === "tasks" && req.operation === "list") {
24992
+ if (req.params && req.params["compact"] === void 0) {
24993
+ req.params["compact"] = true;
24994
+ }
24995
+ }
24996
+ if (!config.allowedDomains.includes(req.domain)) {
24997
+ return {
24998
+ _meta: {
24999
+ gateway: req.gateway,
25000
+ domain: req.domain,
25001
+ operation: req.operation,
25002
+ version: "1.0.0",
25003
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
25004
+ duration_ms: 0,
25005
+ source: req.source,
25006
+ requestId: req.requestId
25007
+ },
25008
+ success: false,
25009
+ error: {
25010
+ code: "E_INVALID_OPERATION",
25011
+ exitCode: 2,
25012
+ message: `Operation not available at '${tier}' tier. Domain '${req.domain}' requires a higher tier.`
25013
+ }
25014
+ };
25015
+ }
25016
+ const response = await next();
25017
+ if (response.success && response.data !== void 0) {
25018
+ response.data = applyProjection(response.data, config);
25019
+ }
25020
+ return response;
25021
+ };
25022
+ }
25023
+
25024
+ // src/dispatch/adapters/mcp.ts
24178
25025
  init_paths();
24179
25026
  var _dispatcher = null;
24180
25027
  function initMcpDispatcher(config = {}) {
@@ -24187,6 +25034,8 @@ function initMcpDispatcher(config = {}) {
24187
25034
  createSessionResolver(),
24188
25035
  // T4959: session identity first
24189
25036
  createSanitizer(() => getProjectRoot()),
25037
+ createProjectionMiddleware(),
25038
+ // T5096: MVI tier-based domain/field projection
24190
25039
  createFieldFilter(),
24191
25040
  createRateLimiter(config.rateLimiting),
24192
25041
  createVerificationGates(strictMode),