@cleocode/cleo 2026.2.9 → 2026.3.1

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 +2113 -1295
  3. package/dist/cli/index.js.map +4 -4
  4. package/dist/mcp/index.js +1796 -865
  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 +2 -2
  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) {
@@ -884,6 +939,65 @@ var init_node_sqlite_adapter = __esm({
884
939
  }
885
940
  });
886
941
 
942
+ // src/store/sqlite-backup.ts
943
+ import { existsSync as existsSync3, mkdirSync, readdirSync, statSync, unlinkSync } from "node:fs";
944
+ import { join as join3 } from "node:path";
945
+ function formatTimestamp(d) {
946
+ const pad = (n, len = 2) => String(n).padStart(len, "0");
947
+ return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
948
+ }
949
+ function rotateSnapshots(backupDir) {
950
+ try {
951
+ const files = readdirSync(backupDir).filter((f) => f.match(/^tasks-\d{8}-\d{6}\.db$/)).map((f) => ({ name: f, path: join3(backupDir, f), mtimeMs: statSync(join3(backupDir, f)).mtimeMs })).sort((a, b) => a.mtimeMs - b.mtimeMs);
952
+ while (files.length >= MAX_SNAPSHOTS) {
953
+ const oldest = files.shift();
954
+ unlinkSync(oldest.path);
955
+ }
956
+ } catch {
957
+ }
958
+ }
959
+ async function vacuumIntoBackup(opts = {}) {
960
+ const now = Date.now();
961
+ if (!opts.force && now - _lastBackupEpoch < DEBOUNCE_MS) {
962
+ return;
963
+ }
964
+ try {
965
+ const cleoDir = getCleoDir(opts.cwd);
966
+ const backupDir = join3(cleoDir, "backups", "sqlite");
967
+ mkdirSync(backupDir, { recursive: true });
968
+ const db = getNativeDb();
969
+ if (!db) return;
970
+ const dest = join3(backupDir, `tasks-${formatTimestamp(/* @__PURE__ */ new Date())}.db`);
971
+ db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
972
+ rotateSnapshots(backupDir);
973
+ const safeDest = dest.replace(/'/g, "''");
974
+ db.exec(`VACUUM INTO '${safeDest}'`);
975
+ _lastBackupEpoch = Date.now();
976
+ } catch {
977
+ }
978
+ }
979
+ function listSqliteBackups(cwd) {
980
+ try {
981
+ const cleoDir = getCleoDir(cwd);
982
+ const backupDir = join3(cleoDir, "backups", "sqlite");
983
+ if (!existsSync3(backupDir)) return [];
984
+ return readdirSync(backupDir).filter((f) => f.match(/^tasks-\d{8}-\d{6}\.db$/)).map((f) => ({ name: f, path: join3(backupDir, f), mtimeMs: statSync(join3(backupDir, f)).mtimeMs })).sort((a, b) => b.mtimeMs - a.mtimeMs);
985
+ } catch {
986
+ return [];
987
+ }
988
+ }
989
+ var MAX_SNAPSHOTS, DEBOUNCE_MS, _lastBackupEpoch;
990
+ var init_sqlite_backup = __esm({
991
+ "src/store/sqlite-backup.ts"() {
992
+ "use strict";
993
+ init_paths();
994
+ init_sqlite();
995
+ MAX_SNAPSHOTS = 10;
996
+ DEBOUNCE_MS = 3e4;
997
+ _lastBackupEpoch = 0;
998
+ }
999
+ });
1000
+
887
1001
  // src/store/sqlite.ts
888
1002
  var sqlite_exports = {};
889
1003
  __export(sqlite_exports, {
@@ -894,20 +1008,79 @@ __export(sqlite_exports, {
894
1008
  getDbPath: () => getDbPath,
895
1009
  getNativeDb: () => getNativeDb,
896
1010
  getSchemaVersion: () => getSchemaVersion,
1011
+ isSqliteBusy: () => isSqliteBusy,
897
1012
  resetDbState: () => resetDbState,
898
1013
  resolveMigrationsFolder: () => resolveMigrationsFolder,
899
1014
  schema: () => schema_exports
900
1015
  });
901
- import { existsSync as existsSync3, mkdirSync } from "node:fs";
1016
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, copyFileSync, unlinkSync as unlinkSync2, renameSync as renameSync2 } from "node:fs";
902
1017
  import { createRequire as createRequire2 } from "node:module";
903
- import { dirname as dirname2, join as join3 } from "node:path";
1018
+ import { dirname as dirname2, join as join4, resolve as resolve3 } from "node:path";
904
1019
  import { fileURLToPath } from "node:url";
905
1020
  import { eq } from "drizzle-orm";
906
1021
  import { readMigrationFiles } from "drizzle-orm/migrator";
907
1022
  import { drizzle } from "drizzle-orm/sqlite-proxy";
908
1023
  import { migrate } from "drizzle-orm/sqlite-proxy/migrator";
909
1024
  function getDbPath(cwd) {
910
- return join3(getCleoDirAbsolute(cwd), DB_FILENAME);
1025
+ return join4(getCleoDirAbsolute(cwd), DB_FILENAME);
1026
+ }
1027
+ async function autoRecoverFromBackup(nativeDb, dbPath, cwd) {
1028
+ const log5 = getLogger("sqlite");
1029
+ try {
1030
+ const countResult = nativeDb.prepare("SELECT COUNT(*) as cnt FROM tasks").get();
1031
+ const taskCount = countResult?.cnt ?? 0;
1032
+ if (taskCount > 0) return;
1033
+ const backups = listSqliteBackups(cwd);
1034
+ if (backups.length === 0) {
1035
+ return;
1036
+ }
1037
+ const newestBackup = backups[0];
1038
+ const backupDb = new DatabaseSync2(newestBackup.path, { readOnly: true });
1039
+ let backupTaskCount = 0;
1040
+ try {
1041
+ const backupCount = backupDb.prepare("SELECT COUNT(*) as cnt FROM tasks").get();
1042
+ backupTaskCount = backupCount?.cnt ?? 0;
1043
+ } finally {
1044
+ backupDb.close();
1045
+ }
1046
+ if (backupTaskCount < MIN_BACKUP_TASK_COUNT) {
1047
+ return;
1048
+ }
1049
+ log5.warn(
1050
+ { dbPath, backupPath: newestBackup.path, backupTasks: backupTaskCount },
1051
+ `Empty database detected with ${backupTaskCount}-task backup available. Auto-recovering from backup. This likely happened because git-tracked WAL/SHM files were overwritten during a branch switch (T5188).`
1052
+ );
1053
+ nativeDb.close();
1054
+ const walPath = dbPath + "-wal";
1055
+ const shmPath = dbPath + "-shm";
1056
+ try {
1057
+ unlinkSync2(walPath);
1058
+ } catch {
1059
+ }
1060
+ try {
1061
+ unlinkSync2(shmPath);
1062
+ } catch {
1063
+ }
1064
+ const tempPath = dbPath + ".recovery-tmp";
1065
+ copyFileSync(newestBackup.path, tempPath);
1066
+ renameSync2(tempPath, dbPath);
1067
+ log5.info(
1068
+ { dbPath, backupPath: newestBackup.path, restoredTasks: backupTaskCount },
1069
+ "Database auto-recovered from backup successfully."
1070
+ );
1071
+ const restoredNativeDb = openNativeDatabase(dbPath);
1072
+ _nativeDb = restoredNativeDb;
1073
+ const callback = createDrizzleCallback(restoredNativeDb);
1074
+ const batchCb = createBatchCallback(restoredNativeDb);
1075
+ const restoredDb = drizzle(callback, batchCb, { schema: schema_exports });
1076
+ await runMigrations(restoredNativeDb, restoredDb);
1077
+ _db = restoredDb;
1078
+ } catch (err) {
1079
+ log5.error(
1080
+ { err, dbPath },
1081
+ "Auto-recovery from backup failed. Continuing with empty database."
1082
+ );
1083
+ }
911
1084
  }
912
1085
  async function getDb(cwd) {
913
1086
  const requestedPath = getDbPath(cwd);
@@ -919,7 +1092,7 @@ async function getDb(cwd) {
919
1092
  _initPromise = (async () => {
920
1093
  const dbPath = requestedPath;
921
1094
  _dbPath = dbPath;
922
- mkdirSync(dirname2(dbPath), { recursive: true });
1095
+ mkdirSync2(dirname2(dbPath), { recursive: true });
923
1096
  const nativeDb = openNativeDatabase(dbPath);
924
1097
  _nativeDb = nativeDb;
925
1098
  const callback = createDrizzleCallback(nativeDb);
@@ -932,6 +1105,31 @@ async function getDb(cwd) {
932
1105
  nativeDb.exec(
933
1106
  `INSERT OR IGNORE INTO schema_meta (key, value) VALUES ('task_id_sequence', '{"counter":0,"lastId":"T000","checksum":"seed"}')`
934
1107
  );
1108
+ await autoRecoverFromBackup(nativeDb, dbPath, cwd);
1109
+ if (!_gitTrackingChecked) {
1110
+ _gitTrackingChecked = true;
1111
+ try {
1112
+ const { execFileSync: execFileSync6 } = await import("node:child_process");
1113
+ const gitCwd = resolve3(dbPath, "..", "..");
1114
+ const filesToCheck = [dbPath, dbPath + "-wal", dbPath + "-shm"];
1115
+ const log5 = getLogger("sqlite");
1116
+ for (const fileToCheck of filesToCheck) {
1117
+ try {
1118
+ execFileSync6("git", ["ls-files", "--error-unmatch", fileToCheck], {
1119
+ cwd: gitCwd,
1120
+ stdio: "pipe"
1121
+ });
1122
+ const basename7 = fileToCheck.split("/").pop();
1123
+ log5.warn(
1124
+ { path: fileToCheck },
1125
+ `${basename7} is tracked by project git \u2014 this risks data loss on branch switch. Run: git rm --cached ${fileToCheck.replace(gitCwd + "/", "")} (see ADR-013, T5188)`
1126
+ );
1127
+ } catch {
1128
+ }
1129
+ }
1130
+ } catch {
1131
+ }
1132
+ }
935
1133
  _db = db;
936
1134
  return db;
937
1135
  })();
@@ -944,7 +1142,7 @@ async function getDb(cwd) {
944
1142
  function resolveMigrationsFolder() {
945
1143
  const __filename = fileURLToPath(import.meta.url);
946
1144
  const __dirname2 = dirname2(__filename);
947
- return join3(__dirname2, "..", "..", "drizzle");
1145
+ return join4(__dirname2, "..", "..", "drizzle");
948
1146
  }
949
1147
  function tableExists(nativeDb, tableName) {
950
1148
  const result = nativeDb.prepare(
@@ -952,6 +1150,11 @@ function tableExists(nativeDb, tableName) {
952
1150
  ).get(tableName);
953
1151
  return !!result;
954
1152
  }
1153
+ function isSqliteBusy(err) {
1154
+ if (!(err instanceof Error)) return false;
1155
+ const msg = err.message.toLowerCase();
1156
+ return msg.includes("sqlite_busy") || msg.includes("database is locked");
1157
+ }
955
1158
  async function runMigrations(nativeDb, db) {
956
1159
  const migrationsFolder = resolveMigrationsFolder();
957
1160
  if (tableExists(nativeDb, "tasks") && !tableExists(nativeDb, "__drizzle_migrations")) {
@@ -971,16 +1174,33 @@ async function runMigrations(nativeDb, db) {
971
1174
  }
972
1175
  }
973
1176
  await migrate(db, async (queries) => {
974
- nativeDb.prepare("BEGIN").run();
975
- try {
976
- for (const query of queries) {
977
- nativeDb.prepare(query).run();
1177
+ let lastError;
1178
+ for (let attempt = 1; attempt <= MAX_MIGRATION_RETRIES; attempt++) {
1179
+ try {
1180
+ nativeDb.prepare("BEGIN IMMEDIATE").run();
1181
+ try {
1182
+ for (const query of queries) {
1183
+ nativeDb.prepare(query).run();
1184
+ }
1185
+ nativeDb.prepare("COMMIT").run();
1186
+ return;
1187
+ } catch (err) {
1188
+ nativeDb.prepare("ROLLBACK").run();
1189
+ throw err;
1190
+ }
1191
+ } catch (err) {
1192
+ if (!isSqliteBusy(err) || attempt === MAX_MIGRATION_RETRIES) {
1193
+ throw err;
1194
+ }
1195
+ lastError = err;
1196
+ const exponentialDelay = MIGRATION_RETRY_BASE_DELAY_MS * Math.pow(2, attempt - 1);
1197
+ const jitter = Math.random() * exponentialDelay * 0.5;
1198
+ const delay = Math.min(exponentialDelay + jitter, MIGRATION_RETRY_MAX_DELAY_MS);
1199
+ const buf = new SharedArrayBuffer(4);
1200
+ Atomics.wait(new Int32Array(buf), 0, 0, Math.round(delay));
978
1201
  }
979
- nativeDb.prepare("COMMIT").run();
980
- } catch (err) {
981
- nativeDb.prepare("ROLLBACK").run();
982
- throw err;
983
1202
  }
1203
+ throw lastError;
984
1204
  }, { migrationsFolder });
985
1205
  }
986
1206
  function closeDb() {
@@ -1016,18 +1236,20 @@ async function getSchemaVersion(cwd) {
1016
1236
  return result[0]?.value ?? null;
1017
1237
  }
1018
1238
  function dbExists(cwd) {
1019
- return existsSync3(getDbPath(cwd));
1239
+ return existsSync4(getDbPath(cwd));
1020
1240
  }
1021
1241
  function getNativeDb() {
1022
1242
  return _nativeDb;
1023
1243
  }
1024
- var _require2, DatabaseSync2, DB_FILENAME, SQLITE_SCHEMA_VERSION, SCHEMA_VERSION, _db, _nativeDb, _dbPath, _initPromise;
1244
+ var _require2, DatabaseSync2, DB_FILENAME, SQLITE_SCHEMA_VERSION, SCHEMA_VERSION, _db, _nativeDb, _dbPath, _initPromise, _gitTrackingChecked, MIN_BACKUP_TASK_COUNT, MAX_MIGRATION_RETRIES, MIGRATION_RETRY_BASE_DELAY_MS, MIGRATION_RETRY_MAX_DELAY_MS;
1025
1245
  var init_sqlite = __esm({
1026
1246
  "src/store/sqlite.ts"() {
1027
1247
  "use strict";
1028
1248
  init_schema();
1029
1249
  init_paths();
1030
1250
  init_node_sqlite_adapter();
1251
+ init_logger();
1252
+ init_sqlite_backup();
1031
1253
  _require2 = createRequire2(import.meta.url);
1032
1254
  ({ DatabaseSync: DatabaseSync2 } = _require2("node:sqlite"));
1033
1255
  DB_FILENAME = "tasks.db";
@@ -1037,6 +1259,11 @@ var init_sqlite = __esm({
1037
1259
  _nativeDb = null;
1038
1260
  _dbPath = null;
1039
1261
  _initPromise = null;
1262
+ _gitTrackingChecked = false;
1263
+ MIN_BACKUP_TASK_COUNT = 10;
1264
+ MAX_MIGRATION_RETRIES = 5;
1265
+ MIGRATION_RETRY_BASE_DELAY_MS = 100;
1266
+ MIGRATION_RETRY_MAX_DELAY_MS = 2e3;
1040
1267
  }
1041
1268
  });
1042
1269
 
@@ -1298,7 +1525,7 @@ var init_atomic = __esm({
1298
1525
 
1299
1526
  // src/store/backup.ts
1300
1527
  import { copyFile, rename as fsRename, readdir, unlink as unlink2, stat, mkdir as mkdir2 } from "node:fs/promises";
1301
- import { join as join4, basename } from "node:path";
1528
+ import { join as join5, basename } from "node:path";
1302
1529
  async function createBackup(filePath, backupDir, maxBackups = DEFAULT_MAX_BACKUPS) {
1303
1530
  try {
1304
1531
  await mkdir2(backupDir, { recursive: true });
@@ -1312,14 +1539,14 @@ async function createBackup(filePath, backupDir, maxBackups = DEFAULT_MAX_BACKUP
1312
1539
  );
1313
1540
  }
1314
1541
  for (let i = maxBackups; i >= 1; i--) {
1315
- const current = join4(backupDir, `${fileName}.${i}`);
1542
+ const current = join5(backupDir, `${fileName}.${i}`);
1316
1543
  if (i === maxBackups) {
1317
1544
  try {
1318
1545
  await unlink2(current);
1319
1546
  } catch {
1320
1547
  }
1321
1548
  } else {
1322
- const next = join4(backupDir, `${fileName}.${i + 1}`);
1549
+ const next = join5(backupDir, `${fileName}.${i + 1}`);
1323
1550
  try {
1324
1551
  await stat(current);
1325
1552
  await fsRename(current, next);
@@ -1327,7 +1554,7 @@ async function createBackup(filePath, backupDir, maxBackups = DEFAULT_MAX_BACKUP
1327
1554
  }
1328
1555
  }
1329
1556
  }
1330
- const backupPath = join4(backupDir, `${fileName}.1`);
1557
+ const backupPath = join5(backupDir, `${fileName}.1`);
1331
1558
  await copyFile(filePath, backupPath);
1332
1559
  return backupPath;
1333
1560
  } catch (err) {
@@ -1803,6 +2030,30 @@ async function loadDependenciesForTasks(db, tasks2, validationIds) {
1803
2030
  }
1804
2031
  }
1805
2032
  }
2033
+ async function loadRelationsForTasks(db, tasks2) {
2034
+ if (tasks2.length === 0) return;
2035
+ const taskIds = tasks2.map((t) => t.id);
2036
+ const allRels = await db.select().from(taskRelations).where(inArray(taskRelations.taskId, taskIds)).all();
2037
+ const relMap = /* @__PURE__ */ new Map();
2038
+ for (const rel of allRels) {
2039
+ let arr = relMap.get(rel.taskId);
2040
+ if (!arr) {
2041
+ arr = [];
2042
+ relMap.set(rel.taskId, arr);
2043
+ }
2044
+ arr.push({
2045
+ taskId: rel.relatedTo,
2046
+ type: rel.relationType,
2047
+ reason: rel.reason ?? void 0
2048
+ });
2049
+ }
2050
+ for (const task of tasks2) {
2051
+ const relations = relMap.get(task.id);
2052
+ if (relations && relations.length > 0) {
2053
+ task.relates = relations;
2054
+ }
2055
+ }
2056
+ }
1806
2057
  var init_db_helpers = __esm({
1807
2058
  "src/store/db-helpers.ts"() {
1808
2059
  "use strict";
@@ -1851,6 +2102,7 @@ async function createSqliteDataAccessor(cwd) {
1851
2102
  const tasks2 = taskRows.map(rowToTask);
1852
2103
  if (tasks2.length > 0) {
1853
2104
  await loadDependenciesForTasks(db, tasks2);
2105
+ await loadRelationsForTasks(db, tasks2);
1854
2106
  }
1855
2107
  const projectMeta = await getMetaValue(cwd, "project_meta") ?? DEFAULT_PROJECT_META;
1856
2108
  const workState = await getMetaValue(cwd, "focus_state") ?? DEFAULT_WORK_STATE;
@@ -1926,6 +2178,7 @@ async function createSqliteDataAccessor(cwd) {
1926
2178
  ...activeRows.map((r) => r.id)
1927
2179
  ]);
1928
2180
  await loadDependenciesForTasks(db, archivedTasks, allKnownIds);
2181
+ await loadRelationsForTasks(db, archivedTasks);
1929
2182
  }
1930
2183
  return {
1931
2184
  archivedTasks,
@@ -1992,6 +2245,14 @@ async function createSqliteDataAccessor(cwd) {
1992
2245
  await upsertTask(db, row);
1993
2246
  await updateDependencies(db, task.id, task.depends ?? []);
1994
2247
  },
2248
+ async addRelation(taskId, relatedTo, relationType, reason) {
2249
+ const db = await getDb(cwd);
2250
+ const validTypes = ["related", "blocks", "duplicates", "absorbs", "fixes", "extends", "supersedes"];
2251
+ if (!validTypes.includes(relationType)) {
2252
+ throw new Error(`Invalid relation type: ${relationType}. Valid types: ${validTypes.join(", ")}`);
2253
+ }
2254
+ await db.insert(taskRelations).values({ taskId, relatedTo, relationType, reason: reason ?? null }).onConflictDoNothing().run();
2255
+ },
1995
2256
  async archiveSingleTask(taskId, fields) {
1996
2257
  const db = await getDb(cwd);
1997
2258
  const rows = await db.select({ id: tasks.id }).from(tasks).where(eq3(tasks.id, taskId)).all();
@@ -2063,22 +2324,22 @@ var init_sqlite_data_accessor = __esm({
2063
2324
 
2064
2325
  // src/store/git-checkpoint.ts
2065
2326
  import { readFile as readFile2, writeFile } from "node:fs/promises";
2066
- import { existsSync as existsSync4 } from "node:fs";
2327
+ import { existsSync as existsSync5 } from "node:fs";
2067
2328
  import { execFile } from "node:child_process";
2068
2329
  import { promisify } from "node:util";
2069
- import { join as join5, resolve as resolve3 } from "node:path";
2330
+ import { join as join6, resolve as resolve4 } from "node:path";
2070
2331
  function makeCleoGitEnv(cleoDir) {
2071
- const abs = resolve3(cleoDir);
2332
+ const abs = resolve4(cleoDir);
2072
2333
  return {
2073
2334
  ...process.env,
2074
- GIT_DIR: join5(abs, ".git"),
2335
+ GIT_DIR: join6(abs, ".git"),
2075
2336
  GIT_WORK_TREE: abs
2076
2337
  };
2077
2338
  }
2078
2339
  async function cleoGitCommand(args, cleoDir) {
2079
2340
  try {
2080
2341
  const result = await execFileAsync("git", args, {
2081
- cwd: resolve3(cleoDir),
2342
+ cwd: resolve4(cleoDir),
2082
2343
  // absolute cwd so relative paths in args resolve correctly
2083
2344
  env: makeCleoGitEnv(cleoDir),
2084
2345
  timeout: 1e4
@@ -2089,7 +2350,7 @@ async function cleoGitCommand(args, cleoDir) {
2089
2350
  }
2090
2351
  }
2091
2352
  function isCleoGitInitialized(cleoDir) {
2092
- return existsSync4(join5(cleoDir, ".git", "HEAD"));
2353
+ return existsSync5(join6(cleoDir, ".git", "HEAD"));
2093
2354
  }
2094
2355
  async function loadCheckpointConfig(cwd) {
2095
2356
  try {
@@ -2116,25 +2377,25 @@ async function isCleoGitRepo(cleoDir) {
2116
2377
  return result.success && (result.stdout === "true" || isCleoGitInitialized(cleoDir));
2117
2378
  }
2118
2379
  function isMergeInProgress(cleoDir) {
2119
- return existsSync4(join5(cleoDir, ".git", "MERGE_HEAD"));
2380
+ return existsSync5(join6(cleoDir, ".git", "MERGE_HEAD"));
2120
2381
  }
2121
2382
  async function isDetachedHead(cleoDir) {
2122
2383
  const result = await cleoGitCommand(["symbolic-ref", "HEAD"], cleoDir);
2123
2384
  return !result.success;
2124
2385
  }
2125
2386
  function isRebaseInProgress(cleoDir) {
2126
- return existsSync4(join5(cleoDir, ".git", "rebase-merge")) || existsSync4(join5(cleoDir, ".git", "rebase-apply"));
2387
+ return existsSync5(join6(cleoDir, ".git", "rebase-merge")) || existsSync5(join6(cleoDir, ".git", "rebase-apply"));
2127
2388
  }
2128
2389
  async function recordCheckpointTime(cleoDir) {
2129
2390
  try {
2130
- const stateFile = join5(cleoDir, CHECKPOINT_STATE_FILE);
2391
+ const stateFile = join6(cleoDir, CHECKPOINT_STATE_FILE);
2131
2392
  await writeFile(stateFile, String(Math.floor(Date.now() / 1e3)));
2132
2393
  } catch {
2133
2394
  }
2134
2395
  }
2135
2396
  async function getLastCheckpointTime(cleoDir) {
2136
2397
  try {
2137
- const stateFile = join5(cleoDir, CHECKPOINT_STATE_FILE);
2398
+ const stateFile = join6(cleoDir, CHECKPOINT_STATE_FILE);
2138
2399
  const content = await readFile2(stateFile, "utf-8");
2139
2400
  const epoch = parseInt(content.trim(), 10);
2140
2401
  return isNaN(epoch) ? 0 : epoch;
@@ -2145,8 +2406,8 @@ async function getLastCheckpointTime(cleoDir) {
2145
2406
  async function getChangedStateFiles(cleoDir) {
2146
2407
  const changed = [];
2147
2408
  for (const stateFile of STATE_FILES) {
2148
- const fullPath = join5(cleoDir, stateFile);
2149
- if (!existsSync4(fullPath)) continue;
2409
+ const fullPath = join6(cleoDir, stateFile);
2410
+ if (!existsSync5(fullPath)) continue;
2150
2411
  const diffResult = await cleoGitCommand(["diff", "--quiet", "--", stateFile], cleoDir);
2151
2412
  const cachedResult = await cleoGitCommand(["diff", "--cached", "--quiet", "--", stateFile], cleoDir);
2152
2413
  const untrackedResult = await cleoGitCommand(
@@ -2170,7 +2431,7 @@ async function shouldCheckpoint(options) {
2170
2431
  const config = await loadCheckpointConfig(cwd);
2171
2432
  if (!config.enabled) return false;
2172
2433
  const cleoDir = getCleoDir(cwd);
2173
- if (!existsSync4(cleoDir)) return false;
2434
+ if (!existsSync5(cleoDir)) return false;
2174
2435
  if (!isCleoGitInitialized(cleoDir)) return false;
2175
2436
  if (!await isCleoGitRepo(cleoDir)) return false;
2176
2437
  if (isMergeInProgress(cleoDir)) return false;
@@ -2242,63 +2503,15 @@ var init_git_checkpoint = __esm({
2242
2503
  }
2243
2504
  });
2244
2505
 
2245
- // src/store/sqlite-backup.ts
2246
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, readdirSync, statSync, unlinkSync } from "node:fs";
2247
- import { join as join6 } from "node:path";
2248
- function formatTimestamp(d) {
2249
- const pad = (n, len = 2) => String(n).padStart(len, "0");
2250
- return `${d.getFullYear()}${pad(d.getMonth() + 1)}${pad(d.getDate())}-${pad(d.getHours())}${pad(d.getMinutes())}${pad(d.getSeconds())}`;
2251
- }
2252
- function rotateSnapshots(backupDir) {
2253
- try {
2254
- const files = readdirSync(backupDir).filter((f) => f.match(/^tasks-\d{8}-\d{6}\.db$/)).map((f) => ({ name: f, path: join6(backupDir, f), mtimeMs: statSync(join6(backupDir, f)).mtimeMs })).sort((a, b) => a.mtimeMs - b.mtimeMs);
2255
- while (files.length >= MAX_SNAPSHOTS) {
2256
- const oldest = files.shift();
2257
- unlinkSync(oldest.path);
2258
- }
2259
- } catch {
2260
- }
2261
- }
2262
- async function vacuumIntoBackup(opts = {}) {
2263
- const now = Date.now();
2264
- if (!opts.force && now - _lastBackupEpoch < DEBOUNCE_MS) {
2265
- return;
2266
- }
2267
- try {
2268
- const cleoDir = getCleoDir(opts.cwd);
2269
- const backupDir = join6(cleoDir, "backups", "sqlite");
2270
- mkdirSync2(backupDir, { recursive: true });
2271
- const db = getNativeDb();
2272
- if (!db) return;
2273
- const dest = join6(backupDir, `tasks-${formatTimestamp(/* @__PURE__ */ new Date())}.db`);
2274
- db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
2275
- rotateSnapshots(backupDir);
2276
- const safeDest = dest.replace(/'/g, "''");
2277
- db.exec(`VACUUM INTO '${safeDest}'`);
2278
- _lastBackupEpoch = Date.now();
2279
- } catch {
2280
- }
2281
- }
2282
- var MAX_SNAPSHOTS, DEBOUNCE_MS, _lastBackupEpoch;
2283
- var init_sqlite_backup = __esm({
2284
- "src/store/sqlite-backup.ts"() {
2285
- "use strict";
2286
- init_paths();
2287
- init_sqlite();
2288
- MAX_SNAPSHOTS = 10;
2289
- DEBOUNCE_MS = 3e4;
2290
- _lastBackupEpoch = 0;
2291
- }
2292
- });
2293
-
2294
2506
  // src/core/sequence/index.ts
2295
2507
  var sequence_exports = {};
2296
2508
  __export(sequence_exports, {
2509
+ allocateNextTaskId: () => allocateNextTaskId,
2297
2510
  checkSequence: () => checkSequence,
2298
2511
  repairSequence: () => repairSequence,
2299
2512
  showSequence: () => showSequence
2300
2513
  });
2301
- import { existsSync as existsSync6, readFileSync as readFileSync3, renameSync as renameSync2 } from "node:fs";
2514
+ import { existsSync as existsSync6, readFileSync as readFileSync3, renameSync as renameSync3 } from "node:fs";
2302
2515
  import { join as join7 } from "node:path";
2303
2516
  import { eq as eq4 } from "drizzle-orm";
2304
2517
  function getLegacySequenceJsonPath(cwd) {
@@ -2329,10 +2542,10 @@ function renameLegacyFile(path) {
2329
2542
  const migratedPath = `${path}.migrated`;
2330
2543
  try {
2331
2544
  if (!existsSync6(migratedPath)) {
2332
- renameSync2(path, migratedPath);
2545
+ renameSync3(path, migratedPath);
2333
2546
  return;
2334
2547
  }
2335
- renameSync2(path, `${migratedPath}.${Date.now()}`);
2548
+ renameSync3(path, `${migratedPath}.${Date.now()}`);
2336
2549
  } catch {
2337
2550
  }
2338
2551
  }
@@ -2480,7 +2693,56 @@ async function repairSequence(cwd, accessor) {
2480
2693
  message: `Sequence repaired: ${oldCounter} -> ${newCounter}`
2481
2694
  };
2482
2695
  }
2483
- var SEQUENCE_META_KEY;
2696
+ async function allocateNextTaskId(cwd, retryCount = 0) {
2697
+ await getDb(cwd);
2698
+ const nativeDb = getNativeDb();
2699
+ if (!nativeDb) {
2700
+ throw new CleoError(3 /* FILE_ERROR */, "Native database not available for atomic ID allocation");
2701
+ }
2702
+ nativeDb.prepare("BEGIN IMMEDIATE").run();
2703
+ try {
2704
+ nativeDb.prepare(`
2705
+ UPDATE schema_meta
2706
+ SET value = json_set(value,
2707
+ '$.counter', json_extract(value, '$.counter') + 1,
2708
+ '$.lastId', 'T' || printf('%03d', json_extract(value, '$.counter') + 1),
2709
+ '$.checksum', 'alloc-' || strftime('%s','now')
2710
+ )
2711
+ WHERE key = 'task_id_sequence'
2712
+ `).run();
2713
+ const row = nativeDb.prepare(`
2714
+ SELECT json_extract(value, '$.counter') AS counter
2715
+ FROM schema_meta WHERE key = 'task_id_sequence'
2716
+ `).get();
2717
+ if (!row) {
2718
+ throw new CleoError(3 /* FILE_ERROR */, "Sequence counter not found after increment");
2719
+ }
2720
+ const newId = `T${String(row.counter).padStart(3, "0")}`;
2721
+ const existing = nativeDb.prepare(
2722
+ "SELECT id FROM tasks WHERE id = ?"
2723
+ ).get(newId);
2724
+ if (existing) {
2725
+ nativeDb.prepare("ROLLBACK").run();
2726
+ if (retryCount >= MAX_ALLOC_RETRIES) {
2727
+ throw new CleoError(
2728
+ 22 /* ID_COLLISION */,
2729
+ `Failed to allocate unique task ID after ${MAX_ALLOC_RETRIES} retries (last attempted: ${newId})`
2730
+ );
2731
+ }
2732
+ await repairSequence(cwd);
2733
+ return allocateNextTaskId(cwd, retryCount + 1);
2734
+ }
2735
+ nativeDb.prepare("COMMIT").run();
2736
+ return newId;
2737
+ } catch (err) {
2738
+ try {
2739
+ nativeDb.prepare("ROLLBACK").run();
2740
+ } catch {
2741
+ }
2742
+ throw err;
2743
+ }
2744
+ }
2745
+ var SEQUENCE_META_KEY, MAX_ALLOC_RETRIES;
2484
2746
  var init_sequence = __esm({
2485
2747
  "src/core/sequence/index.ts"() {
2486
2748
  "use strict";
@@ -2491,6 +2753,7 @@ var init_sequence = __esm({
2491
2753
  init_sqlite_data_accessor();
2492
2754
  init_data_accessor();
2493
2755
  SEQUENCE_META_KEY = "task_id_sequence";
2756
+ MAX_ALLOC_RETRIES = 3;
2494
2757
  }
2495
2758
  });
2496
2759
 
@@ -2803,6 +3066,11 @@ var init_safety_data_accessor = __esm({
2803
3066
  this.getSafetyOptions()
2804
3067
  );
2805
3068
  }
3069
+ // ---- Relations (pass-through to inner, T5168) ----
3070
+ async addRelation(taskId, relatedTo, relationType, reason) {
3071
+ if (!this.inner.addRelation) return;
3072
+ await this.inner.addRelation(taskId, relatedTo, relationType, reason);
3073
+ }
2806
3074
  // ---- Metadata (pass-through to inner) ----
2807
3075
  async getMetaValue(key) {
2808
3076
  return this.inner.getMetaValue?.(key) ?? null;
@@ -2838,10 +3106,57 @@ var init_data_accessor = __esm({
2838
3106
  }
2839
3107
  });
2840
3108
 
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";
3109
+ // src/store/file-utils.ts
3110
+ import { readFileSync as readFileSync4, writeFileSync, renameSync as renameSync4, existsSync as existsSync8, unlinkSync as unlinkSync3, mkdirSync as mkdirSync3, readdirSync as readdirSync2 } from "node:fs";
3111
+ import { join as join8, dirname as dirname5, basename as basename2 } from "node:path";
3112
+ import { randomBytes as randomBytes2 } from "node:crypto";
2844
3113
  import * as lockfile2 from "proper-lockfile";
3114
+ function rotateBackup(filePath) {
3115
+ const dir = dirname5(filePath);
3116
+ const name = basename2(filePath);
3117
+ const backupDir = join8(dir, ".backups");
3118
+ if (!existsSync8(backupDir)) {
3119
+ mkdirSync3(backupDir, { recursive: true });
3120
+ }
3121
+ for (let i = MAX_BACKUPS; i >= 1; i--) {
3122
+ const current = join8(backupDir, `${name}.${i}`);
3123
+ if (i === MAX_BACKUPS) {
3124
+ try {
3125
+ unlinkSync3(current);
3126
+ } catch {
3127
+ }
3128
+ } else {
3129
+ const next = join8(backupDir, `${name}.${i + 1}`);
3130
+ try {
3131
+ if (existsSync8(current)) renameSync4(current, next);
3132
+ } catch {
3133
+ }
3134
+ }
3135
+ }
3136
+ try {
3137
+ const content = readFileSync4(filePath, "utf-8");
3138
+ writeFileSync(join8(backupDir, `${name}.1`), content, "utf-8");
3139
+ } catch {
3140
+ }
3141
+ }
3142
+ function writeJsonFileAtomic(filePath, data, indent = 2) {
3143
+ const dir = dirname5(filePath);
3144
+ const tempPath = join8(dir, `.${basename2(filePath)}.${randomBytes2(6).toString("hex")}.tmp`);
3145
+ const content = JSON.stringify(data, null, indent) + "\n";
3146
+ writeFileSync(tempPath, content, "utf-8");
3147
+ try {
3148
+ if (existsSync8(filePath)) {
3149
+ rotateBackup(filePath);
3150
+ }
3151
+ renameSync4(tempPath, filePath);
3152
+ } catch (error) {
3153
+ try {
3154
+ unlinkSync3(tempPath);
3155
+ } catch {
3156
+ }
3157
+ throw error;
3158
+ }
3159
+ }
2845
3160
  function readJsonFile(filePath) {
2846
3161
  try {
2847
3162
  const content = readFileSync4(filePath, "utf-8");
@@ -2853,12 +3168,95 @@ function readJsonFile(filePath) {
2853
3168
  throw error;
2854
3169
  }
2855
3170
  }
3171
+ function readLogFileEntries(filePath) {
3172
+ let content;
3173
+ try {
3174
+ content = readFileSync4(filePath, "utf-8").trim();
3175
+ } catch (error) {
3176
+ if (error.code === "ENOENT") return [];
3177
+ throw error;
3178
+ }
3179
+ if (!content) return [];
3180
+ try {
3181
+ const parsed = JSON.parse(content);
3182
+ if (Array.isArray(parsed)) return parsed;
3183
+ if (parsed && typeof parsed === "object" && Array.isArray(parsed.entries)) {
3184
+ return parsed.entries;
3185
+ }
3186
+ return [parsed];
3187
+ } catch {
3188
+ }
3189
+ const entries = [];
3190
+ if (content.startsWith("{")) {
3191
+ let depth = 0;
3192
+ let inString = false;
3193
+ let escaped = false;
3194
+ let jsonEnd = -1;
3195
+ for (let i = 0; i < content.length; i++) {
3196
+ const ch = content[i];
3197
+ if (escaped) {
3198
+ escaped = false;
3199
+ continue;
3200
+ }
3201
+ if (ch === "\\" && inString) {
3202
+ escaped = true;
3203
+ continue;
3204
+ }
3205
+ if (ch === '"') {
3206
+ inString = !inString;
3207
+ continue;
3208
+ }
3209
+ if (inString) continue;
3210
+ if (ch === "{") depth++;
3211
+ else if (ch === "}") {
3212
+ depth--;
3213
+ if (depth === 0) {
3214
+ jsonEnd = i + 1;
3215
+ break;
3216
+ }
3217
+ }
3218
+ }
3219
+ if (jsonEnd > 0) {
3220
+ try {
3221
+ const initial = JSON.parse(content.substring(0, jsonEnd));
3222
+ if (initial && Array.isArray(initial.entries)) entries.push(...initial.entries);
3223
+ } catch {
3224
+ }
3225
+ const remainder = content.substring(jsonEnd).trim();
3226
+ if (remainder) {
3227
+ for (const line of remainder.split("\n")) {
3228
+ const l = line.trim();
3229
+ if (!l || !l.startsWith("{")) continue;
3230
+ try {
3231
+ entries.push(JSON.parse(l));
3232
+ } catch {
3233
+ }
3234
+ }
3235
+ }
3236
+ }
3237
+ } else {
3238
+ for (const line of content.split("\n")) {
3239
+ const l = line.trim();
3240
+ if (!l || !l.startsWith("{")) continue;
3241
+ try {
3242
+ entries.push(JSON.parse(l));
3243
+ } catch {
3244
+ }
3245
+ }
3246
+ }
3247
+ return entries;
3248
+ }
2856
3249
  function getDataPath(projectRoot, filename) {
2857
3250
  return join8(projectRoot, ".cleo", filename);
2858
3251
  }
2859
- var init_store = __esm({
2860
- "src/mcp/engine/store.ts"() {
3252
+ function resolveProjectRoot() {
3253
+ return process.env["CLEO_ROOT"] || process.cwd();
3254
+ }
3255
+ var MAX_BACKUPS;
3256
+ var init_file_utils = __esm({
3257
+ "src/store/file-utils.ts"() {
2861
3258
  "use strict";
3259
+ MAX_BACKUPS = 10;
2862
3260
  }
2863
3261
  });
2864
3262
 
@@ -3102,7 +3500,7 @@ var init_plan = __esm({
3102
3500
  "src/core/tasks/plan.ts"() {
3103
3501
  "use strict";
3104
3502
  init_data_accessor();
3105
- init_store();
3503
+ init_file_utils();
3106
3504
  init_deps_ready();
3107
3505
  PRIORITY_SCORE2 = {
3108
3506
  critical: 100,
@@ -3114,7 +3512,7 @@ var init_plan = __esm({
3114
3512
  });
3115
3513
 
3116
3514
  // src/core/sessions/decisions.ts
3117
- import { randomBytes as randomBytes2 } from "node:crypto";
3515
+ import { randomBytes as randomBytes3 } from "node:crypto";
3118
3516
  import { readFileSync as readFileSync5, appendFileSync, mkdirSync as mkdirSync4, existsSync as existsSync9 } from "node:fs";
3119
3517
  import { join as join9 } from "node:path";
3120
3518
  async function recordDecision(projectRoot, params) {
@@ -3130,7 +3528,7 @@ async function recordDecision(projectRoot, params) {
3130
3528
  }
3131
3529
  const decisionPath = join9(auditDir, "decisions.jsonl");
3132
3530
  const record = {
3133
- id: `dec-${randomBytes2(8).toString("hex")}`,
3531
+ id: `dec-${randomBytes3(8).toString("hex")}`,
3134
3532
  sessionId: params.sessionId,
3135
3533
  taskId: params.taskId,
3136
3534
  decision: params.decision,
@@ -3416,189 +3814,36 @@ async function computeChainPosition(projectRoot, sessionId) {
3416
3814
  let current = sessionId;
3417
3815
  let position = 1;
3418
3816
  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
- }
3817
+ while (true) {
3818
+ visited.add(current);
3819
+ const session = sessionMap.get(current);
3820
+ if (!session?.previousSessionId || visited.has(session.previousSessionId)) break;
3821
+ current = session.previousSessionId;
3822
+ position++;
3823
+ }
3824
+ let length = position;
3825
+ const startSession = sessionMap.get(sessionId);
3826
+ let fwd = startSession?.nextSessionId;
3827
+ while (fwd && !visited.has(fwd)) {
3828
+ visited.add(fwd);
3829
+ length++;
3830
+ const s = sessionMap.get(fwd);
3831
+ fwd = s?.nextSessionId ?? void 0;
3587
3832
  }
3833
+ return { position, length };
3834
+ } catch {
3835
+ return { position: 1, length: 1 };
3588
3836
  }
3589
- return entries;
3590
- }
3591
- function getDataPath2(projectRoot, filename) {
3592
- return join11(projectRoot, ".cleo", filename);
3593
3837
  }
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"() {
3838
+ var execFileAsync2;
3839
+ var init_handoff = __esm({
3840
+ "src/core/sessions/handoff.ts"() {
3600
3841
  "use strict";
3601
- MAX_BACKUPS = 10;
3842
+ init_data_accessor();
3843
+ init_errors();
3844
+ init_exit_codes();
3845
+ init_decisions();
3846
+ execFileAsync2 = promisify2(execFile2);
3602
3847
  }
3603
3848
  });
3604
3849
 
@@ -3613,15 +3858,15 @@ __export(platform_exports, {
3613
3858
  dateDaysAgo: () => dateDaysAgo,
3614
3859
  detectPlatform: () => detectPlatform,
3615
3860
  generateRandomHex: () => generateRandomHex,
3616
- getDataPath: () => getDataPath2,
3861
+ getDataPath: () => getDataPath,
3617
3862
  getFileMtime: () => getFileMtime,
3618
3863
  getFileSize: () => getFileSize,
3619
3864
  getIsoTimestamp: () => getIsoTimestamp,
3620
3865
  getNodeUpgradeInstructions: () => getNodeUpgradeInstructions,
3621
3866
  getNodeVersionInfo: () => getNodeVersionInfo,
3622
3867
  isoToEpoch: () => isoToEpoch,
3623
- readJsonFile: () => readJsonFile2,
3624
- readLogFileEntries: () => readLogFileEntries2,
3868
+ readJsonFile: () => readJsonFile,
3869
+ readLogFileEntries: () => readLogFileEntries,
3625
3870
  requireTool: () => requireTool,
3626
3871
  resolveProjectRoot: () => resolveProjectRoot,
3627
3872
  sha256: () => sha256,
@@ -3629,8 +3874,8 @@ __export(platform_exports, {
3629
3874
  });
3630
3875
  import { execFileSync } from "node:child_process";
3631
3876
  import { tmpdir } from "node:os";
3632
- import { join as join12 } from "node:path";
3633
- import { existsSync as existsSync12, statSync as statSync2 } from "node:fs";
3877
+ import { join as join11 } from "node:path";
3878
+ import { existsSync as existsSync11, statSync as statSync2 } from "node:fs";
3634
3879
  import { createHash as createHash3, randomBytes as randomBytes6 } from "node:crypto";
3635
3880
  function detectPlatform() {
3636
3881
  switch (process.platform) {
@@ -3689,11 +3934,11 @@ function dateDaysAgo(days) {
3689
3934
  return d.toISOString();
3690
3935
  }
3691
3936
  function getFileSize(filePath) {
3692
- if (!existsSync12(filePath)) return 0;
3937
+ if (!existsSync11(filePath)) return 0;
3693
3938
  return statSync2(filePath).size;
3694
3939
  }
3695
3940
  function getFileMtime(filePath) {
3696
- if (!existsSync12(filePath)) return null;
3941
+ if (!existsSync11(filePath)) return null;
3697
3942
  return statSync2(filePath).mtime.toISOString();
3698
3943
  }
3699
3944
  function generateRandomHex(bytes = 6) {
@@ -3704,7 +3949,7 @@ function sha256(data) {
3704
3949
  }
3705
3950
  function createTempFilePath(prefix = "cleo-", suffix = ".tmp") {
3706
3951
  const random = generateRandomHex(8);
3707
- return join12(tmpdir(), `${prefix}${random}${suffix}`);
3952
+ return join11(tmpdir(), `${prefix}${random}${suffix}`);
3708
3953
  }
3709
3954
  function getNodeVersionInfo() {
3710
3955
  const version = process.version.replace("v", "");
@@ -3834,20 +4079,20 @@ __export(schema_integrity_exports, {
3834
4079
  checkSchemaIntegrity: () => checkSchemaIntegrity,
3835
4080
  readSchemaVersionFromFile: () => readSchemaVersionFromFile
3836
4081
  });
3837
- import { existsSync as existsSync22, readFileSync as readFileSync15 } from "node:fs";
3838
- import { join as join22, dirname as dirname7 } from "node:path";
4082
+ import { existsSync as existsSync21, readFileSync as readFileSync14 } from "node:fs";
4083
+ import { join as join21, dirname as dirname6 } from "node:path";
3839
4084
  import { fileURLToPath as fileURLToPath2 } from "node:url";
3840
4085
  function resolveSchemaPath(schemaName) {
3841
- const __dirname2 = dirname7(fileURLToPath2(import.meta.url));
4086
+ const __dirname2 = dirname6(fileURLToPath2(import.meta.url));
3842
4087
  const candidates = [
3843
4088
  // dist/core/validation/ → schemas/
3844
- join22(__dirname2, "..", "..", "..", "schemas", schemaName),
4089
+ join21(__dirname2, "..", "..", "..", "schemas", schemaName),
3845
4090
  // src/core/validation/ → schemas/ (ts-node / vitest)
3846
- join22(__dirname2, "..", "..", "schemas", schemaName),
4091
+ join21(__dirname2, "..", "..", "schemas", schemaName),
3847
4092
  // fallback: look relative to cwd
3848
- join22(process.cwd(), "schemas", schemaName)
4093
+ join21(process.cwd(), "schemas", schemaName)
3849
4094
  ];
3850
- return candidates.find(existsSync22) ?? null;
4095
+ return candidates.find(existsSync21) ?? null;
3851
4096
  }
3852
4097
  function readSchemaVersionFromFile(schemaName) {
3853
4098
  const path = resolveSchemaPath(schemaName);
@@ -3856,7 +4101,7 @@ function readSchemaVersionFromFile(schemaName) {
3856
4101
  }
3857
4102
  function readSchemaVersion(schemaPath) {
3858
4103
  try {
3859
- const raw = readFileSync15(schemaPath, "utf-8");
4104
+ const raw = readFileSync14(schemaPath, "utf-8");
3860
4105
  const schema = JSON.parse(raw);
3861
4106
  const version = schema["schemaVersion"] ?? schema["_meta"]?.["schemaVersion"];
3862
4107
  return typeof version === "string" ? version : null;
@@ -3866,7 +4111,7 @@ function readSchemaVersion(schemaPath) {
3866
4111
  }
3867
4112
  function checkFile(target, cleoDir) {
3868
4113
  const filePath = target.filePath(cleoDir);
3869
- if (!existsSync22(filePath)) {
4114
+ if (!existsSync21(filePath)) {
3870
4115
  if (target.required) {
3871
4116
  return {
3872
4117
  label: target.label,
@@ -3878,7 +4123,7 @@ function checkFile(target, cleoDir) {
3878
4123
  }
3879
4124
  let data;
3880
4125
  try {
3881
- data = JSON.parse(readFileSync15(filePath, "utf-8"));
4126
+ data = JSON.parse(readFileSync14(filePath, "utf-8"));
3882
4127
  } catch (err) {
3883
4128
  return {
3884
4129
  label: target.label,
@@ -3896,7 +4141,7 @@ function checkFile(target, cleoDir) {
3896
4141
  }
3897
4142
  let schema;
3898
4143
  try {
3899
- schema = JSON.parse(readFileSync15(schemaPath, "utf-8"));
4144
+ schema = JSON.parse(readFileSync14(schemaPath, "utf-8"));
3900
4145
  } catch (err) {
3901
4146
  return {
3902
4147
  label: target.label,
@@ -3960,28 +4205,28 @@ var init_schema_integrity = __esm({
3960
4205
  INTEGRITY_TARGETS = [
3961
4206
  {
3962
4207
  label: "config.json",
3963
- filePath: (d) => join22(d, "config.json"),
4208
+ filePath: (d) => join21(d, "config.json"),
3964
4209
  schemaName: "config.schema.json",
3965
4210
  required: true,
3966
4211
  versionKey: "schemaVersion"
3967
4212
  },
3968
4213
  {
3969
4214
  label: "project-info.json",
3970
- filePath: (d) => join22(d, "project-info.json"),
4215
+ filePath: (d) => join21(d, "project-info.json"),
3971
4216
  schemaName: "project-info.schema.json",
3972
4217
  required: false,
3973
4218
  versionKey: "schemaVersion"
3974
4219
  },
3975
4220
  {
3976
4221
  label: "project-context.json",
3977
- filePath: (d) => join22(d, "project-context.json"),
4222
+ filePath: (d) => join21(d, "project-context.json"),
3978
4223
  schemaName: "project-context.schema.json",
3979
4224
  required: false,
3980
4225
  versionKey: "schemaVersion"
3981
4226
  },
3982
4227
  {
3983
4228
  label: ".context-state.json",
3984
- filePath: (d) => join22(d, ".context-state.json"),
4229
+ filePath: (d) => join21(d, ".context-state.json"),
3985
4230
  schemaName: "context-state.schema.json",
3986
4231
  required: false,
3987
4232
  versionKey: "version"
@@ -3996,17 +4241,17 @@ __export(mcp_exports, {
3996
4241
  detectEnvMode: () => detectEnvMode,
3997
4242
  generateMcpServerEntry: () => generateMcpServerEntry
3998
4243
  });
3999
- import { readFileSync as readFileSync16 } from "node:fs";
4000
- import { join as join23 } from "node:path";
4244
+ import { readFileSync as readFileSync15 } from "node:fs";
4245
+ import { join as join22 } from "node:path";
4001
4246
  import { homedir as homedir3 } from "node:os";
4002
4247
  function detectEnvMode() {
4003
- const versionPath = join23(
4004
- process.env["CLEO_HOME"] ?? join23(homedir3(), ".cleo"),
4248
+ const versionPath = join22(
4249
+ process.env["CLEO_HOME"] ?? join22(homedir3(), ".cleo"),
4005
4250
  "VERSION"
4006
4251
  );
4007
4252
  let content;
4008
4253
  try {
4009
- content = readFileSync16(versionPath, "utf-8");
4254
+ content = readFileSync15(versionPath, "utf-8");
4010
4255
  } catch {
4011
4256
  return { mode: "unknown", source: null };
4012
4257
  }
@@ -4029,7 +4274,7 @@ function generateMcpServerEntry(env) {
4029
4274
  if (env.mode === "dev-ts" && env.source) {
4030
4275
  return {
4031
4276
  command: "node",
4032
- args: [join23(env.source, "dist", "mcp", "index.js")],
4277
+ args: [join22(env.source, "dist", "mcp", "index.js")],
4033
4278
  env: {}
4034
4279
  };
4035
4280
  }
@@ -4068,17 +4313,17 @@ __export(registry_exports, {
4068
4313
  readRegistryRequired: () => readRegistryRequired
4069
4314
  });
4070
4315
  import { createHash as createHash4 } from "node:crypto";
4071
- import { join as join24 } from "node:path";
4316
+ import { join as join23 } from "node:path";
4072
4317
  import { mkdir as mkdir4, access, readFile as readFile4 } from "node:fs/promises";
4073
4318
  import { z } from "zod";
4074
4319
  function getNexusHome() {
4075
- return process.env["NEXUS_HOME"] ?? join24(getCleoHome(), "nexus");
4320
+ return process.env["NEXUS_HOME"] ?? join23(getCleoHome(), "nexus");
4076
4321
  }
4077
4322
  function getNexusCacheDir() {
4078
- return process.env["NEXUS_CACHE_DIR"] ?? join24(getNexusHome(), "cache");
4323
+ return process.env["NEXUS_CACHE_DIR"] ?? join23(getNexusHome(), "cache");
4079
4324
  }
4080
4325
  function getRegistryPath() {
4081
- return process.env["NEXUS_REGISTRY_FILE"] ?? join24(getCleoHome(), "projects-registry.json");
4326
+ return process.env["NEXUS_REGISTRY_FILE"] ?? join23(getCleoHome(), "projects-registry.json");
4082
4327
  }
4083
4328
  function generateProjectHash(projectPath) {
4084
4329
  const hash = createHash4("sha256").update(projectPath).digest("hex");
@@ -4120,7 +4365,7 @@ async function nexusInit() {
4120
4365
  }
4121
4366
  async function isCleoProject(projectPath) {
4122
4367
  try {
4123
- await access(join24(projectPath, ".cleo", "todo.json"));
4368
+ await access(join23(projectPath, ".cleo", "todo.json"));
4124
4369
  return true;
4125
4370
  } catch {
4126
4371
  return false;
@@ -4130,9 +4375,9 @@ async function readProjectMeta(projectPath) {
4130
4375
  try {
4131
4376
  let raw;
4132
4377
  try {
4133
- raw = await readFile4(join24(projectPath, ".cleo", "tasks.json"), "utf-8");
4378
+ raw = await readFile4(join23(projectPath, ".cleo", "tasks.json"), "utf-8");
4134
4379
  } catch {
4135
- raw = await readFile4(join24(projectPath, ".cleo", "todo.json"), "utf-8");
4380
+ raw = await readFile4(join23(projectPath, ".cleo", "todo.json"), "utf-8");
4136
4381
  }
4137
4382
  const data = JSON.parse(raw);
4138
4383
  const tasks2 = data.tasks ?? [];
@@ -4318,10 +4563,10 @@ var project_detect_exports = {};
4318
4563
  __export(project_detect_exports, {
4319
4564
  detectProjectType: () => detectProjectType
4320
4565
  });
4321
- import { existsSync as existsSync23, readdirSync as readdirSync6 } from "node:fs";
4322
- import { join as join25 } from "node:path";
4566
+ import { existsSync as existsSync22, readdirSync as readdirSync6 } from "node:fs";
4567
+ import { join as join24 } from "node:path";
4323
4568
  function detectProjectType(projectDir) {
4324
- const exists = (f) => existsSync23(join25(projectDir, f));
4569
+ const exists = (f) => existsSync22(join24(projectDir, f));
4325
4570
  const info = {
4326
4571
  type: "unknown",
4327
4572
  testFramework: "unknown",
@@ -4392,6 +4637,7 @@ __export(validation_schemas_exports, {
4392
4637
  insertLifecyclePipelineSchema: () => insertLifecyclePipelineSchema,
4393
4638
  insertLifecycleStageSchema: () => insertLifecycleStageSchema,
4394
4639
  insertLifecycleTransitionSchema: () => insertLifecycleTransitionSchema,
4640
+ insertManifestEntrySchema: () => insertManifestEntrySchema,
4395
4641
  insertSchemaMetaSchema: () => insertSchemaMetaSchema,
4396
4642
  insertSessionSchema: () => insertSessionSchema,
4397
4643
  insertTaskDependencySchema: () => insertTaskDependencySchema,
@@ -4405,6 +4651,7 @@ __export(validation_schemas_exports, {
4405
4651
  selectLifecyclePipelineSchema: () => selectLifecyclePipelineSchema,
4406
4652
  selectLifecycleStageSchema: () => selectLifecycleStageSchema,
4407
4653
  selectLifecycleTransitionSchema: () => selectLifecycleTransitionSchema,
4654
+ selectManifestEntrySchema: () => selectManifestEntrySchema,
4408
4655
  selectSchemaMetaSchema: () => selectSchemaMetaSchema,
4409
4656
  selectSessionSchema: () => selectSessionSchema,
4410
4657
  selectTaskDependencySchema: () => selectTaskDependencySchema,
@@ -4418,7 +4665,7 @@ __export(validation_schemas_exports, {
4418
4665
  });
4419
4666
  import { createInsertSchema, createSelectSchema } from "drizzle-orm/zod";
4420
4667
  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;
4668
+ 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
4669
  var init_validation_schemas = __esm({
4423
4670
  "src/store/validation-schemas.ts"() {
4424
4671
  "use strict";
@@ -4511,6 +4758,8 @@ var init_validation_schemas = __esm({
4511
4758
  AuditLogSelectSchema = selectAuditLogSchema;
4512
4759
  insertArchitectureDecisionSchema = createInsertSchema(architectureDecisions);
4513
4760
  selectArchitectureDecisionSchema = createSelectSchema(architectureDecisions);
4761
+ insertManifestEntrySchema = createInsertSchema(manifestEntries);
4762
+ selectManifestEntrySchema = createSelectSchema(manifestEntries);
4514
4763
  }
4515
4764
  });
4516
4765
 
@@ -4684,8 +4933,8 @@ __export(session_grade_exports, {
4684
4933
  gradeSession: () => gradeSession,
4685
4934
  readGrades: () => readGrades
4686
4935
  });
4687
- import { join as join42 } from "node:path";
4688
- import { existsSync as existsSync41 } from "node:fs";
4936
+ import { join as join41 } from "node:path";
4937
+ import { existsSync as existsSync40 } from "node:fs";
4689
4938
  import { readFile as readFile6, appendFile, mkdir as mkdir6 } from "node:fs/promises";
4690
4939
  async function gradeSession(sessionId, cwd) {
4691
4940
  const sessionEntries = await queryAudit({ sessionId });
@@ -4865,9 +5114,9 @@ function detectDuplicateCreates(entries) {
4865
5114
  async function appendGradeResult(result, cwd) {
4866
5115
  try {
4867
5116
  const cleoDir = getCleoDirAbsolute(cwd);
4868
- const metricsDir = join42(cleoDir, "metrics");
5117
+ const metricsDir = join41(cleoDir, "metrics");
4869
5118
  await mkdir6(metricsDir, { recursive: true });
4870
- const gradesPath = join42(metricsDir, "GRADES.jsonl");
5119
+ const gradesPath = join41(metricsDir, "GRADES.jsonl");
4871
5120
  const line = JSON.stringify({ ...result, evaluator: "auto" }) + "\n";
4872
5121
  await appendFile(gradesPath, line, "utf8");
4873
5122
  } catch {
@@ -4876,8 +5125,8 @@ async function appendGradeResult(result, cwd) {
4876
5125
  async function readGrades(sessionId, cwd) {
4877
5126
  try {
4878
5127
  const cleoDir = getCleoDirAbsolute(cwd);
4879
- const gradesPath = join42(cleoDir, "metrics", "GRADES.jsonl");
4880
- if (!existsSync41(gradesPath)) return [];
5128
+ const gradesPath = join41(cleoDir, "metrics", "GRADES.jsonl");
5129
+ if (!existsSync40(gradesPath)) return [];
4881
5130
  const content = await readFile6(gradesPath, "utf8");
4882
5131
  const results = content.split("\n").filter((l) => l.trim()).map((l) => JSON.parse(l));
4883
5132
  return sessionId ? results.filter((r) => r.sessionId === sessionId) : results;
@@ -5241,7 +5490,7 @@ if (actualQueryCount < 1) {
5241
5490
  function registerQueryTool() {
5242
5491
  return {
5243
5492
  name: "cleo_query",
5244
- description: "CLEO read operations: task discovery, status checks, analysis, validation, and compliance metrics. Never modifies state.",
5493
+ 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
5494
  inputSchema: {
5246
5495
  type: "object",
5247
5496
  required: ["domain", "operation"],
@@ -5253,7 +5502,7 @@ function registerQueryTool() {
5253
5502
  },
5254
5503
  operation: {
5255
5504
  type: "string",
5256
- description: "Domain-specific read operation (see operation matrix)"
5505
+ 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
5506
  },
5258
5507
  params: {
5259
5508
  type: "object",
@@ -5563,7 +5812,7 @@ if (actualMutateCount < 1) {
5563
5812
  function registerMutateTool() {
5564
5813
  return {
5565
5814
  name: "cleo_mutate",
5566
- description: "CLEO write operations: create, update, complete tasks; manage sessions; spawn agents; progress lifecycle; execute releases. Modifies state with validation.",
5815
+ 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
5816
  inputSchema: {
5568
5817
  type: "object",
5569
5818
  required: ["domain", "operation"],
@@ -5575,7 +5824,7 @@ function registerMutateTool() {
5575
5824
  },
5576
5825
  operation: {
5577
5826
  type: "string",
5578
- description: "Domain-specific write operation (see operation matrix)"
5827
+ 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
5828
  },
5580
5829
  params: {
5581
5830
  type: "object",
@@ -7813,7 +8062,6 @@ init_data_accessor();
7813
8062
 
7814
8063
  // src/dispatch/engines/_error.ts
7815
8064
  init_logger();
7816
- var logger = getLogger("engine");
7817
8065
  var STRING_TO_EXIT = {
7818
8066
  // General Errors (1-9)
7819
8067
  E_GENERAL: 1,
@@ -7942,6 +8190,7 @@ function logLevel(exitCode) {
7942
8190
  function engineError(code, message, options) {
7943
8191
  const exitCode = STRING_TO_EXIT[code] ?? 1;
7944
8192
  const level = logLevel(exitCode);
8193
+ const logger = getLogger("engine");
7945
8194
  logger[level]({ code, exitCode, ...options?.details && { details: options.details } }, message);
7946
8195
  return {
7947
8196
  success: false,
@@ -8564,6 +8813,7 @@ function validateHierarchyPlacement(parentId, tasks2, policy) {
8564
8813
  }
8565
8814
 
8566
8815
  // src/core/tasks/add.ts
8816
+ init_sequence();
8567
8817
  function validateTitle(title) {
8568
8818
  if (!title || title.trim().length === 0) {
8569
8819
  throw new CleoError(2 /* INVALID_INPUT */, "Task title is required");
@@ -8746,20 +8996,23 @@ async function addTask(options, cwd, accessor) {
8746
8996
  validateTitle(options.title);
8747
8997
  const data = accessor ? await accessor.loadTaskFile() : await readJsonRequired(taskPath);
8748
8998
  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;
8999
+ const useSqliteAlloc = accessor?.engine === "sqlite" || !accessor && (await Promise.resolve().then(() => (init_sqlite(), sqlite_exports))).getNativeDb() !== null;
9000
+ if (!useSqliteAlloc) {
9001
+ try {
9002
+ if (accessor) {
9003
+ const archive = await accessor.loadArchive();
9004
+ if (archive?.archivedTasks) {
9005
+ archivedTasks = archive.archivedTasks;
9006
+ }
9007
+ } else {
9008
+ const { readJson: readJson2 } = await Promise.resolve().then(() => (init_json(), json_exports));
9009
+ const archive = await readJson2(archivePath);
9010
+ if (archive?.archivedTasks) {
9011
+ archivedTasks = archive.archivedTasks;
9012
+ }
8760
9013
  }
9014
+ } catch {
8761
9015
  }
8762
- } catch {
8763
9016
  }
8764
9017
  const status = options.status ?? "pending";
8765
9018
  const priority = normalizePriority(options.priority ?? "medium");
@@ -8837,9 +9090,14 @@ async function addTask(options, cwd, accessor) {
8837
9090
  if (duplicate) {
8838
9091
  return { task: duplicate, duplicate: true };
8839
9092
  }
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`);
9093
+ let taskId;
9094
+ if (useSqliteAlloc) {
9095
+ taskId = await allocateNextTaskId(cwd);
9096
+ } else {
9097
+ taskId = generateTaskId(data.tasks, archivedTasks);
9098
+ if (data.tasks.some((t) => t.id === taskId)) {
9099
+ throw new CleoError(22 /* ID_COLLISION */, `Generated ID ${taskId} already exists`);
9100
+ }
8843
9101
  }
8844
9102
  const now = (/* @__PURE__ */ new Date()).toISOString();
8845
9103
  const position = options.position ?? getNextPosition(parentId, data.tasks);
@@ -9193,6 +9451,291 @@ async function updateTask2(options, cwd, accessor) {
9193
9451
  return { task, changes };
9194
9452
  }
9195
9453
 
9454
+ // src/core/tasks/complete.ts
9455
+ init_json();
9456
+ init_errors();
9457
+ init_exit_codes();
9458
+ init_paths();
9459
+
9460
+ // src/core/tasks/dependency-check.ts
9461
+ function detectCircularDeps(taskId, tasks2) {
9462
+ const taskMap = new Map(tasks2.map((t) => [t.id, t]));
9463
+ const visited = /* @__PURE__ */ new Set();
9464
+ const recursionStack = /* @__PURE__ */ new Set();
9465
+ const path = [];
9466
+ function dfs(id) {
9467
+ visited.add(id);
9468
+ recursionStack.add(id);
9469
+ path.push(id);
9470
+ const task = taskMap.get(id);
9471
+ if (task?.depends) {
9472
+ for (const depId of task.depends) {
9473
+ if (!visited.has(depId)) {
9474
+ const cycle = dfs(depId);
9475
+ if (cycle.length > 0) return cycle;
9476
+ } else if (recursionStack.has(depId)) {
9477
+ const cycleStart = path.indexOf(depId);
9478
+ return [...path.slice(cycleStart), depId];
9479
+ }
9480
+ }
9481
+ }
9482
+ path.pop();
9483
+ recursionStack.delete(id);
9484
+ return [];
9485
+ }
9486
+ return dfs(taskId);
9487
+ }
9488
+ function getBlockedTasks(tasks2) {
9489
+ const completedIds = new Set(
9490
+ tasks2.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
9491
+ );
9492
+ return tasks2.filter((t) => {
9493
+ if (!t.depends?.length) return false;
9494
+ if (t.status === "done" || t.status === "cancelled") return false;
9495
+ return t.depends.some((depId) => !completedIds.has(depId));
9496
+ });
9497
+ }
9498
+ function getReadyTasks(tasks2) {
9499
+ const completedIds = new Set(
9500
+ tasks2.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
9501
+ );
9502
+ return tasks2.filter((t) => {
9503
+ if (t.status === "done" || t.status === "cancelled") return false;
9504
+ if (!t.depends?.length) return true;
9505
+ return t.depends.every((depId) => completedIds.has(depId));
9506
+ });
9507
+ }
9508
+ function getDependents(taskId, tasks2) {
9509
+ return tasks2.filter((t) => t.depends?.includes(taskId));
9510
+ }
9511
+ function getUnresolvedDeps(taskId, tasks2) {
9512
+ const task = tasks2.find((t) => t.id === taskId);
9513
+ if (!task?.depends?.length) return [];
9514
+ const completedIds = new Set(
9515
+ tasks2.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
9516
+ );
9517
+ return task.depends.filter((depId) => !completedIds.has(depId));
9518
+ }
9519
+ function validateDependencyRefs(tasks2) {
9520
+ const taskIds = new Set(tasks2.map((t) => t.id));
9521
+ const errors = [];
9522
+ for (const task of tasks2) {
9523
+ if (!task.depends?.length) continue;
9524
+ for (const depId of task.depends) {
9525
+ if (!taskIds.has(depId)) {
9526
+ errors.push({
9527
+ code: "E_DEP_NOT_FOUND",
9528
+ taskId: task.id,
9529
+ message: `Task ${task.id} depends on ${depId}, which does not exist`,
9530
+ relatedIds: [depId]
9531
+ });
9532
+ }
9533
+ }
9534
+ }
9535
+ return errors;
9536
+ }
9537
+ function validateDependencies(tasks2) {
9538
+ const errors = [];
9539
+ const warnings = [];
9540
+ errors.push(...validateDependencyRefs(tasks2));
9541
+ const visited = /* @__PURE__ */ new Set();
9542
+ for (const task of tasks2) {
9543
+ if (visited.has(task.id)) continue;
9544
+ if (!task.depends?.length) continue;
9545
+ const cycle = detectCircularDeps(task.id, tasks2);
9546
+ if (cycle.length > 0) {
9547
+ errors.push({
9548
+ code: "E_CIRCULAR_DEP",
9549
+ taskId: task.id,
9550
+ message: `Circular dependency detected: ${cycle.join(" -> ")}`,
9551
+ relatedIds: cycle
9552
+ });
9553
+ cycle.forEach((id) => visited.add(id));
9554
+ }
9555
+ }
9556
+ for (const task of tasks2) {
9557
+ if (task.depends?.includes(task.id)) {
9558
+ errors.push({
9559
+ code: "E_SELF_DEP",
9560
+ taskId: task.id,
9561
+ message: `Task ${task.id} depends on itself`
9562
+ });
9563
+ }
9564
+ }
9565
+ for (const task of tasks2) {
9566
+ if (task.status === "done" && task.depends?.length) {
9567
+ const unresolved = getUnresolvedDeps(task.id, tasks2);
9568
+ if (unresolved.length > 0) {
9569
+ warnings.push({
9570
+ code: "W_COMPLETED_WITH_UNMET_DEPS",
9571
+ taskId: task.id,
9572
+ message: `Completed task ${task.id} has unmet dependencies: ${unresolved.join(", ")}`
9573
+ });
9574
+ }
9575
+ }
9576
+ }
9577
+ return {
9578
+ valid: errors.length === 0,
9579
+ errors,
9580
+ warnings
9581
+ };
9582
+ }
9583
+ function getTransitiveBlockers(taskId, tasks2) {
9584
+ const taskMap = new Map(tasks2.map((t) => [t.id, t]));
9585
+ const task = taskMap.get(taskId);
9586
+ if (!task?.depends?.length) return [];
9587
+ const blockers = /* @__PURE__ */ new Set();
9588
+ const visited = /* @__PURE__ */ new Set();
9589
+ function walk(id) {
9590
+ if (visited.has(id)) return;
9591
+ visited.add(id);
9592
+ const t = taskMap.get(id);
9593
+ if (!t?.depends?.length) return;
9594
+ for (const depId of t.depends) {
9595
+ const dep = taskMap.get(depId);
9596
+ if (!dep) continue;
9597
+ if (dep.status === "done" || dep.status === "cancelled") continue;
9598
+ blockers.add(depId);
9599
+ walk(depId);
9600
+ }
9601
+ }
9602
+ walk(taskId);
9603
+ return [...blockers];
9604
+ }
9605
+ function getLeafBlockers(taskId, tasks2) {
9606
+ const blockerIds = getTransitiveBlockers(taskId, tasks2);
9607
+ if (blockerIds.length === 0) return [];
9608
+ const taskMap = new Map(tasks2.map((t) => [t.id, t]));
9609
+ const completedIds = new Set(
9610
+ tasks2.filter((t) => t.status === "done" || t.status === "cancelled").map((t) => t.id)
9611
+ );
9612
+ return blockerIds.filter((id) => {
9613
+ const t = taskMap.get(id);
9614
+ if (!t?.depends?.length) return true;
9615
+ return t.depends.every((depId) => completedIds.has(depId));
9616
+ });
9617
+ }
9618
+
9619
+ // src/core/tasks/complete.ts
9620
+ init_data_safety_central();
9621
+ async function completeTask(options, cwd, accessor) {
9622
+ const taskPath = getTaskPath(cwd);
9623
+ const logPath = getLogPath(cwd);
9624
+ const backupDir = getBackupDir(cwd);
9625
+ const data = accessor ? await accessor.loadTaskFile() : await readJsonRequired(taskPath);
9626
+ const taskIdx = data.tasks.findIndex((t) => t.id === options.taskId);
9627
+ if (taskIdx === -1) {
9628
+ throw new CleoError(
9629
+ 4 /* NOT_FOUND */,
9630
+ `Task not found: ${options.taskId}`,
9631
+ { fix: `Use 'cleo find "${options.taskId}"' to search` }
9632
+ );
9633
+ }
9634
+ const task = data.tasks[taskIdx];
9635
+ if (task.status === "done") {
9636
+ throw new CleoError(
9637
+ 17 /* TASK_COMPLETED */,
9638
+ `Task ${options.taskId} is already completed`
9639
+ );
9640
+ }
9641
+ if (task.depends?.length) {
9642
+ const incompleteDeps = task.depends.filter((depId) => {
9643
+ const dep = data.tasks.find((t) => t.id === depId);
9644
+ return dep && dep.status !== "done";
9645
+ });
9646
+ if (incompleteDeps.length > 0) {
9647
+ throw new CleoError(
9648
+ 5 /* DEPENDENCY_ERROR */,
9649
+ `Task ${options.taskId} has incomplete dependencies: ${incompleteDeps.join(", ")}`,
9650
+ { fix: `Complete dependencies first: ${incompleteDeps.map((d) => `cleo complete ${d}`).join(", ")}` }
9651
+ );
9652
+ }
9653
+ }
9654
+ const children = data.tasks.filter((t) => t.parentId === options.taskId);
9655
+ const incompleteChildren = children.filter((c) => c.status !== "done" && c.status !== "cancelled");
9656
+ if (incompleteChildren.length > 0 && task.type === "epic") {
9657
+ if (!task.noAutoComplete) {
9658
+ throw new CleoError(
9659
+ 16 /* HAS_CHILDREN */,
9660
+ `Epic ${options.taskId} has ${incompleteChildren.length} incomplete children: ${incompleteChildren.map((c) => c.id).join(", ")}`,
9661
+ { fix: `Complete children first or use 'cleo update ${options.taskId} --no-auto-complete'` }
9662
+ );
9663
+ }
9664
+ }
9665
+ const now = (/* @__PURE__ */ new Date()).toISOString();
9666
+ const before = { ...task };
9667
+ task.status = "done";
9668
+ task.completedAt = now;
9669
+ task.updatedAt = now;
9670
+ if (options.notes) {
9671
+ const timestampedNote = `${(/* @__PURE__ */ new Date()).toISOString().replace("T", " ").replace(/\.\d+Z$/, " UTC")}: ${options.notes}`;
9672
+ if (!task.notes) task.notes = [];
9673
+ task.notes.push(timestampedNote);
9674
+ }
9675
+ if (options.changeset) {
9676
+ if (!task.notes) task.notes = [];
9677
+ task.notes.push(`Changeset: ${options.changeset}`);
9678
+ }
9679
+ data.tasks[taskIdx] = task;
9680
+ const autoCompleted = [];
9681
+ if (task.parentId) {
9682
+ const parent = data.tasks.find((t) => t.id === task.parentId);
9683
+ if (parent && parent.type === "epic" && !parent.noAutoComplete) {
9684
+ const parentChildren = data.tasks.filter((t) => t.parentId === parent.id);
9685
+ const allDone = parentChildren.every((c) => c.status === "done" || c.status === "cancelled");
9686
+ if (allDone) {
9687
+ parent.status = "done";
9688
+ parent.completedAt = now;
9689
+ parent.updatedAt = now;
9690
+ autoCompleted.push(parent.id);
9691
+ }
9692
+ }
9693
+ }
9694
+ data._meta.checksum = computeChecksum(data.tasks);
9695
+ data.lastUpdated = now;
9696
+ if (accessor) {
9697
+ if (accessor.upsertSingleTask) {
9698
+ await accessor.upsertSingleTask(task);
9699
+ for (const parentId of autoCompleted) {
9700
+ const parent = data.tasks.find((t) => t.id === parentId);
9701
+ if (parent) await accessor.upsertSingleTask(parent);
9702
+ }
9703
+ } else {
9704
+ await safeSaveTaskFile(accessor, data, cwd);
9705
+ }
9706
+ await safeAppendLog(accessor, {
9707
+ id: `log-${Math.floor(Date.now() / 1e3)}-${(await import("node:crypto")).randomBytes(3).toString("hex")}`,
9708
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
9709
+ action: "task_completed",
9710
+ taskId: options.taskId,
9711
+ actor: "system",
9712
+ details: { title: task.title, previousStatus: before.status },
9713
+ before: null,
9714
+ after: { title: task.title, previousStatus: before.status }
9715
+ }, cwd);
9716
+ } else {
9717
+ await saveJson(taskPath, data, { backupDir });
9718
+ await logOperation(logPath, "task_completed", options.taskId, {
9719
+ title: task.title,
9720
+ previousStatus: before.status
9721
+ });
9722
+ }
9723
+ const dependents = getDependents(options.taskId, data.tasks);
9724
+ const unblockedTasks = [];
9725
+ for (const dep of dependents) {
9726
+ if (dep.status === "done" || dep.status === "cancelled") continue;
9727
+ const stillUnresolved = getUnresolvedDeps(dep.id, data.tasks);
9728
+ if (stillUnresolved.length === 0) {
9729
+ unblockedTasks.push({ id: dep.id, title: dep.title });
9730
+ }
9731
+ }
9732
+ return {
9733
+ task,
9734
+ ...autoCompleted.length > 0 && { autoCompleted },
9735
+ ...unblockedTasks.length > 0 && { unblockedTasks }
9736
+ };
9737
+ }
9738
+
9196
9739
  // src/core/tasks/delete.ts
9197
9740
  init_json();
9198
9741
  init_errors();
@@ -9498,6 +10041,16 @@ async function showTask(taskId, cwd, accessor) {
9498
10041
  title: dep?.title ?? "Unknown task"
9499
10042
  };
9500
10043
  });
10044
+ const unresolved = detail.dependencyStatus.filter(
10045
+ (d) => d.status !== "done" && d.status !== "cancelled"
10046
+ );
10047
+ if (unresolved.length > 0) {
10048
+ detail.unresolvedDeps = unresolved;
10049
+ }
10050
+ }
10051
+ const dependents = data.tasks.filter((t) => t.depends?.includes(taskId)).map((t) => t.id);
10052
+ if (dependents.length > 0) {
10053
+ detail.dependents = dependents;
9501
10054
  }
9502
10055
  const path = [taskId];
9503
10056
  let currentId = task.parentId;
@@ -9518,6 +10071,16 @@ async function showTask(taskId, cwd, accessor) {
9518
10071
  // src/core/tasks/list.ts
9519
10072
  init_json();
9520
10073
  init_paths();
10074
+ function toCompact(task) {
10075
+ return {
10076
+ id: task.id,
10077
+ title: task.title,
10078
+ status: task.status,
10079
+ priority: task.priority,
10080
+ type: task.type,
10081
+ parentId: task.parentId
10082
+ };
10083
+ }
9521
10084
  async function listTasks(options = {}, cwd, accessor) {
9522
10085
  const taskPath = getTaskPath(cwd);
9523
10086
  const data = accessor ? await accessor.loadTaskFile() : await readJsonRequired(taskPath);
@@ -9694,34 +10257,19 @@ async function findTasks(options, cwd, accessor) {
9694
10257
 
9695
10258
  // src/core/tasks/task-ops.ts
9696
10259
  init_data_accessor();
9697
- init_store();
10260
+ init_file_utils();
9698
10261
  init_status_registry();
9699
10262
  init_deps_ready();
9700
10263
  var PRIORITY_SCORE = {
9701
10264
  critical: 100,
9702
10265
  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;
10266
+ medium: 50,
10267
+ low: 25
10268
+ };
10269
+ async function loadAllTasks2(projectRoot) {
10270
+ const accessor = await getAccessor(projectRoot);
10271
+ const data = await accessor.loadTaskFile();
10272
+ return data.tasks;
9725
10273
  }
9726
10274
  function buildTreeNode(task, childrenMap) {
9727
10275
  const children = (childrenMap.get(task.id) ?? []).map(
@@ -9735,6 +10283,25 @@ function buildTreeNode(task, childrenMap) {
9735
10283
  children
9736
10284
  };
9737
10285
  }
10286
+ function buildUpstreamTree(taskId, taskMap, visited = /* @__PURE__ */ new Set()) {
10287
+ const task = taskMap.get(taskId);
10288
+ if (!task?.depends?.length) return [];
10289
+ const nodes = [];
10290
+ for (const depId of task.depends) {
10291
+ if (visited.has(depId)) continue;
10292
+ visited.add(depId);
10293
+ const dep = taskMap.get(depId);
10294
+ if (!dep) continue;
10295
+ nodes.push({
10296
+ id: dep.id,
10297
+ title: dep.title,
10298
+ status: dep.status,
10299
+ type: dep.type,
10300
+ children: buildUpstreamTree(depId, taskMap, visited)
10301
+ });
10302
+ }
10303
+ return nodes;
10304
+ }
9738
10305
  function countNodes(nodes) {
9739
10306
  let count2 = nodes.length;
9740
10307
  for (const node of nodes) {
@@ -9827,20 +10394,21 @@ async function coreTaskBlockers(projectRoot, params) {
9827
10394
  return dep && dep.status !== "done" && dep.status !== "cancelled";
9828
10395
  })
9829
10396
  );
10397
+ const tasksAsTask = allTasks;
9830
10398
  const blockerInfos = [
9831
10399
  ...blockedTasks.map((t) => ({
9832
10400
  id: t.id,
9833
10401
  title: t.title,
9834
10402
  status: t.status,
9835
10403
  depends: t.depends,
9836
- blockingChain: analyze ? buildBlockingChain(t, taskMap) : []
10404
+ blockingChain: analyze ? getTransitiveBlockers(t.id, tasksAsTask) : []
9837
10405
  })),
9838
10406
  ...depBlockedTasks.filter((t) => !blockedTasks.some((bt) => bt.id === t.id)).map((t) => ({
9839
10407
  id: t.id,
9840
10408
  title: t.title,
9841
10409
  status: t.status,
9842
10410
  depends: t.depends,
9843
- blockingChain: analyze ? buildBlockingChain(t, taskMap) : []
10411
+ blockingChain: analyze ? getTransitiveBlockers(t.id, tasksAsTask) : []
9844
10412
  }))
9845
10413
  ];
9846
10414
  const blockerCounts = /* @__PURE__ */ new Map();
@@ -9922,7 +10490,10 @@ async function coreTaskRelatesAdd(projectRoot, taskId, relatedId, type, reason)
9922
10490
  } else {
9923
10491
  await accessor.saveTaskFile(current);
9924
10492
  }
9925
- return { from: taskId, to: relatedId, type, added: true };
10493
+ if (accessor.addRelation) {
10494
+ await accessor.addRelation(taskId, relatedId, type, reason);
10495
+ }
10496
+ return { from: taskId, to: relatedId, type, reason, added: true };
9926
10497
  }
9927
10498
  async function coreTaskAnalyze(projectRoot, taskId) {
9928
10499
  const allTasks = await loadAllTasks2(projectRoot);
@@ -10242,7 +10813,63 @@ async function coreTaskComplexityEstimate(projectRoot, params) {
10242
10813
  else size = "large";
10243
10814
  return { size, score, factors, dependencyDepth, subtaskCount, fileCount };
10244
10815
  }
10245
- async function coreTaskDepends(projectRoot, taskId, direction = "both") {
10816
+ async function coreTaskDepsOverview(projectRoot) {
10817
+ const allTasks = await loadAllTasks2(projectRoot);
10818
+ const tasksAsTask = allTasks;
10819
+ const tasksWithDeps = allTasks.filter((t) => t.depends && t.depends.length > 0);
10820
+ const blocked = getBlockedTasks(tasksAsTask);
10821
+ const ready = getReadyTasks(tasksAsTask);
10822
+ const validation = validateDependencies(tasksAsTask);
10823
+ return {
10824
+ totalTasks: allTasks.length,
10825
+ tasksWithDeps: tasksWithDeps.length,
10826
+ blockedTasks: blocked.map((t) => ({
10827
+ id: t.id,
10828
+ title: t.title,
10829
+ status: t.status,
10830
+ unblockedBy: (t.depends ?? []).filter((depId) => {
10831
+ const dep = allTasks.find((x) => x.id === depId);
10832
+ return dep && dep.status !== "done" && dep.status !== "cancelled";
10833
+ })
10834
+ })),
10835
+ readyTasks: ready.filter((t) => t.status !== "done" && t.status !== "cancelled").map((t) => ({
10836
+ id: t.id,
10837
+ title: t.title,
10838
+ status: t.status
10839
+ })),
10840
+ validation: {
10841
+ valid: validation.valid,
10842
+ errorCount: validation.errors.length,
10843
+ warningCount: validation.warnings.length
10844
+ }
10845
+ };
10846
+ }
10847
+ async function coreTaskDepsCycles(projectRoot) {
10848
+ const allTasks = await loadAllTasks2(projectRoot);
10849
+ const tasksAsTask = allTasks;
10850
+ const taskMap = new Map(allTasks.map((t) => [t.id, t]));
10851
+ const visited = /* @__PURE__ */ new Set();
10852
+ const cycles = [];
10853
+ for (const task of allTasks) {
10854
+ if (visited.has(task.id)) continue;
10855
+ if (!task.depends?.length) continue;
10856
+ const cycle = detectCircularDeps(task.id, tasksAsTask);
10857
+ if (cycle.length > 0) {
10858
+ cycles.push({
10859
+ path: cycle,
10860
+ // Deduplicate: detectCircularDeps returns [A,B,C,A] where
10861
+ // last element closes the cycle. Use Set for robustness.
10862
+ tasks: [...new Set(cycle)].map((id) => {
10863
+ const t = taskMap.get(id);
10864
+ return { id, title: t?.title ?? "unknown" };
10865
+ })
10866
+ });
10867
+ cycle.forEach((id) => visited.add(id));
10868
+ }
10869
+ }
10870
+ return { hasCycles: cycles.length > 0, cycles };
10871
+ }
10872
+ async function coreTaskDepends(projectRoot, taskId, direction = "both", options) {
10246
10873
  const allTasks = await loadAllTasks2(projectRoot);
10247
10874
  const task = allTasks.find((t) => t.id === taskId);
10248
10875
  if (!task) {
@@ -10266,7 +10893,31 @@ async function coreTaskDepends(projectRoot, taskId, direction = "both") {
10266
10893
  }
10267
10894
  }
10268
10895
  }
10269
- return { taskId, direction, upstream, downstream };
10896
+ const tasksAsTask = allTasks;
10897
+ const transitiveIds = getTransitiveBlockers(taskId, tasksAsTask);
10898
+ const unresolvedChain = transitiveIds.length;
10899
+ const leafIds = getLeafBlockers(taskId, tasksAsTask);
10900
+ const leafBlockers = leafIds.map((id) => {
10901
+ const t = taskMap.get(id);
10902
+ return { id: t.id, title: t.title, status: t.status };
10903
+ });
10904
+ const allDepsReady = unresolvedChain === 0;
10905
+ const hint = unresolvedChain > 0 ? `Run 'ct deps show ${taskId} --tree' for full dependency graph` : void 0;
10906
+ let upstreamTree;
10907
+ if (options?.tree) {
10908
+ upstreamTree = buildUpstreamTree(taskId, taskMap);
10909
+ }
10910
+ return {
10911
+ taskId,
10912
+ direction,
10913
+ upstream,
10914
+ downstream,
10915
+ unresolvedChain,
10916
+ leafBlockers,
10917
+ allDepsReady,
10918
+ ...hint && { hint },
10919
+ ...upstreamTree && { upstreamTree }
10920
+ };
10270
10921
  }
10271
10922
 
10272
10923
  // src/dispatch/engines/task-engine.ts
@@ -10300,17 +10951,25 @@ async function taskList(projectRoot, params) {
10300
10951
  status: params?.status,
10301
10952
  limit: params?.limit
10302
10953
  }, projectRoot, accessor);
10954
+ if (params?.compact) {
10955
+ return { success: true, data: { tasks: result.tasks.map((t) => toCompact(t)), total: result.total } };
10956
+ }
10303
10957
  return { success: true, data: { tasks: tasksToRecords(result.tasks), total: result.total } };
10304
10958
  } catch {
10305
10959
  return engineError("E_NOT_INITIALIZED", "Task database not initialized");
10306
10960
  }
10307
10961
  }
10308
- async function taskFind(projectRoot, query, limit) {
10962
+ async function taskFind(projectRoot, query, limit, options) {
10309
10963
  try {
10310
10964
  const accessor = await getAccessor(projectRoot);
10311
10965
  const findResult = await findTasks({
10312
10966
  query,
10313
- limit: limit ?? 20
10967
+ id: options?.id,
10968
+ exact: options?.exact,
10969
+ status: options?.status,
10970
+ includeArchive: options?.includeArchive,
10971
+ limit: limit ?? 20,
10972
+ offset: options?.offset
10314
10973
  }, projectRoot, accessor);
10315
10974
  const results = findResult.results.map((r) => ({
10316
10975
  id: r.id,
@@ -10413,14 +11072,33 @@ async function taskUpdate(projectRoot, taskId, updates) {
10413
11072
  }
10414
11073
  }
10415
11074
  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`);
11075
+ try {
11076
+ const accessor = await getAccessor(projectRoot);
11077
+ const result = await completeTask({ taskId, notes }, void 0, accessor);
11078
+ return {
11079
+ success: true,
11080
+ data: {
11081
+ task: result.task,
11082
+ ...result.autoCompleted && { autoCompleted: result.autoCompleted },
11083
+ ...result.unblockedTasks && { unblockedTasks: result.unblockedTasks }
11084
+ }
11085
+ };
11086
+ } catch (err) {
11087
+ const message = err.message;
11088
+ if (message.includes("already completed")) {
11089
+ return engineError("E_TASK_COMPLETED", message);
11090
+ }
11091
+ if (message.includes("not found")) {
11092
+ return engineError("E_NOT_FOUND", message);
11093
+ }
11094
+ if (message.includes("incomplete dependencies")) {
11095
+ return engineError("E_DEPENDENCY_ERROR", message);
11096
+ }
11097
+ if (message.includes("incomplete children")) {
11098
+ return engineError("E_HAS_CHILDREN", message);
11099
+ }
11100
+ return engineError("E_INTERNAL", message);
10419
11101
  }
10420
- return taskUpdate(projectRoot, taskId, {
10421
- status: "done",
10422
- notes: notes || void 0
10423
- });
10424
11102
  }
10425
11103
  async function taskDelete(projectRoot, taskId, force) {
10426
11104
  try {
@@ -10625,9 +11303,9 @@ async function taskComplexityEstimate(projectRoot, params) {
10625
11303
  return engineError("E_NOT_INITIALIZED", "Task database not initialized");
10626
11304
  }
10627
11305
  }
10628
- async function taskDepends(projectRoot, taskId, direction = "both") {
11306
+ async function taskDepends(projectRoot, taskId, direction = "both", tree) {
10629
11307
  try {
10630
- const result = await coreTaskDepends(projectRoot, taskId, direction);
11308
+ const result = await coreTaskDepends(projectRoot, taskId, direction, tree ? { tree } : void 0);
10631
11309
  return { success: true, data: result };
10632
11310
  } catch (err) {
10633
11311
  const message = err instanceof Error ? err.message : String(err);
@@ -10637,6 +11315,24 @@ async function taskDepends(projectRoot, taskId, direction = "both") {
10637
11315
  return engineError("E_NOT_INITIALIZED", "Task database not initialized");
10638
11316
  }
10639
11317
  }
11318
+ async function taskDepsOverview(projectRoot) {
11319
+ try {
11320
+ const result = await coreTaskDepsOverview(projectRoot);
11321
+ return { success: true, data: result };
11322
+ } catch (err) {
11323
+ const message = err instanceof Error ? err.message : String(err);
11324
+ return engineError("E_NOT_INITIALIZED", message);
11325
+ }
11326
+ }
11327
+ async function taskDepsCycles(projectRoot) {
11328
+ try {
11329
+ const result = await coreTaskDepsCycles(projectRoot);
11330
+ return { success: true, data: result };
11331
+ } catch (err) {
11332
+ const message = err instanceof Error ? err.message : String(err);
11333
+ return engineError("E_NOT_INITIALIZED", message);
11334
+ }
11335
+ }
10640
11336
  async function taskPlan(projectRoot) {
10641
11337
  const { coreTaskPlan: coreTaskPlan2 } = await Promise.resolve().then(() => (init_plan(), plan_exports));
10642
11338
  try {
@@ -10894,7 +11590,7 @@ init_decisions();
10894
11590
  init_data_accessor();
10895
11591
  init_errors();
10896
11592
  init_exit_codes();
10897
- import { randomBytes as randomBytes3 } from "node:crypto";
11593
+ import { randomBytes as randomBytes4 } from "node:crypto";
10898
11594
  import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync5, existsSync as existsSync10 } from "node:fs";
10899
11595
  import { join as join10 } from "node:path";
10900
11596
  async function recordAssumption(projectRoot, params) {
@@ -10914,7 +11610,7 @@ async function recordAssumption(projectRoot, params) {
10914
11610
  const taskData = await accessor.loadTaskFile();
10915
11611
  const current = taskData;
10916
11612
  const sessionId = params.sessionId || current._meta?.activeSession || "default";
10917
- const id = `asm-${randomBytes3(8).toString("hex")}`;
11613
+ const id = `asm-${randomBytes4(8).toString("hex")}`;
10918
11614
  const now = (/* @__PURE__ */ new Date()).toISOString();
10919
11615
  const record = {
10920
11616
  id,
@@ -10981,6 +11677,12 @@ async function computeBriefing(projectRoot, options = {}) {
10981
11677
  scopeTaskIds
10982
11678
  });
10983
11679
  const pipelineStage = computePipelineStage(current);
11680
+ const warnings = [];
11681
+ if (currentTaskInfo?.blockedBy?.length) {
11682
+ warnings.push(
11683
+ `Focused task ${currentTaskInfo.id} is blocked by: ${currentTaskInfo.blockedBy.join(", ")}`
11684
+ );
11685
+ }
10984
11686
  return {
10985
11687
  lastSession,
10986
11688
  currentTask: currentTaskInfo,
@@ -10988,7 +11690,8 @@ async function computeBriefing(projectRoot, options = {}) {
10988
11690
  openBugs,
10989
11691
  blockedTasks,
10990
11692
  activeEpics,
10991
- ...pipelineStage && { pipelineStage }
11693
+ ...pipelineStage && { pipelineStage },
11694
+ ...warnings.length > 0 && { warnings }
10992
11695
  };
10993
11696
  }
10994
11697
  function parseScope(scopeStr, current) {
@@ -11076,11 +11779,21 @@ function computeCurrentTask(current, taskMap) {
11076
11779
  if (!focusTaskId) return null;
11077
11780
  const task = taskMap.get(focusTaskId);
11078
11781
  if (!task) return null;
11079
- return {
11782
+ const info = {
11080
11783
  id: task.id,
11081
11784
  title: task.title,
11082
11785
  status: task.status
11083
11786
  };
11787
+ if (task.depends?.length) {
11788
+ const unresolved = task.depends.filter((depId) => {
11789
+ const dep = taskMap.get(depId);
11790
+ return dep && dep.status !== "done" && dep.status !== "cancelled";
11791
+ });
11792
+ if (unresolved.length > 0) {
11793
+ info.blockedBy = unresolved;
11794
+ }
11795
+ }
11796
+ return info;
11084
11797
  }
11085
11798
  function calculateLeverage2(taskId, taskMap) {
11086
11799
  let leverage = 0;
@@ -11227,11 +11940,11 @@ function computePipelineStage(current) {
11227
11940
  }
11228
11941
 
11229
11942
  // src/core/sessions/session-id.ts
11230
- import { randomBytes as randomBytes4 } from "node:crypto";
11943
+ import { randomBytes as randomBytes5 } from "node:crypto";
11231
11944
  function generateSessionId() {
11232
11945
  const now = /* @__PURE__ */ new Date();
11233
11946
  const ts = now.toISOString().replace(/[-:T]/g, "").substring(0, 14);
11234
- const hex = randomBytes4(3).toString("hex");
11947
+ const hex = randomBytes5(3).toString("hex");
11235
11948
  return `ses_${ts}_${hex}`;
11236
11949
  }
11237
11950
 
@@ -11269,6 +11982,14 @@ async function startTask(taskId, cwd, accessor) {
11269
11982
  { fix: `Use 'cleo find "${taskId}"' to search` }
11270
11983
  );
11271
11984
  }
11985
+ const unresolvedDeps = getUnresolvedDeps(taskId, data.tasks);
11986
+ if (unresolvedDeps.length > 0) {
11987
+ throw new CleoError(
11988
+ 5 /* DEPENDENCY_ERROR */,
11989
+ `Task ${taskId} is blocked by unresolved dependencies: ${unresolvedDeps.join(", ")}`,
11990
+ { fix: `Complete blockers first: ${unresolvedDeps.map((d) => `cleo complete ${d}`).join(", ")}` }
11991
+ );
11992
+ }
11272
11993
  const previousTask = data.focus?.currentTask ?? null;
11273
11994
  if (!data.focus) {
11274
11995
  data.focus = {};
@@ -11327,6 +12048,8 @@ async function stopTask(cwd, accessor) {
11327
12048
  }
11328
12049
 
11329
12050
  // src/dispatch/engines/session-engine.ts
12051
+ init_errors();
12052
+ init_exit_codes();
11330
12053
  async function sessionStatus(projectRoot) {
11331
12054
  try {
11332
12055
  const accessor = await getAccessor(projectRoot);
@@ -11396,6 +12119,12 @@ async function taskStart(projectRoot, taskId) {
11396
12119
  data: { taskId: result.taskId, previousTask: result.previousTask }
11397
12120
  };
11398
12121
  } catch (err) {
12122
+ if (err instanceof CleoError) {
12123
+ const stringCode = `E_${getExitCodeName(err.code)}`;
12124
+ return engineError(stringCode, err.message, {
12125
+ ...err.fix && { fix: err.fix }
12126
+ });
12127
+ }
11399
12128
  const message = err.message;
11400
12129
  const code = message.includes("not found") ? "E_NOT_FOUND" : "E_NOT_INITIALIZED";
11401
12130
  return engineError(code, message);
@@ -11860,8 +12589,8 @@ async function sessionChainShow(projectRoot, sessionId) {
11860
12589
  // src/dispatch/engines/system-engine.ts
11861
12590
  init_platform();
11862
12591
  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";
12592
+ import { readFileSync as readFileSync13, existsSync as existsSync20, readdirSync as readdirSync5 } from "node:fs";
12593
+ import { join as join20, basename as basename4 } from "node:path";
11865
12594
 
11866
12595
  // src/core/stats/index.ts
11867
12596
  init_json();
@@ -11989,14 +12718,14 @@ init_paths();
11989
12718
  // src/core/system/inject-generate.ts
11990
12719
  init_json();
11991
12720
  init_paths();
11992
- import { readFileSync as readFileSync7, existsSync as existsSync13 } from "node:fs";
11993
- import { join as join13 } from "node:path";
12721
+ import { readFileSync as readFileSync6, existsSync as existsSync12 } from "node:fs";
12722
+ import { join as join12 } from "node:path";
11994
12723
  async function generateInjection(projectRoot, accessor) {
11995
12724
  let version = "unknown";
11996
12725
  try {
11997
- const pkgPath = join13(projectRoot, "package.json");
11998
- if (existsSync13(pkgPath)) {
11999
- const pkg = JSON.parse(readFileSync7(pkgPath, "utf-8"));
12726
+ const pkgPath = join12(projectRoot, "package.json");
12727
+ if (existsSync12(pkgPath)) {
12728
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
12000
12729
  version = pkg.version || "unknown";
12001
12730
  }
12002
12731
  } catch {
@@ -12011,8 +12740,8 @@ async function generateInjection(projectRoot, accessor) {
12011
12740
  }
12012
12741
  try {
12013
12742
  const sessionsPath = getSessionsPath(projectRoot);
12014
- if (existsSync13(sessionsPath)) {
12015
- const sessionsData = JSON.parse(readFileSync7(sessionsPath, "utf-8"));
12743
+ if (existsSync12(sessionsPath)) {
12744
+ const sessionsData = JSON.parse(readFileSync6(sessionsPath, "utf-8"));
12016
12745
  const active = sessionsData.sessions?.find((s) => s.status === "active");
12017
12746
  if (active) {
12018
12747
  activeSessionName = active.name || active.id;
@@ -12093,8 +12822,8 @@ For full protocol details, load the **ct-cleo** skill: \`cleo_query({ domain: "s
12093
12822
  }
12094
12823
 
12095
12824
  // 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";
12825
+ import { existsSync as existsSync14, readFileSync as readFileSync8, statSync as statSync4 } from "node:fs";
12826
+ import { join as join14 } from "node:path";
12098
12827
  import { execFile as execFile3 } from "node:child_process";
12099
12828
  import { promisify as promisify3 } from "node:util";
12100
12829
 
@@ -12106,25 +12835,25 @@ init_platform();
12106
12835
 
12107
12836
  // src/core/migration/agent-outputs.ts
12108
12837
  import {
12109
- existsSync as existsSync14,
12110
- readFileSync as readFileSync8,
12111
- writeFileSync as writeFileSync3,
12112
- mkdirSync as mkdirSync7,
12838
+ existsSync as existsSync13,
12839
+ readFileSync as readFileSync7,
12840
+ writeFileSync as writeFileSync2,
12841
+ mkdirSync as mkdirSync6,
12113
12842
  readdirSync as readdirSync3,
12114
- copyFileSync,
12843
+ copyFileSync as copyFileSync2,
12115
12844
  statSync as statSync3,
12116
12845
  rmSync
12117
12846
  } from "node:fs";
12118
- import { join as join14 } from "node:path";
12847
+ import { join as join13 } from "node:path";
12119
12848
  var CANONICAL_DIR = ".cleo/agent-outputs";
12120
12849
  var MANIFEST_PATH_REWRITES = [
12121
12850
  [/claudedocs\/research-outputs\//g, `${CANONICAL_DIR}/`],
12122
12851
  [/claudedocs\/agent-outputs\//g, `${CANONICAL_DIR}/`]
12123
12852
  ];
12124
12853
  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"));
12854
+ const hasResearchOutputs = existsSync13(join13(projectRoot, "claudedocs", "research-outputs"));
12855
+ const hasLegacyAgentOutputs = existsSync13(join13(projectRoot, "claudedocs", "agent-outputs"));
12856
+ const hasCanonical = existsSync13(join13(cleoDir, "agent-outputs"));
12128
12857
  const legacyPaths = [];
12129
12858
  if (hasResearchOutputs) legacyPaths.push("claudedocs/research-outputs/");
12130
12859
  if (hasLegacyAgentOutputs) legacyPaths.push("claudedocs/agent-outputs/");
@@ -12147,30 +12876,30 @@ function migrateAgentOutputs(projectRoot, cleoDir) {
12147
12876
  summary: "No legacy output directories found"
12148
12877
  };
12149
12878
  }
12150
- const newDir = join14(cleoDir, "agent-outputs");
12879
+ const newDir = join13(cleoDir, "agent-outputs");
12151
12880
  const hadCanonical = detection.hasCanonical;
12152
- mkdirSync7(newDir, { recursive: true });
12881
+ mkdirSync6(newDir, { recursive: true });
12153
12882
  let totalCopied = 0;
12154
12883
  const mergedManifestLines = [];
12155
12884
  const copiedFiles = /* @__PURE__ */ new Set();
12156
12885
  const legacySources = [
12157
- { path: join14(projectRoot, "claudedocs", "research-outputs"), exists: detection.hasResearchOutputs },
12158
- { path: join14(projectRoot, "claudedocs", "agent-outputs"), exists: detection.hasLegacyAgentOutputs }
12886
+ { path: join13(projectRoot, "claudedocs", "research-outputs"), exists: detection.hasResearchOutputs },
12887
+ { path: join13(projectRoot, "claudedocs", "agent-outputs"), exists: detection.hasLegacyAgentOutputs }
12159
12888
  ];
12160
12889
  for (const source of legacySources) {
12161
12890
  if (!source.exists) continue;
12162
12891
  totalCopied += copyDirContents(source.path, newDir, mergedManifestLines, copiedFiles);
12163
12892
  }
12164
- const manifestEntries = mergeManifests(newDir, hadCanonical, mergedManifestLines);
12893
+ const manifestEntries2 = mergeManifests(newDir, hadCanonical, mergedManifestLines);
12165
12894
  updateConfigPaths(cleoDir);
12166
12895
  const removed = removeLegacyDirs(projectRoot, detection);
12167
12896
  const parts = [`Migrated ${totalCopied} files \u2192 ${CANONICAL_DIR}/`];
12168
- if (manifestEntries > 0) parts.push(`merged ${manifestEntries} manifest entries`);
12897
+ if (manifestEntries2 > 0) parts.push(`merged ${manifestEntries2} manifest entries`);
12169
12898
  if (removed.length > 0) parts.push(`removed: ${removed.join(", ")}`);
12170
12899
  return {
12171
12900
  migrated: true,
12172
12901
  filesCopied: totalCopied,
12173
- manifestEntries,
12902
+ manifestEntries: manifestEntries2,
12174
12903
  removed,
12175
12904
  summary: parts.join("; ")
12176
12905
  };
@@ -12180,19 +12909,19 @@ function copyDirContents(srcDir, dstDir, manifestLines, copiedFiles) {
12180
12909
  const entries = readdirSync3(srcDir);
12181
12910
  for (const entry of entries) {
12182
12911
  if (entry === "MANIFEST.jsonl") {
12183
- collectManifestLines(join14(srcDir, entry), manifestLines);
12912
+ collectManifestLines(join13(srcDir, entry), manifestLines);
12184
12913
  continue;
12185
12914
  }
12186
- const srcPath = join14(srcDir, entry);
12187
- const dstPath = join14(dstDir, entry);
12915
+ const srcPath = join13(srcDir, entry);
12916
+ const dstPath = join13(dstDir, entry);
12188
12917
  try {
12189
12918
  const st = statSync3(srcPath);
12190
12919
  if (st.isDirectory()) {
12191
- mkdirSync7(dstPath, { recursive: true });
12920
+ mkdirSync6(dstPath, { recursive: true });
12192
12921
  for (const sf of readdirSync3(srcPath)) {
12193
12922
  if (!copiedFiles.has(sf)) {
12194
12923
  try {
12195
- copyFileSync(join14(srcPath, sf), join14(dstPath, sf));
12924
+ copyFileSync2(join13(srcPath, sf), join13(dstPath, sf));
12196
12925
  copiedFiles.add(sf);
12197
12926
  count2++;
12198
12927
  } catch {
@@ -12200,7 +12929,7 @@ function copyDirContents(srcDir, dstDir, manifestLines, copiedFiles) {
12200
12929
  }
12201
12930
  }
12202
12931
  } else if (!copiedFiles.has(entry)) {
12203
- copyFileSync(srcPath, dstPath);
12932
+ copyFileSync2(srcPath, dstPath);
12204
12933
  copiedFiles.add(entry);
12205
12934
  count2++;
12206
12935
  }
@@ -12211,7 +12940,7 @@ function copyDirContents(srcDir, dstDir, manifestLines, copiedFiles) {
12211
12940
  }
12212
12941
  function collectManifestLines(manifestPath, out) {
12213
12942
  try {
12214
- const content = readFileSync8(manifestPath, "utf-8");
12943
+ const content = readFileSync7(manifestPath, "utf-8");
12215
12944
  for (const line of content.split("\n")) {
12216
12945
  if (!line.trim()) continue;
12217
12946
  let rewritten = line;
@@ -12224,11 +12953,11 @@ function collectManifestLines(manifestPath, out) {
12224
12953
  }
12225
12954
  }
12226
12955
  function mergeManifests(newDir, hadCanonical, legacyLines) {
12227
- const manifestPath = join14(newDir, "MANIFEST.jsonl");
12956
+ const manifestPath = join13(newDir, "MANIFEST.jsonl");
12228
12957
  const existingLines = [];
12229
- if (hadCanonical && existsSync14(manifestPath)) {
12958
+ if (hadCanonical && existsSync13(manifestPath)) {
12230
12959
  try {
12231
- const existing = readFileSync8(manifestPath, "utf-8");
12960
+ const existing = readFileSync7(manifestPath, "utf-8");
12232
12961
  for (const line of existing.split("\n")) {
12233
12962
  if (line.trim()) existingLines.push(line);
12234
12963
  }
@@ -12255,15 +12984,15 @@ function mergeManifests(newDir, hadCanonical, legacyLines) {
12255
12984
  finalLines.push(line);
12256
12985
  }
12257
12986
  if (finalLines.length > 0) {
12258
- writeFileSync3(manifestPath, finalLines.join("\n") + "\n");
12987
+ writeFileSync2(manifestPath, finalLines.join("\n") + "\n");
12259
12988
  }
12260
12989
  return finalLines.length;
12261
12990
  }
12262
12991
  function updateConfigPaths(cleoDir) {
12263
- const configPath = join14(cleoDir, "config.json");
12264
- if (!existsSync14(configPath)) return;
12992
+ const configPath = join13(cleoDir, "config.json");
12993
+ if (!existsSync13(configPath)) return;
12265
12994
  try {
12266
- const config = JSON.parse(readFileSync8(configPath, "utf-8"));
12995
+ const config = JSON.parse(readFileSync7(configPath, "utf-8"));
12267
12996
  const currentDir = config.agentOutputs?.directory ?? config.agentOutputs ?? config.research?.outputDir;
12268
12997
  if (currentDir && currentDir !== CANONICAL_DIR) {
12269
12998
  if (typeof config.agentOutputs === "object") {
@@ -12277,7 +13006,7 @@ function updateConfigPaths(cleoDir) {
12277
13006
  delete config.research;
12278
13007
  }
12279
13008
  }
12280
- writeFileSync3(configPath, JSON.stringify(config, null, 2));
13009
+ writeFileSync2(configPath, JSON.stringify(config, null, 2));
12281
13010
  }
12282
13011
  } catch {
12283
13012
  }
@@ -12286,20 +13015,20 @@ function removeLegacyDirs(projectRoot, detection) {
12286
13015
  const removed = [];
12287
13016
  if (detection.hasResearchOutputs) {
12288
13017
  try {
12289
- rmSync(join14(projectRoot, "claudedocs", "research-outputs"), { recursive: true, force: true });
13018
+ rmSync(join13(projectRoot, "claudedocs", "research-outputs"), { recursive: true, force: true });
12290
13019
  removed.push("claudedocs/research-outputs/");
12291
13020
  } catch {
12292
13021
  }
12293
13022
  }
12294
13023
  if (detection.hasLegacyAgentOutputs) {
12295
13024
  try {
12296
- rmSync(join14(projectRoot, "claudedocs", "agent-outputs"), { recursive: true, force: true });
13025
+ rmSync(join13(projectRoot, "claudedocs", "agent-outputs"), { recursive: true, force: true });
12297
13026
  removed.push("claudedocs/agent-outputs/");
12298
13027
  } catch {
12299
13028
  }
12300
13029
  }
12301
- const claudedocsDir = join14(projectRoot, "claudedocs");
12302
- if (existsSync14(claudedocsDir)) {
13030
+ const claudedocsDir = join13(projectRoot, "claudedocs");
13031
+ if (existsSync13(claudedocsDir)) {
12303
13032
  try {
12304
13033
  if (readdirSync3(claudedocsDir).length === 0) {
12305
13034
  rmSync(claudedocsDir, { recursive: true, force: true });
@@ -12316,15 +13045,15 @@ init_data_accessor();
12316
13045
  var execAsync = promisify3(execFile3);
12317
13046
  var STALE_JSON_FILES = ["todo.json", "sessions.json", "todo-archive.json"];
12318
13047
  function getSystemHealth(projectRoot, opts) {
12319
- const cleoDir = join15(projectRoot, ".cleo");
13048
+ const cleoDir = join14(projectRoot, ".cleo");
12320
13049
  const checks = [];
12321
- if (existsSync15(cleoDir)) {
13050
+ if (existsSync14(cleoDir)) {
12322
13051
  checks.push({ name: "cleo_dir", status: "pass", message: ".cleo directory exists" });
12323
13052
  } else {
12324
13053
  checks.push({ name: "cleo_dir", status: "fail", message: ".cleo directory not found" });
12325
13054
  }
12326
- const dbPath = join15(cleoDir, "tasks.db");
12327
- if (existsSync15(dbPath)) {
13055
+ const dbPath = join14(cleoDir, "tasks.db");
13056
+ if (existsSync14(dbPath)) {
12328
13057
  try {
12329
13058
  const dbSize = statSync4(dbPath).size;
12330
13059
  if (dbSize > 0) {
@@ -12338,10 +13067,10 @@ function getSystemHealth(projectRoot, opts) {
12338
13067
  } else {
12339
13068
  checks.push({ name: "tasks_db", status: "fail", message: "tasks.db not found" });
12340
13069
  }
12341
- const configPath = join15(cleoDir, "config.json");
12342
- if (existsSync15(configPath)) {
13070
+ const configPath = join14(cleoDir, "config.json");
13071
+ if (existsSync14(configPath)) {
12343
13072
  try {
12344
- JSON.parse(readFileSync9(configPath, "utf-8"));
13073
+ JSON.parse(readFileSync8(configPath, "utf-8"));
12345
13074
  checks.push({ name: "config_json", status: "pass", message: "config.json is valid JSON" });
12346
13075
  } catch {
12347
13076
  checks.push({ name: "config_json", status: "warn", message: "config.json is not valid JSON" });
@@ -12349,8 +13078,8 @@ function getSystemHealth(projectRoot, opts) {
12349
13078
  } else {
12350
13079
  checks.push({ name: "config_json", status: "warn", message: "config.json not found" });
12351
13080
  }
12352
- if (existsSync15(dbPath)) {
12353
- const staleFiles = STALE_JSON_FILES.filter((f) => existsSync15(join15(cleoDir, f)));
13081
+ if (existsSync14(dbPath)) {
13082
+ const staleFiles = STALE_JSON_FILES.filter((f) => existsSync14(join14(cleoDir, f)));
12354
13083
  if (staleFiles.length > 0) {
12355
13084
  checks.push({
12356
13085
  name: "stale_json",
@@ -12360,14 +13089,14 @@ function getSystemHealth(projectRoot, opts) {
12360
13089
  }
12361
13090
  }
12362
13091
  if (opts?.detailed) {
12363
- const logPath = join15(cleoDir, "todo-log.jsonl");
12364
- if (existsSync15(logPath)) {
13092
+ const logPath = join14(cleoDir, "todo-log.jsonl");
13093
+ if (existsSync14(logPath)) {
12365
13094
  checks.push({ name: "log_file", status: "pass", message: "todo-log.jsonl exists" });
12366
13095
  } else {
12367
13096
  checks.push({ name: "log_file", status: "warn", message: "todo-log.jsonl not found" });
12368
13097
  }
12369
- const backupDir = join15(cleoDir, ".backups");
12370
- if (existsSync15(backupDir)) {
13098
+ const backupDir = join14(cleoDir, ".backups");
13099
+ if (existsSync14(backupDir)) {
12371
13100
  checks.push({ name: "backups_dir", status: "pass", message: ".backups directory exists" });
12372
13101
  } else {
12373
13102
  checks.push({ name: "backups_dir", status: "pass", message: "No backups directory (created on first write)" });
@@ -12375,9 +13104,9 @@ function getSystemHealth(projectRoot, opts) {
12375
13104
  }
12376
13105
  let version = "unknown";
12377
13106
  try {
12378
- const pkgPath = join15(projectRoot, "package.json");
12379
- if (existsSync15(pkgPath)) {
12380
- const pkg = JSON.parse(readFileSync9(pkgPath, "utf-8"));
13107
+ const pkgPath = join14(projectRoot, "package.json");
13108
+ if (existsSync14(pkgPath)) {
13109
+ const pkg = JSON.parse(readFileSync8(pkgPath, "utf-8"));
12381
13110
  version = pkg.version || "unknown";
12382
13111
  }
12383
13112
  } catch {
@@ -12396,34 +13125,34 @@ init_paths();
12396
13125
  // src/core/system/backup.ts
12397
13126
  init_errors();
12398
13127
  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";
13128
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync3, existsSync as existsSync15, mkdirSync as mkdirSync7 } from "node:fs";
13129
+ import { join as join15 } from "node:path";
12401
13130
  function createBackup2(projectRoot, opts) {
12402
- const cleoDir = join16(projectRoot, ".cleo");
13131
+ const cleoDir = join15(projectRoot, ".cleo");
12403
13132
  const btype = opts?.type || "snapshot";
12404
13133
  const timestamp = (/* @__PURE__ */ new Date()).toISOString();
12405
13134
  const backupId = `${btype}-${timestamp.replace(/[:.]/g, "-")}`;
12406
- const backupDir = join16(cleoDir, "backups", btype);
12407
- if (!existsSync16(backupDir)) {
12408
- mkdirSync8(backupDir, { recursive: true });
13135
+ const backupDir = join15(cleoDir, "backups", btype);
13136
+ if (!existsSync15(backupDir)) {
13137
+ mkdirSync7(backupDir, { recursive: true });
12409
13138
  }
12410
13139
  const filesToBackup = ["todo.json", "todo-archive.json", "sessions.json", "config.json", "todo-log.jsonl"];
12411
13140
  const backedUp = [];
12412
13141
  for (const file of filesToBackup) {
12413
- const src = join16(cleoDir, file);
12414
- if (existsSync16(src)) {
12415
- const dest = join16(backupDir, `${file}.${backupId}`);
13142
+ const src = join15(cleoDir, file);
13143
+ if (existsSync15(src)) {
13144
+ const dest = join15(backupDir, `${file}.${backupId}`);
12416
13145
  try {
12417
- const content = readFileSync10(src, "utf-8");
12418
- writeFileSync4(dest, content, "utf-8");
13146
+ const content = readFileSync9(src, "utf-8");
13147
+ writeFileSync3(dest, content, "utf-8");
12419
13148
  backedUp.push(file);
12420
13149
  } catch {
12421
13150
  }
12422
13151
  }
12423
13152
  }
12424
- const metaPath = join16(backupDir, `${backupId}.meta.json`);
13153
+ const metaPath = join15(backupDir, `${backupId}.meta.json`);
12425
13154
  try {
12426
- writeFileSync4(metaPath, JSON.stringify({
13155
+ writeFileSync3(metaPath, JSON.stringify({
12427
13156
  backupId,
12428
13157
  type: btype,
12429
13158
  timestamp,
@@ -12438,15 +13167,15 @@ function restoreBackup(projectRoot, params) {
12438
13167
  if (!params.backupId) {
12439
13168
  throw new CleoError(2 /* INVALID_INPUT */, "backupId is required");
12440
13169
  }
12441
- const cleoDir = join16(projectRoot, ".cleo");
13170
+ const cleoDir = join15(projectRoot, ".cleo");
12442
13171
  const backupTypes = ["snapshot", "safety", "migration"];
12443
13172
  let metaPath = null;
12444
13173
  let backupDir = null;
12445
13174
  for (const btype of backupTypes) {
12446
- const candidateMeta = join16(cleoDir, "backups", btype, `${params.backupId}.meta.json`);
12447
- if (existsSync16(candidateMeta)) {
13175
+ const candidateMeta = join15(cleoDir, "backups", btype, `${params.backupId}.meta.json`);
13176
+ if (existsSync15(candidateMeta)) {
12448
13177
  metaPath = candidateMeta;
12449
- backupDir = join16(cleoDir, "backups", btype);
13178
+ backupDir = join15(cleoDir, "backups", btype);
12450
13179
  break;
12451
13180
  }
12452
13181
  }
@@ -12455,17 +13184,17 @@ function restoreBackup(projectRoot, params) {
12455
13184
  }
12456
13185
  let meta;
12457
13186
  try {
12458
- meta = JSON.parse(readFileSync10(metaPath, "utf-8"));
13187
+ meta = JSON.parse(readFileSync9(metaPath, "utf-8"));
12459
13188
  } catch {
12460
13189
  throw new CleoError(3 /* FILE_ERROR */, "Failed to read backup metadata");
12461
13190
  }
12462
13191
  const restored = [];
12463
13192
  for (const file of meta.files ?? []) {
12464
- const backupFile = join16(backupDir, `${file}.${params.backupId}`);
12465
- if (existsSync16(backupFile)) {
13193
+ const backupFile = join15(backupDir, `${file}.${params.backupId}`);
13194
+ if (existsSync15(backupFile)) {
12466
13195
  try {
12467
- const content = readFileSync10(backupFile, "utf-8");
12468
- writeFileSync4(join16(cleoDir, file), content, "utf-8");
13196
+ const content = readFileSync9(backupFile, "utf-8");
13197
+ writeFileSync3(join15(cleoDir, file), content, "utf-8");
12469
13198
  restored.push(file);
12470
13199
  } catch {
12471
13200
  }
@@ -12482,14 +13211,14 @@ function restoreBackup(projectRoot, params) {
12482
13211
  // src/core/system/migrate.ts
12483
13212
  init_errors();
12484
13213
  init_exit_codes();
12485
- import { readFileSync as readFileSync11, existsSync as existsSync17 } from "node:fs";
12486
- import { join as join17 } from "node:path";
13214
+ import { readFileSync as readFileSync10, existsSync as existsSync16 } from "node:fs";
13215
+ import { join as join16 } from "node:path";
12487
13216
  function getMigrationStatus(projectRoot, opts) {
12488
- const taskPath = join17(projectRoot, ".cleo", "tasks.json");
13217
+ const taskPath = join16(projectRoot, ".cleo", "tasks.json");
12489
13218
  let currentVersion = "unknown";
12490
- if (existsSync17(taskPath)) {
13219
+ if (existsSync16(taskPath)) {
12491
13220
  try {
12492
- const taskFile = JSON.parse(readFileSync11(taskPath, "utf-8"));
13221
+ const taskFile = JSON.parse(readFileSync10(taskPath, "utf-8"));
12493
13222
  currentVersion = taskFile._meta?.schemaVersion ?? taskFile.version ?? "unknown";
12494
13223
  } catch {
12495
13224
  throw new CleoError(3 /* FILE_ERROR */, "Failed to read tasks.json");
@@ -12509,21 +13238,21 @@ function getMigrationStatus(projectRoot, opts) {
12509
13238
  // src/core/system/cleanup.ts
12510
13239
  init_errors();
12511
13240
  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";
13241
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync4, existsSync as existsSync17, readdirSync as readdirSync4, unlinkSync as unlinkSync4 } from "node:fs";
13242
+ import { join as join17 } from "node:path";
12514
13243
  function cleanupSystem(projectRoot, params) {
12515
13244
  if (!params.target) {
12516
13245
  throw new CleoError(2 /* INVALID_INPUT */, "target is required (sessions|backups|logs|archive)");
12517
13246
  }
12518
- const cleoDir = join18(projectRoot, ".cleo");
13247
+ const cleoDir = join17(projectRoot, ".cleo");
12519
13248
  const dryRun = params.dryRun ?? false;
12520
13249
  const items = [];
12521
13250
  switch (params.target) {
12522
13251
  case "sessions": {
12523
- const sessPath = join18(cleoDir, "sessions.json");
12524
- if (existsSync18(sessPath)) {
13252
+ const sessPath = join17(cleoDir, "sessions.json");
13253
+ if (existsSync17(sessPath)) {
12525
13254
  try {
12526
- const data = JSON.parse(readFileSync12(sessPath, "utf-8"));
13255
+ const data = JSON.parse(readFileSync11(sessPath, "utf-8"));
12527
13256
  const sessions2 = data.sessions ?? [];
12528
13257
  const stale = sessions2.filter((s) => {
12529
13258
  if (s.status !== "active") return false;
@@ -12539,7 +13268,7 @@ function cleanupSystem(projectRoot, params) {
12539
13268
  if (!dryRun && stale.length > 0) {
12540
13269
  const staleIds = new Set(stale.map((s) => s.id));
12541
13270
  data.sessions = sessions2.filter((s) => !staleIds.has(s.id));
12542
- writeFileSync5(sessPath, JSON.stringify(data, null, 2), "utf-8");
13271
+ writeFileSync4(sessPath, JSON.stringify(data, null, 2), "utf-8");
12543
13272
  }
12544
13273
  } catch {
12545
13274
  }
@@ -12547,16 +13276,16 @@ function cleanupSystem(projectRoot, params) {
12547
13276
  break;
12548
13277
  }
12549
13278
  case "backups": {
12550
- const backupBaseDir = join18(cleoDir, "backups");
12551
- if (existsSync18(backupBaseDir)) {
13279
+ const backupBaseDir = join17(cleoDir, "backups");
13280
+ if (existsSync17(backupBaseDir)) {
12552
13281
  for (const typeDir of readdirSync4(backupBaseDir)) {
12553
- const fullDir = join18(backupBaseDir, typeDir);
13282
+ const fullDir = join17(backupBaseDir, typeDir);
12554
13283
  try {
12555
13284
  for (const file of readdirSync4(fullDir)) {
12556
13285
  if (file.endsWith(".meta.json")) {
12557
- const metaFilePath = join18(fullDir, file);
13286
+ const metaFilePath = join17(fullDir, file);
12558
13287
  try {
12559
- const meta = JSON.parse(readFileSync12(metaFilePath, "utf-8"));
13288
+ const meta = JSON.parse(readFileSync11(metaFilePath, "utf-8"));
12560
13289
  if (params.olderThan && meta.timestamp < params.olderThan) {
12561
13290
  items.push(file.replace(".meta.json", ""));
12562
13291
  if (!dryRun) {
@@ -12564,7 +13293,7 @@ function cleanupSystem(projectRoot, params) {
12564
13293
  for (const bf of readdirSync4(fullDir)) {
12565
13294
  if (bf.includes(meta.backupId)) {
12566
13295
  try {
12567
- unlinkSync4(join18(fullDir, bf));
13296
+ unlinkSync4(join17(fullDir, bf));
12568
13297
  } catch {
12569
13298
  }
12570
13299
  }
@@ -12583,13 +13312,13 @@ function cleanupSystem(projectRoot, params) {
12583
13312
  }
12584
13313
  case "logs": {
12585
13314
  const auditPattern = /^audit-log-.*\.json$/;
12586
- if (existsSync18(cleoDir)) {
13315
+ if (existsSync17(cleoDir)) {
12587
13316
  for (const file of readdirSync4(cleoDir)) {
12588
13317
  if (auditPattern.test(file)) {
12589
13318
  items.push(file);
12590
13319
  if (!dryRun) {
12591
13320
  try {
12592
- unlinkSync4(join18(cleoDir, file));
13321
+ unlinkSync4(join17(cleoDir, file));
12593
13322
  } catch {
12594
13323
  }
12595
13324
  }
@@ -12607,17 +13336,17 @@ function cleanupSystem(projectRoot, params) {
12607
13336
  // src/core/system/safestop.ts
12608
13337
  init_errors();
12609
13338
  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";
13339
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync5, existsSync as existsSync18 } from "node:fs";
13340
+ import { join as join18 } from "node:path";
12612
13341
  function safestop(projectRoot, opts) {
12613
13342
  const dryRun = opts?.dryRun ?? false;
12614
13343
  const reason = opts?.reason ?? "Manual safestop";
12615
13344
  let sessionEnded = false;
12616
13345
  if (!dryRun && !opts?.noSessionEnd) {
12617
- const sessPath = join19(projectRoot, ".cleo", "sessions.json");
12618
- if (existsSync19(sessPath)) {
13346
+ const sessPath = join18(projectRoot, ".cleo", "sessions.json");
13347
+ if (existsSync18(sessPath)) {
12619
13348
  try {
12620
- const data = JSON.parse(readFileSync13(sessPath, "utf-8"));
13349
+ const data = JSON.parse(readFileSync12(sessPath, "utf-8"));
12621
13350
  const sessions2 = data.sessions ?? [];
12622
13351
  let changed = false;
12623
13352
  for (const s of sessions2) {
@@ -12630,7 +13359,7 @@ function safestop(projectRoot, opts) {
12630
13359
  }
12631
13360
  }
12632
13361
  if (changed) {
12633
- writeFileSync6(sessPath, JSON.stringify(data, null, 2), "utf-8");
13362
+ writeFileSync5(sessPath, JSON.stringify(data, null, 2), "utf-8");
12634
13363
  }
12635
13364
  } catch {
12636
13365
  }
@@ -12643,9 +13372,9 @@ function safestop(projectRoot, opts) {
12643
13372
  init_sequence();
12644
13373
 
12645
13374
  // src/core/system/runtime.ts
12646
- import { existsSync as existsSync20 } from "node:fs";
13375
+ import { existsSync as existsSync19 } from "node:fs";
12647
13376
  import { readFile as readFile3 } from "node:fs/promises";
12648
- import { basename as basename4, join as join20 } from "node:path";
13377
+ import { basename as basename3, join as join19 } from "node:path";
12649
13378
  import { homedir as homedir2 } from "node:os";
12650
13379
  import { execFile as execFile4 } from "node:child_process";
12651
13380
  import { promisify as promisify4 } from "node:util";
@@ -12679,8 +13408,8 @@ function getExpectedNaming(channel) {
12679
13408
  }
12680
13409
  }
12681
13410
  async function parseVersionFile(dataRoot) {
12682
- const versionPath = join20(dataRoot, "VERSION");
12683
- if (!existsSync20(versionPath)) return null;
13411
+ const versionPath = join19(dataRoot, "VERSION");
13412
+ if (!existsSync19(versionPath)) return null;
12684
13413
  let content;
12685
13414
  try {
12686
13415
  content = await readFile3(versionPath, "utf-8");
@@ -12709,9 +13438,9 @@ async function parseVersionFile(dataRoot) {
12709
13438
  async function getPackageInfo(sourceDir) {
12710
13439
  const candidates = [];
12711
13440
  if (sourceDir && sourceDir !== "unknown" && sourceDir !== "npm") {
12712
- candidates.push(join20(sourceDir, "package.json"));
13441
+ candidates.push(join19(sourceDir, "package.json"));
12713
13442
  }
12714
- candidates.push(join20(process.cwd(), "package.json"));
13443
+ candidates.push(join19(process.cwd(), "package.json"));
12715
13444
  for (const candidate of candidates) {
12716
13445
  try {
12717
13446
  const raw = await readFile3(candidate, "utf-8");
@@ -12735,9 +13464,9 @@ async function resolveBinaryPath(name) {
12735
13464
  }
12736
13465
  async function getRuntimeDiagnostics(options) {
12737
13466
  const scriptPath = process.argv[1] ?? "";
12738
- const invocationName = basename4(scriptPath || process.argv0 || "cleo");
13467
+ const invocationName = basename3(scriptPath || process.argv0 || "cleo");
12739
13468
  const envChannel = normalizeChannel(process.env["CLEO_CHANNEL"]);
12740
- const dataRoot = process.env["CLEO_HOME"] ?? join20(homedir2(), ".cleo");
13469
+ const dataRoot = process.env["CLEO_HOME"] ?? join19(homedir2(), ".cleo");
12741
13470
  const versionInfo = await parseVersionFile(dataRoot);
12742
13471
  const packageInfo = await getPackageInfo(versionInfo?.source);
12743
13472
  const channel = envChannel ?? detectFromInvocation(invocationName) ?? normalizeChannel(versionInfo?.version.includes("-beta") ? "beta" : void 0) ?? detectFromDataRoot(dataRoot) ?? normalizeChannel(versionInfo?.mode.startsWith("dev") ? "dev" : void 0) ?? "stable";
@@ -12952,10 +13681,10 @@ async function systemLog(projectRoot, filters) {
12952
13681
  }
12953
13682
  async function queryAuditLogSqlite(projectRoot, filters) {
12954
13683
  try {
12955
- const { join: join46 } = await import("node:path");
12956
- const { existsSync: existsSync45 } = await import("node:fs");
12957
- const dbPath = join46(projectRoot, ".cleo", "tasks.db");
12958
- if (!existsSync45(dbPath)) return null;
13684
+ const { join: join45 } = await import("node:path");
13685
+ const { existsSync: existsSync44 } = await import("node:fs");
13686
+ const dbPath = join45(projectRoot, ".cleo", "tasks.db");
13687
+ if (!existsSync44(dbPath)) return null;
12959
13688
  const { getDb: getDb2 } = await Promise.resolve().then(() => (init_sqlite(), sqlite_exports));
12960
13689
  const { auditLog: auditLog2 } = await Promise.resolve().then(() => (init_schema(), schema_exports));
12961
13690
  const { sql: sql4 } = await import("drizzle-orm");
@@ -13020,8 +13749,8 @@ async function queryAuditLogSqlite(projectRoot, filters) {
13020
13749
  }
13021
13750
  }
13022
13751
  function queryAuditLogJsonl(projectRoot, filters) {
13023
- const logPath = getDataPath2(projectRoot, "todo-log.jsonl");
13024
- const raw = readLogFileEntries2(logPath);
13752
+ const logPath = getDataPath(projectRoot, "todo-log.jsonl");
13753
+ const raw = readLogFileEntries(logPath);
13025
13754
  let entries = raw;
13026
13755
  if (filters?.operation) {
13027
13756
  entries = entries.filter((e) => e.operation === filters.operation);
@@ -13050,34 +13779,34 @@ function queryAuditLogJsonl(projectRoot, filters) {
13050
13779
  }
13051
13780
  function systemContext(projectRoot, params) {
13052
13781
  try {
13053
- const cleoDir = join21(projectRoot, ".cleo");
13782
+ const cleoDir = join20(projectRoot, ".cleo");
13054
13783
  let stateFile;
13055
13784
  if (params?.session) {
13056
- const sessionFile = join21(cleoDir, "context-states", `context-state-${params.session}.json`);
13057
- stateFile = existsSync21(sessionFile) ? sessionFile : join21(cleoDir, ".context-state.json");
13785
+ const sessionFile = join20(cleoDir, "context-states", `context-state-${params.session}.json`);
13786
+ stateFile = existsSync20(sessionFile) ? sessionFile : join20(cleoDir, ".context-state.json");
13058
13787
  } else {
13059
- const currentSessionPath = join21(cleoDir, ".current-session");
13060
- if (existsSync21(currentSessionPath)) {
13061
- const currentSession = readFileSync14(currentSessionPath, "utf-8").trim();
13788
+ const currentSessionPath = join20(cleoDir, ".current-session");
13789
+ if (existsSync20(currentSessionPath)) {
13790
+ const currentSession = readFileSync13(currentSessionPath, "utf-8").trim();
13062
13791
  if (currentSession) {
13063
- const sessionFile = join21(cleoDir, "context-states", `context-state-${currentSession}.json`);
13064
- stateFile = existsSync21(sessionFile) ? sessionFile : join21(cleoDir, ".context-state.json");
13792
+ const sessionFile = join20(cleoDir, "context-states", `context-state-${currentSession}.json`);
13793
+ stateFile = existsSync20(sessionFile) ? sessionFile : join20(cleoDir, ".context-state.json");
13065
13794
  } else {
13066
- stateFile = join21(cleoDir, ".context-state.json");
13795
+ stateFile = join20(cleoDir, ".context-state.json");
13067
13796
  }
13068
13797
  } else {
13069
- stateFile = join21(cleoDir, ".context-state.json");
13798
+ stateFile = join20(cleoDir, ".context-state.json");
13070
13799
  }
13071
13800
  }
13072
13801
  const sessions2 = [];
13073
- const statesDir = join21(cleoDir, "context-states");
13074
- if (existsSync21(statesDir)) {
13802
+ const statesDir = join20(cleoDir, "context-states");
13803
+ if (existsSync20(statesDir)) {
13075
13804
  for (const file of readdirSync5(statesDir)) {
13076
13805
  if (file.startsWith("context-state-") && file.endsWith(".json")) {
13077
13806
  try {
13078
- const state = JSON.parse(readFileSync14(join21(statesDir, file), "utf-8"));
13807
+ const state = JSON.parse(readFileSync13(join20(statesDir, file), "utf-8"));
13079
13808
  sessions2.push({
13080
- file: basename5(file),
13809
+ file: basename4(file),
13081
13810
  sessionId: state.sessionId ?? null,
13082
13811
  percentage: state.contextWindow?.percentage ?? 0,
13083
13812
  status: state.status ?? "unknown",
@@ -13088,10 +13817,10 @@ function systemContext(projectRoot, params) {
13088
13817
  }
13089
13818
  }
13090
13819
  }
13091
- const singletonFile = join21(cleoDir, ".context-state.json");
13092
- if (existsSync21(singletonFile)) {
13820
+ const singletonFile = join20(cleoDir, ".context-state.json");
13821
+ if (existsSync20(singletonFile)) {
13093
13822
  try {
13094
- const state = JSON.parse(readFileSync14(singletonFile, "utf-8"));
13823
+ const state = JSON.parse(readFileSync13(singletonFile, "utf-8"));
13095
13824
  sessions2.push({
13096
13825
  file: ".context-state.json",
13097
13826
  sessionId: state.sessionId ?? "global",
@@ -13102,7 +13831,7 @@ function systemContext(projectRoot, params) {
13102
13831
  } catch {
13103
13832
  }
13104
13833
  }
13105
- if (!existsSync21(stateFile)) {
13834
+ if (!existsSync20(stateFile)) {
13106
13835
  return {
13107
13836
  success: true,
13108
13837
  data: {
@@ -13118,7 +13847,7 @@ function systemContext(projectRoot, params) {
13118
13847
  };
13119
13848
  }
13120
13849
  try {
13121
- const state = JSON.parse(readFileSync14(stateFile, "utf-8"));
13850
+ const state = JSON.parse(readFileSync13(stateFile, "utf-8"));
13122
13851
  const timestamp = state.timestamp;
13123
13852
  const staleMs = state.staleAfterMs ?? 5e3;
13124
13853
  const percentage = state.contextWindow?.percentage ?? 0;
@@ -13313,8 +14042,8 @@ init_json();
13313
14042
  init_json();
13314
14043
  init_paths();
13315
14044
  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";
13316
- import { constants as fsConstants, existsSync as existsSync24, readFileSync as readFileSync17, readdirSync as readdirSync7 } from "node:fs";
13317
- import { join as join26, resolve as resolve4, basename as basename6, dirname as dirname8 } from "node:path";
14045
+ import { constants as fsConstants, existsSync as existsSync23, readFileSync as readFileSync16, readdirSync as readdirSync7 } from "node:fs";
14046
+ import { join as join25, resolve as resolve5, basename as basename5, dirname as dirname7 } from "node:path";
13318
14047
  import { fileURLToPath as fileURLToPath3 } from "node:url";
13319
14048
  import { createHash as createHash5 } from "node:crypto";
13320
14049
  import { homedir as homedir4 } from "node:os";
@@ -13330,7 +14059,7 @@ async function fileExists(path) {
13330
14059
  }
13331
14060
  }
13332
14061
  async function stripCLEOBlocks(filePath) {
13333
- if (!existsSync24(filePath)) return;
14062
+ if (!existsSync23(filePath)) return;
13334
14063
  const content = await readFile5(filePath, "utf8");
13335
14064
  const stripped = content.replace(
13336
14065
  /\n?<!-- CLEO:START -->[\s\S]*?<!-- CLEO:END -->\n?/g,
@@ -13366,31 +14095,67 @@ function createDefaultConfig() {
13366
14095
  }
13367
14096
  function getPackageRoot() {
13368
14097
  const thisFile = fileURLToPath3(import.meta.url);
13369
- return resolve4(dirname8(thisFile), "..", "..");
14098
+ return resolve5(dirname7(thisFile), "..", "..");
13370
14099
  }
13371
14100
  function getGitignoreContent() {
13372
14101
  try {
13373
14102
  const packageRoot = getPackageRoot();
13374
- const templatePath = join26(packageRoot, "templates", "cleo-gitignore");
13375
- if (existsSync24(templatePath)) {
13376
- return readFileSync17(templatePath, "utf-8");
14103
+ const templatePath = join25(packageRoot, "templates", "cleo-gitignore");
14104
+ if (existsSync23(templatePath)) {
14105
+ return readFileSync16(templatePath, "utf-8");
13377
14106
  }
13378
14107
  } catch {
13379
14108
  }
13380
14109
  return CLEO_GITIGNORE_FALLBACK;
13381
14110
  }
13382
- var CLEO_GITIGNORE_FALLBACK = `# CLEO Project Data - Selective Git Tracking
13383
- # Tracked via STATE_FILES: config.json, project-info.json, project-context.json, adrs/, agent-outputs/
13384
- # agent-outputs/ is intentionally tracked (research outputs, ADR evidence)
13385
- logs/
13386
- audit-log*.json
13387
- tasks.db
13388
- tasks.db-shm
13389
- tasks.db-wal
13390
- .git/
14111
+ var CLEO_GITIGNORE_FALLBACK = `# .cleo/.gitignore \u2014 Deny-by-default for CLEO project data
14112
+ # Ignore everything, then explicitly allow only tracked files.
14113
+
14114
+ # Step 1: Ignore everything
14115
+ *
14116
+
14117
+ # Allow list
14118
+ !.gitignore
14119
+ !config.json
14120
+ !project-context.json
14121
+ !project-info.json
14122
+ !setup-otel.sh
14123
+ !DATA-SAFETY-IMPLEMENTATION-SUMMARY.md
14124
+ !schemas/
14125
+ !schemas/**
14126
+ !templates/
14127
+ !templates/**
14128
+ !adrs/
14129
+ !adrs/**
14130
+ !rcasd/
14131
+ !rcasd/**
14132
+ !agent-outputs/
14133
+ !agent-outputs/**
14134
+
14135
+ # Explicit deny safety net
14136
+ *.db
14137
+ *.db-shm
14138
+ *.db-wal
14139
+ *.db-journal
14140
+ log.json
14141
+ tasks-log.jsonl
14142
+ todo-log.jsonl
14143
+ bypass-log.json
14144
+ qa-log.json
14145
+ .deps-cache/
14146
+ .context-alert-state.json
14147
+ .context-state*.json
14148
+ context-states/
14149
+ .git-checkpoint-state
14150
+ .migration-state.json
14151
+ migrations.json
14152
+ sync/
14153
+ metrics/
14154
+ .backups/
14155
+ backups/
13391
14156
  `;
13392
14157
  async function removeCleoFromRootGitignore(projectRoot) {
13393
- const rootGitignorePath = join26(projectRoot, ".gitignore");
14158
+ const rootGitignorePath = join25(projectRoot, ".gitignore");
13394
14159
  if (!await fileExists(rootGitignorePath)) {
13395
14160
  return false;
13396
14161
  }
@@ -13411,8 +14176,8 @@ function generateProjectHash2(projectPath) {
13411
14176
  }
13412
14177
  function getCleoVersion() {
13413
14178
  try {
13414
- const pkgPath = join26(getPackageRoot(), "package.json");
13415
- const pkg = JSON.parse(readFileSync17(pkgPath, "utf-8"));
14179
+ const pkgPath = join25(getPackageRoot(), "package.json");
14180
+ const pkg = JSON.parse(readFileSync16(pkgPath, "utf-8"));
13416
14181
  return pkg.version ?? "0.0.0";
13417
14182
  } catch {
13418
14183
  return "0.0.0";
@@ -13420,9 +14185,9 @@ function getCleoVersion() {
13420
14185
  }
13421
14186
  function getInjectionTemplateContent() {
13422
14187
  const packageRoot = getPackageRoot();
13423
- const packageTemplatePath = join26(packageRoot, "templates", "CLEO-INJECTION.md");
13424
- if (existsSync24(packageTemplatePath)) {
13425
- return readFileSync17(packageTemplatePath, "utf-8");
14188
+ const packageTemplatePath = join25(packageRoot, "templates", "CLEO-INJECTION.md");
14189
+ if (existsSync23(packageTemplatePath)) {
14190
+ return readFileSync16(packageTemplatePath, "utf-8");
13426
14191
  }
13427
14192
  return null;
13428
14193
  }
@@ -13435,27 +14200,27 @@ async function initCoreFiles(cleoDir, _projectName, force, created, skipped) {
13435
14200
  await saveJson(configPath, createDefaultConfig());
13436
14201
  created.push("config.json");
13437
14202
  }
13438
- const legacySequencePath = join26(cleoDir, ".sequence");
14203
+ const legacySequencePath = join25(cleoDir, ".sequence");
13439
14204
  try {
13440
14205
  await unlink3(legacySequencePath);
13441
14206
  } catch {
13442
14207
  }
13443
- const legacySequenceJsonPath = join26(cleoDir, ".sequence.json");
14208
+ const legacySequenceJsonPath = join25(cleoDir, ".sequence.json");
13444
14209
  try {
13445
14210
  await unlink3(legacySequenceJsonPath);
13446
14211
  } catch {
13447
14212
  }
13448
- const backupDir = join26(cleoDir, "backups");
13449
- await mkdir5(join26(backupDir, "operational"), { recursive: true });
13450
- await mkdir5(join26(backupDir, "safety"), { recursive: true });
14213
+ const backupDir = join25(cleoDir, "backups");
14214
+ await mkdir5(join25(backupDir, "operational"), { recursive: true });
14215
+ await mkdir5(join25(backupDir, "safety"), { recursive: true });
13451
14216
  try {
13452
14217
  const { getDb: getDb2 } = await Promise.resolve().then(() => (init_sqlite(), sqlite_exports));
13453
- await getDb2(join26(cleoDir, ".."));
14218
+ await getDb2(join25(cleoDir, ".."));
13454
14219
  created.push("tasks.db");
13455
14220
  } catch (err) {
13456
14221
  created.push(`tasks.db (deferred: ${err instanceof Error ? err.message : String(err)})`);
13457
14222
  }
13458
- const gitignorePath = join26(cleoDir, ".gitignore");
14223
+ const gitignorePath = join25(cleoDir, ".gitignore");
13459
14224
  if (await fileExists(gitignorePath) && !force) {
13460
14225
  skipped.push(".gitignore");
13461
14226
  } else {
@@ -13464,8 +14229,8 @@ async function initCoreFiles(cleoDir, _projectName, force, created, skipped) {
13464
14229
  }
13465
14230
  }
13466
14231
  async function initCleoGitRepo(cleoDir, created, warnings) {
13467
- const cleoGitDir = join26(cleoDir, ".git");
13468
- if (existsSync24(cleoGitDir)) {
14232
+ const cleoGitDir = join25(cleoDir, ".git");
14233
+ if (existsSync23(cleoGitDir)) {
13469
14234
  return;
13470
14235
  }
13471
14236
  const gitEnv = {
@@ -13483,11 +14248,11 @@ async function initCleoGitRepo(cleoDir, created, warnings) {
13483
14248
  }
13484
14249
  }
13485
14250
  async function initSchemas(cleoDir, force, created, warnings) {
13486
- const schemasDir = join26(cleoDir, "schemas");
14251
+ const schemasDir = join25(cleoDir, "schemas");
13487
14252
  await mkdir5(schemasDir, { recursive: true });
13488
14253
  const packageRoot = getPackageRoot();
13489
- const sourceSchemaDir = join26(packageRoot, "schemas");
13490
- if (!existsSync24(sourceSchemaDir)) {
14254
+ const sourceSchemaDir = join25(packageRoot, "schemas");
14255
+ if (!existsSync23(sourceSchemaDir)) {
13491
14256
  warnings.push("schemas/ directory not found in package root, skipping schema installation");
13492
14257
  return;
13493
14258
  }
@@ -13498,9 +14263,9 @@ async function initSchemas(cleoDir, force, created, warnings) {
13498
14263
  ];
13499
14264
  let copiedCount = 0;
13500
14265
  for (const schemaFile of coreSchemas) {
13501
- const sourcePath = join26(sourceSchemaDir, schemaFile);
13502
- const destPath = join26(schemasDir, schemaFile);
13503
- if (!existsSync24(sourcePath)) {
14266
+ const sourcePath = join25(sourceSchemaDir, schemaFile);
14267
+ const destPath = join25(schemasDir, schemaFile);
14268
+ if (!existsSync23(sourcePath)) {
13504
14269
  continue;
13505
14270
  }
13506
14271
  if (await fileExists(destPath) && !force) {
@@ -13518,27 +14283,27 @@ async function initSchemas(cleoDir, force, created, warnings) {
13518
14283
  }
13519
14284
  }
13520
14285
  async function initGitHooks(projRoot, force, created, warnings) {
13521
- const gitHooksDir = join26(projRoot, ".git", "hooks");
13522
- if (!existsSync24(join26(projRoot, ".git"))) {
14286
+ const gitHooksDir = join25(projRoot, ".git", "hooks");
14287
+ if (!existsSync23(join25(projRoot, ".git"))) {
13523
14288
  warnings.push("No .git/ directory found, skipping git hook installation");
13524
14289
  return;
13525
14290
  }
13526
14291
  await mkdir5(gitHooksDir, { recursive: true });
13527
14292
  const packageRoot = getPackageRoot();
13528
- const sourceDir = join26(packageRoot, "templates", "git-hooks");
13529
- if (!existsSync24(sourceDir)) {
14293
+ const sourceDir = join25(packageRoot, "templates", "git-hooks");
14294
+ if (!existsSync23(sourceDir)) {
13530
14295
  warnings.push("templates/git-hooks/ not found in package root, skipping git hook installation");
13531
14296
  return;
13532
14297
  }
13533
14298
  const hooks = ["commit-msg", "pre-commit"];
13534
14299
  let installedCount = 0;
13535
14300
  for (const hook of hooks) {
13536
- const sourcePath = join26(sourceDir, hook);
13537
- const destPath = join26(gitHooksDir, hook);
13538
- if (!existsSync24(sourcePath)) {
14301
+ const sourcePath = join25(sourceDir, hook);
14302
+ const destPath = join25(gitHooksDir, hook);
14303
+ if (!existsSync23(sourcePath)) {
13539
14304
  continue;
13540
14305
  }
13541
- if (existsSync24(destPath) && !force) {
14306
+ if (existsSync23(destPath) && !force) {
13542
14307
  continue;
13543
14308
  }
13544
14309
  try {
@@ -13554,7 +14319,7 @@ async function initGitHooks(projRoot, force, created, warnings) {
13554
14319
  }
13555
14320
  }
13556
14321
  async function initProjectInfo(cleoDir, projectRoot, force, created, skipped) {
13557
- const projectInfoPath = join26(cleoDir, "project-info.json");
14322
+ const projectInfoPath = join25(cleoDir, "project-info.json");
13558
14323
  if (await fileExists(projectInfoPath) && !force) {
13559
14324
  skipped.push("project-info.json");
13560
14325
  return;
@@ -13603,40 +14368,40 @@ async function initInjection(projectRoot, created, warnings) {
13603
14368
  return;
13604
14369
  }
13605
14370
  for (const provider of providers) {
13606
- const instructFile = join26(projectRoot, provider.pathProject, provider.instructFile);
14371
+ const instructFile = join25(projectRoot, provider.pathProject, provider.instructFile);
13607
14372
  await stripCLEOBlocks(instructFile);
13608
14373
  }
13609
- await stripCLEOBlocks(join26(projectRoot, "AGENTS.md"));
14374
+ await stripCLEOBlocks(join25(projectRoot, "AGENTS.md"));
13610
14375
  const injectionContent = buildInjectionContent2({ references: ["@AGENTS.md"] });
13611
14376
  const results = await injectAll2(providers, projectRoot, "project", injectionContent);
13612
14377
  const injected = [];
13613
14378
  for (const [filePath, action] of results) {
13614
- const fileName = basename6(filePath);
14379
+ const fileName = basename5(filePath);
13615
14380
  injected.push(`${fileName} (${action})`);
13616
14381
  }
13617
14382
  if (injected.length > 0) {
13618
14383
  created.push(`injection: ${injected.join(", ")}`);
13619
14384
  }
13620
- const agentsMdPath = join26(projectRoot, "AGENTS.md");
14385
+ const agentsMdPath = join25(projectRoot, "AGENTS.md");
13621
14386
  const agentsMdLines = ["@~/.cleo/templates/CLEO-INJECTION.md"];
13622
- const projectContextPath = join26(projectRoot, ".cleo", "project-context.json");
13623
- if (existsSync24(projectContextPath)) {
14387
+ const projectContextPath = join25(projectRoot, ".cleo", "project-context.json");
14388
+ if (existsSync23(projectContextPath)) {
13624
14389
  agentsMdLines.push("@.cleo/project-context.json");
13625
14390
  }
13626
14391
  const agentsAction = await inject(agentsMdPath, agentsMdLines.join("\n"));
13627
14392
  created.push(`AGENTS.md CLEO content (${agentsAction})`);
13628
14393
  const content = getInjectionTemplateContent();
13629
14394
  if (content) {
13630
- const globalTemplatesDir = join26(getCleoHome(), "templates");
14395
+ const globalTemplatesDir = join25(getCleoHome(), "templates");
13631
14396
  await mkdir5(globalTemplatesDir, { recursive: true });
13632
- const globalPath = join26(globalTemplatesDir, "CLEO-INJECTION.md");
13633
- if (!existsSync24(globalPath)) {
14397
+ const globalPath = join25(globalTemplatesDir, "CLEO-INJECTION.md");
14398
+ if (!existsSync23(globalPath)) {
13634
14399
  await writeFile3(globalPath, content);
13635
14400
  }
13636
14401
  }
13637
14402
  try {
13638
- const globalAgentsDir = join26(homedir4(), ".agents");
13639
- const globalAgentsMd = join26(globalAgentsDir, "AGENTS.md");
14403
+ const globalAgentsDir = join25(homedir4(), ".agents");
14404
+ const globalAgentsMd = join25(globalAgentsDir, "AGENTS.md");
13640
14405
  await mkdir5(globalAgentsDir, { recursive: true });
13641
14406
  await inject(globalAgentsMd, "@~/.cleo/templates/CLEO-INJECTION.md");
13642
14407
  } catch {
@@ -13647,13 +14412,13 @@ async function initInjection(projectRoot, created, warnings) {
13647
14412
  }
13648
14413
  async function initAgentDefinition(created, warnings) {
13649
14414
  const packageRoot = getPackageRoot();
13650
- const agentSourceDir = join26(packageRoot, "agents", "cleo-subagent");
13651
- if (!existsSync24(agentSourceDir)) {
14415
+ const agentSourceDir = join25(packageRoot, "agents", "cleo-subagent");
14416
+ if (!existsSync23(agentSourceDir)) {
13652
14417
  warnings.push("agents/cleo-subagent/ not found in package, skipping agent definition install");
13653
14418
  return;
13654
14419
  }
13655
- const globalAgentsDir = join26(homedir4(), ".agents", "agents", "cleo-subagent");
13656
- await mkdir5(dirname8(globalAgentsDir), { recursive: true });
14420
+ const globalAgentsDir = join25(homedir4(), ".agents", "agents", "cleo-subagent");
14421
+ await mkdir5(dirname7(globalAgentsDir), { recursive: true });
13657
14422
  try {
13658
14423
  try {
13659
14424
  const stat2 = await lstat(globalAgentsDir);
@@ -13669,7 +14434,7 @@ async function initAgentDefinition(created, warnings) {
13669
14434
  await mkdir5(globalAgentsDir, { recursive: true });
13670
14435
  const files = readdirSync7(agentSourceDir);
13671
14436
  for (const file of files) {
13672
- await copyFile2(join26(agentSourceDir, file), join26(globalAgentsDir, file));
14437
+ await copyFile2(join25(agentSourceDir, file), join25(globalAgentsDir, file));
13673
14438
  }
13674
14439
  created.push("agent: cleo-subagent (copied)");
13675
14440
  } catch (copyErr) {
@@ -13721,12 +14486,12 @@ async function initCoreSkills(created, warnings) {
13721
14486
  let ctSkillsRoot = null;
13722
14487
  try {
13723
14488
  const packageRoot = getPackageRoot();
13724
- const bundledPath = join26(packageRoot, "packages", "ct-skills");
13725
- if (existsSync24(join26(bundledPath, "skills.json"))) {
14489
+ const bundledPath = join25(packageRoot, "packages", "ct-skills");
14490
+ if (existsSync23(join25(bundledPath, "skills.json"))) {
13726
14491
  ctSkillsRoot = bundledPath;
13727
14492
  } else {
13728
- const ctSkillsPath = join26(packageRoot, "node_modules", "@cleocode", "ct-skills");
13729
- if (existsSync24(join26(ctSkillsPath, "skills.json"))) {
14493
+ const ctSkillsPath = join25(packageRoot, "node_modules", "@cleocode", "ct-skills");
14494
+ if (existsSync23(join25(ctSkillsPath, "skills.json"))) {
13730
14495
  ctSkillsRoot = ctSkillsPath;
13731
14496
  }
13732
14497
  }
@@ -13741,14 +14506,14 @@ async function initCoreSkills(created, warnings) {
13741
14506
  } catch {
13742
14507
  warnings.push("Failed to register skill library with CAAMP");
13743
14508
  }
13744
- const catalogPath = join26(ctSkillsRoot, "skills.json");
13745
- const catalog2 = JSON.parse(readFileSync17(catalogPath, "utf-8"));
14509
+ const catalogPath = join25(ctSkillsRoot, "skills.json");
14510
+ const catalog2 = JSON.parse(readFileSync16(catalogPath, "utf-8"));
13746
14511
  const skills = catalog2.skills ?? [];
13747
14512
  const coreSkills = skills.filter((s) => s.tier <= 2);
13748
14513
  const installed = [];
13749
14514
  for (const skill of coreSkills) {
13750
- const skillSourceDir = dirname8(join26(ctSkillsRoot, skill.path));
13751
- if (!existsSync24(skillSourceDir)) {
14515
+ const skillSourceDir = dirname7(join25(ctSkillsRoot, skill.path));
14516
+ if (!existsSync23(skillSourceDir)) {
13752
14517
  continue;
13753
14518
  }
13754
14519
  try {
@@ -13783,7 +14548,7 @@ async function initProjectDetect(cleoDir, projectRoot, created, warnings) {
13783
14548
  try {
13784
14549
  const { detectProjectType: detectProjectType2 } = await Promise.resolve().then(() => (init_project_detect(), project_detect_exports));
13785
14550
  const info = detectProjectType2(projectRoot);
13786
- const contextPath = join26(cleoDir, "project-context.json");
14551
+ const contextPath = join25(cleoDir, "project-context.json");
13787
14552
  const context = {
13788
14553
  ...info,
13789
14554
  detectedAt: (/* @__PURE__ */ new Date()).toISOString()
@@ -13856,8 +14621,8 @@ async function initProject(opts = {}) {
13856
14621
  async function getVersion(projectRoot) {
13857
14622
  const root = projectRoot ?? getProjectRoot();
13858
14623
  const versionPaths = [
13859
- join26(root, "VERSION"),
13860
- join26(root, "..", "VERSION")
14624
+ join25(root, "VERSION"),
14625
+ join25(root, "..", "VERSION")
13861
14626
  ];
13862
14627
  for (const versionPath of versionPaths) {
13863
14628
  try {
@@ -13869,7 +14634,7 @@ async function getVersion(projectRoot) {
13869
14634
  } catch {
13870
14635
  }
13871
14636
  }
13872
- const pkg = await readJson(join26(root, "package.json"));
14637
+ const pkg = await readJson(join25(root, "package.json"));
13873
14638
  if (pkg?.version) {
13874
14639
  return { version: pkg.version };
13875
14640
  }
@@ -13912,8 +14677,8 @@ async function getVersion2(projectRoot) {
13912
14677
  init_paths();
13913
14678
  init_file_utils();
13914
14679
  init_schema();
13915
- import { existsSync as existsSync25, mkdirSync as mkdirSync9, readdirSync as readdirSync8 } from "node:fs";
13916
- import { join as join27 } from "node:path";
14680
+ import { existsSync as existsSync24, mkdirSync as mkdirSync8, readdirSync as readdirSync8 } from "node:fs";
14681
+ import { join as join26 } from "node:path";
13917
14682
 
13918
14683
  // src/core/lifecycle/stages.ts
13919
14684
  var PIPELINE_STAGES = [
@@ -14059,7 +14824,7 @@ var DEFAULT_LIFECYCLE_DATA_DIR = "rcasd";
14059
14824
  function resolveLifecycleDir(epicId, cwd) {
14060
14825
  const cleoDir = getCleoDirAbsolute(cwd);
14061
14826
  for (const dirName of LIFECYCLE_DATA_DIRS) {
14062
- if (existsSync25(join27(cleoDir, dirName, epicId))) {
14827
+ if (existsSync24(join26(cleoDir, dirName, epicId))) {
14063
14828
  return dirName;
14064
14829
  }
14065
14830
  }
@@ -14068,18 +14833,18 @@ function resolveLifecycleDir(epicId, cwd) {
14068
14833
  function getRcsdDir(epicId, cwd) {
14069
14834
  const cleoDir = getCleoDirAbsolute(cwd);
14070
14835
  const dirName = resolveLifecycleDir(epicId, cwd);
14071
- return join27(cleoDir, dirName, epicId);
14836
+ return join26(cleoDir, dirName, epicId);
14072
14837
  }
14073
14838
  function getRcsdManifestPath(epicId, cwd) {
14074
- return join27(getRcsdDir(epicId, cwd), "_manifest.json");
14839
+ return join26(getRcsdDir(epicId, cwd), "_manifest.json");
14075
14840
  }
14076
14841
  function readManifestSync(epicId, cwd) {
14077
- return readJsonFile2(getRcsdManifestPath(epicId, cwd));
14842
+ return readJsonFile(getRcsdManifestPath(epicId, cwd));
14078
14843
  }
14079
14844
  function writeManifestSync(epicId, manifest, cwd) {
14080
14845
  const dir = getRcsdDir(epicId, cwd);
14081
- if (!existsSync25(dir)) {
14082
- mkdirSync9(dir, { recursive: true });
14846
+ if (!existsSync24(dir)) {
14847
+ mkdirSync8(dir, { recursive: true });
14083
14848
  }
14084
14849
  writeJsonFileAtomic(getRcsdManifestPath(epicId, cwd), manifest);
14085
14850
  }
@@ -14533,16 +15298,16 @@ init_platform();
14533
15298
  // src/core/validation/validate-ops.ts
14534
15299
  init_paths();
14535
15300
  init_data_accessor();
14536
- import { readFileSync as readFileSync19, existsSync as existsSync27, appendFileSync as appendFileSync3, mkdirSync as mkdirSync10 } from "node:fs";
15301
+ import { readFileSync as readFileSync18, existsSync as existsSync26, appendFileSync as appendFileSync3, mkdirSync as mkdirSync9 } from "node:fs";
14537
15302
  import { execFileSync as execFileSync2 } from "node:child_process";
14538
- import { join as join29, dirname as dirname9, resolve as resolve5 } from "node:path";
15303
+ import { join as join28, dirname as dirname8, resolve as resolve6 } from "node:path";
14539
15304
 
14540
- // src/mcp/engine/schema-validator.ts
15305
+ // src/core/validation/schema-validator.ts
14541
15306
  init_validation_schemas();
14542
15307
  import AjvModule2 from "ajv";
14543
15308
  import addFormatsModule2 from "ajv-formats";
14544
- import { readFileSync as readFileSync18, existsSync as existsSync26 } from "fs";
14545
- import { join as join28 } from "path";
15309
+ import { readFileSync as readFileSync17, existsSync as existsSync25 } from "fs";
15310
+ import { join as join27 } from "path";
14546
15311
  var Ajv2 = AjvModule2.default || AjvModule2;
14547
15312
  var addFormats2 = addFormatsModule2.default || addFormatsModule2;
14548
15313
  var schemaCache2 = /* @__PURE__ */ new Map();
@@ -14566,14 +15331,14 @@ function resolveSchemaPath2(schemaType) {
14566
15331
  const filename = `${schemaType}.schema.json`;
14567
15332
  const projectRoot = process.env.CLEO_ROOT || process.cwd();
14568
15333
  const paths = [
14569
- join28(projectRoot, "schemas", filename),
14570
- join28(__dirname, "..", "..", "..", "schemas", filename),
14571
- // relative from dist/mcp/engine/
14572
- join28(__dirname, "..", "..", "schemas", filename)
14573
- // relative from dist/mcp/
15334
+ join27(projectRoot, "schemas", filename),
15335
+ join27(__dirname, "..", "..", "..", "schemas", filename),
15336
+ // relative from dist/core/validation/
15337
+ join27(__dirname, "..", "..", "schemas", filename)
15338
+ // relative from dist/core/
14574
15339
  ];
14575
15340
  for (const p of paths) {
14576
- if (existsSync26(p)) {
15341
+ if (existsSync25(p)) {
14577
15342
  return p;
14578
15343
  }
14579
15344
  }
@@ -14589,7 +15354,7 @@ function getValidator(schemaType) {
14589
15354
  return null;
14590
15355
  }
14591
15356
  try {
14592
- const schemaContent = readFileSync18(schemaPath, "utf-8");
15357
+ const schemaContent = readFileSync17(schemaPath, "utf-8");
14593
15358
  const schema = JSON.parse(schemaContent);
14594
15359
  const ajv = getAjv2();
14595
15360
  const validate = ajv.compile(schema);
@@ -14627,7 +15392,7 @@ function validateSchema(schemaType, data) {
14627
15392
  return { valid: false, errors };
14628
15393
  }
14629
15394
 
14630
- // src/mcp/engine/validation-rules.ts
15395
+ // src/core/validation/validation-rules.ts
14631
15396
  function validateTitleDescription(title, description) {
14632
15397
  const violations = [];
14633
15398
  if (!title || title.trim().length === 0) {
@@ -14761,35 +15526,6 @@ function hasErrors(violations) {
14761
15526
  return violations.some((v) => v.severity === "error");
14762
15527
  }
14763
15528
 
14764
- // src/core/tasks/dependency-check.ts
14765
- function detectCircularDeps(taskId, tasks2) {
14766
- const taskMap = new Map(tasks2.map((t) => [t.id, t]));
14767
- const visited = /* @__PURE__ */ new Set();
14768
- const recursionStack = /* @__PURE__ */ new Set();
14769
- const path = [];
14770
- function dfs(id) {
14771
- visited.add(id);
14772
- recursionStack.add(id);
14773
- path.push(id);
14774
- const task = taskMap.get(id);
14775
- if (task?.depends) {
14776
- for (const depId of task.depends) {
14777
- if (!visited.has(depId)) {
14778
- const cycle = dfs(depId);
14779
- if (cycle.length > 0) return cycle;
14780
- } else if (recursionStack.has(depId)) {
14781
- const cycleStart = path.indexOf(depId);
14782
- return [...path.slice(cycleStart), depId];
14783
- }
14784
- }
14785
- }
14786
- path.pop();
14787
- recursionStack.delete(id);
14788
- return [];
14789
- }
14790
- return dfs(taskId);
14791
- }
14792
-
14793
15529
  // src/core/validation/validate-ops.ts
14794
15530
  init_json();
14795
15531
  init_status_registry();
@@ -14798,9 +15534,9 @@ init_status_registry();
14798
15534
  import { isNull as isNull2, sql as sql3 } from "drizzle-orm";
14799
15535
 
14800
15536
  // src/core/validation/validate-ops.ts
14801
- function readJsonFile3(filePath) {
15537
+ function readJsonFile2(filePath) {
14802
15538
  try {
14803
- const raw = readFileSync19(filePath, "utf-8");
15539
+ const raw = readFileSync18(filePath, "utf-8");
14804
15540
  return JSON.parse(raw);
14805
15541
  } catch {
14806
15542
  return null;
@@ -14815,11 +15551,11 @@ async function coreValidateSchema(type, data, projectRoot) {
14815
15551
  throw new Error(`Unknown schema type: ${type}. Valid types: ${validTypes.join(", ")}`);
14816
15552
  }
14817
15553
  if (type === "config") {
14818
- const filePath = join29(projectRoot, ".cleo", "config.json");
14819
- if (!existsSync27(filePath)) {
15554
+ const filePath = join28(projectRoot, ".cleo", "config.json");
15555
+ if (!existsSync26(filePath)) {
14820
15556
  throw new Error("File not found: .cleo/config.json");
14821
15557
  }
14822
- const configData = data ?? readJsonFile3(filePath);
15558
+ const configData = data ?? readJsonFile2(filePath);
14823
15559
  const result = validateSchema("config", configData);
14824
15560
  return { type, valid: result.valid, errors: result.errors, errorCount: result.errors.length };
14825
15561
  }
@@ -14953,7 +15689,7 @@ async function coreValidateProtocol(taskId, protocolType, projectRoot) {
14953
15689
  }
14954
15690
  function coreValidateManifest(projectRoot) {
14955
15691
  const manifestPath = getManifestPath(projectRoot);
14956
- if (!existsSync27(manifestPath)) {
15692
+ if (!existsSync26(manifestPath)) {
14957
15693
  return {
14958
15694
  valid: true,
14959
15695
  totalEntries: 0,
@@ -14963,7 +15699,7 @@ function coreValidateManifest(projectRoot) {
14963
15699
  message: "No manifest file found"
14964
15700
  };
14965
15701
  }
14966
- const content = readFileSync19(manifestPath, "utf-8");
15702
+ const content = readFileSync18(manifestPath, "utf-8");
14967
15703
  const lines = content.split("\n").filter((l) => l.trim());
14968
15704
  let validCount = 0;
14969
15705
  let invalidCount = 0;
@@ -15003,11 +15739,11 @@ function coreValidateOutput(filePath, taskId, projectRoot) {
15003
15739
  if (!filePath) {
15004
15740
  throw new Error("filePath is required");
15005
15741
  }
15006
- const fullPath = resolve5(projectRoot, filePath);
15007
- if (!existsSync27(fullPath)) {
15742
+ const fullPath = resolve6(projectRoot, filePath);
15743
+ if (!existsSync26(fullPath)) {
15008
15744
  throw new Error(`Output file not found: ${filePath}`);
15009
15745
  }
15010
- const content = readFileSync19(fullPath, "utf-8");
15746
+ const content = readFileSync18(fullPath, "utf-8");
15011
15747
  const issues = [];
15012
15748
  if (!content.includes("# ")) {
15013
15749
  issues.push({ code: "O_MISSING_TITLE", message: "Output file should have a markdown title", severity: "warning" });
@@ -15027,11 +15763,11 @@ function coreValidateOutput(filePath, taskId, projectRoot) {
15027
15763
  };
15028
15764
  }
15029
15765
  function parseComplianceEntries(projectRoot) {
15030
- const compliancePath = join29(projectRoot, ".cleo", "metrics", "COMPLIANCE.jsonl");
15031
- if (!existsSync27(compliancePath)) {
15766
+ const compliancePath = join28(projectRoot, ".cleo", "metrics", "COMPLIANCE.jsonl");
15767
+ if (!existsSync26(compliancePath)) {
15032
15768
  return [];
15033
15769
  }
15034
- const content = readFileSync19(compliancePath, "utf-8");
15770
+ const content = readFileSync18(compliancePath, "utf-8");
15035
15771
  const entries = [];
15036
15772
  for (const line of content.split("\n")) {
15037
15773
  const trimmed = line.trim();
@@ -15091,10 +15827,10 @@ function coreComplianceRecord(taskId, result, protocol, violations, projectRoot)
15091
15827
  if (!validResults.includes(result)) {
15092
15828
  throw new Error(`Invalid result: ${result}. Valid: ${validResults.join(", ")}`);
15093
15829
  }
15094
- const compliancePath = join29(projectRoot, ".cleo", "metrics", "COMPLIANCE.jsonl");
15095
- const dir = dirname9(compliancePath);
15096
- if (!existsSync27(dir)) {
15097
- mkdirSync10(dir, { recursive: true });
15830
+ const compliancePath = join28(projectRoot, ".cleo", "metrics", "COMPLIANCE.jsonl");
15831
+ const dir = dirname8(compliancePath);
15832
+ if (!existsSync26(dir)) {
15833
+ mkdirSync9(dir, { recursive: true });
15098
15834
  }
15099
15835
  const entry = {
15100
15836
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
@@ -15113,10 +15849,10 @@ function coreComplianceRecord(taskId, result, protocol, violations, projectRoot)
15113
15849
  };
15114
15850
  }
15115
15851
  function coreTestStatus(projectRoot) {
15116
- const testDir = join29(projectRoot, "tests");
15117
- const mcpTestDir = join29(projectRoot, "src", "mcp", "__tests__");
15118
- const hasBatsTests = existsSync27(testDir);
15119
- const hasMcpTests = existsSync27(mcpTestDir);
15852
+ const testDir = join28(projectRoot, "tests");
15853
+ const mcpTestDir = join28(projectRoot, "src", "mcp", "__tests__");
15854
+ const hasBatsTests = existsSync26(testDir);
15855
+ const hasMcpTests = existsSync26(mcpTestDir);
15120
15856
  return {
15121
15857
  batsTests: {
15122
15858
  available: hasBatsTests,
@@ -15258,8 +15994,8 @@ async function coreCoherenceCheck(projectRoot) {
15258
15994
  };
15259
15995
  }
15260
15996
  function coreTestRun(params, projectRoot) {
15261
- const hasVitest = existsSync27(join29(projectRoot, "node_modules", ".bin", "vitest"));
15262
- const hasBats = existsSync27(join29(projectRoot, "tests"));
15997
+ const hasVitest = existsSync26(join28(projectRoot, "node_modules", ".bin", "vitest"));
15998
+ const hasBats = existsSync26(join28(projectRoot, "tests"));
15263
15999
  if (!hasVitest && !hasBats) {
15264
16000
  return {
15265
16001
  ran: false,
@@ -15305,14 +16041,14 @@ function coreTestRun(params, projectRoot) {
15305
16041
  }
15306
16042
  }
15307
16043
  function coreTestCoverage(projectRoot) {
15308
- const coveragePath = join29(projectRoot, "coverage", "coverage-summary.json");
15309
- if (!existsSync27(coveragePath)) {
16044
+ const coveragePath = join28(projectRoot, "coverage", "coverage-summary.json");
16045
+ if (!existsSync26(coveragePath)) {
15310
16046
  return {
15311
16047
  available: false,
15312
16048
  message: "No coverage data found. Run tests with coverage first."
15313
16049
  };
15314
16050
  }
15315
- const coverageData = readJsonFile3(coveragePath);
16051
+ const coverageData = readJsonFile2(coveragePath);
15316
16052
  if (!coverageData) {
15317
16053
  throw new Error("Failed to read coverage data");
15318
16054
  }
@@ -15444,7 +16180,7 @@ function validateTestCoverage(projectRoot) {
15444
16180
  init_platform();
15445
16181
  init_paths();
15446
16182
  init_data_accessor();
15447
- import { readFileSync as readFileSync22, existsSync as existsSync30 } from "node:fs";
16183
+ import { readFileSync as readFileSync21, existsSync as existsSync29 } from "node:fs";
15448
16184
 
15449
16185
  // src/core/orchestration/index.ts
15450
16186
  init_json();
@@ -15573,7 +16309,7 @@ async function analyzeEpic(epicId, cwd, accessor) {
15573
16309
  completedTasks
15574
16310
  };
15575
16311
  }
15576
- async function getReadyTasks(epicId, cwd, accessor) {
16312
+ async function getReadyTasks2(epicId, cwd, accessor) {
15577
16313
  const data = accessor ? await accessor.loadTaskFile() : await readJsonRequired(getTaskPath(cwd));
15578
16314
  const childTasks = data.tasks.filter((t) => t.parentId === epicId);
15579
16315
  const completedIds = new Set(
@@ -15593,7 +16329,7 @@ async function getReadyTasks(epicId, cwd, accessor) {
15593
16329
  });
15594
16330
  }
15595
16331
  async function getNextTask(epicId, cwd, accessor) {
15596
- const readyTasks = await getReadyTasks(epicId, cwd, accessor);
16332
+ const readyTasks = await getReadyTasks2(epicId, cwd, accessor);
15597
16333
  const ready = readyTasks.filter((t) => t.ready);
15598
16334
  if (ready.length === 0) return null;
15599
16335
  return ready[0];
@@ -15954,27 +16690,27 @@ async function getUnblockOpportunities(cwd, accessor) {
15954
16690
  // src/core/orchestration/parallel.ts
15955
16691
  init_errors();
15956
16692
  init_exit_codes();
15957
- import { readFileSync as readFileSync20, writeFileSync as writeFileSync7, existsSync as existsSync28, mkdirSync as mkdirSync11 } from "node:fs";
15958
- import { join as join30, dirname as dirname10 } from "node:path";
16693
+ import { readFileSync as readFileSync19, writeFileSync as writeFileSync6, existsSync as existsSync27, mkdirSync as mkdirSync10 } from "node:fs";
16694
+ import { join as join29, dirname as dirname9 } from "node:path";
15959
16695
  init_json();
15960
16696
  init_paths();
15961
16697
  function getParallelStatePath(projectRoot) {
15962
- return join30(projectRoot, ".cleo", "parallel-state.json");
16698
+ return join29(projectRoot, ".cleo", "parallel-state.json");
15963
16699
  }
15964
16700
  function readParallelState(projectRoot) {
15965
16701
  const statePath = getParallelStatePath(projectRoot);
15966
- if (!existsSync28(statePath)) return { active: false };
16702
+ if (!existsSync27(statePath)) return { active: false };
15967
16703
  try {
15968
- return JSON.parse(readFileSync20(statePath, "utf-8"));
16704
+ return JSON.parse(readFileSync19(statePath, "utf-8"));
15969
16705
  } catch {
15970
16706
  return { active: false };
15971
16707
  }
15972
16708
  }
15973
16709
  function writeParallelState(state, projectRoot) {
15974
16710
  const statePath = getParallelStatePath(projectRoot);
15975
- const dir = dirname10(statePath);
15976
- if (!existsSync28(dir)) mkdirSync11(dir, { recursive: true });
15977
- writeFileSync7(statePath, JSON.stringify(state, null, 2), "utf-8");
16711
+ const dir = dirname9(statePath);
16712
+ if (!existsSync27(dir)) mkdirSync10(dir, { recursive: true });
16713
+ writeFileSync6(statePath, JSON.stringify(state, null, 2), "utf-8");
15978
16714
  }
15979
16715
  async function startParallelExecution(epicId, wave, cwd, accessor) {
15980
16716
  const projectRoot = cwd ?? process.cwd();
@@ -16109,8 +16845,8 @@ async function validateSpawnReadiness(taskId, cwd, accessor) {
16109
16845
  // src/core/orchestration/bootstrap.ts
16110
16846
  init_json();
16111
16847
  init_paths();
16112
- import { readFileSync as readFileSync21, existsSync as existsSync29 } from "node:fs";
16113
- import { join as join31 } from "node:path";
16848
+ import { readFileSync as readFileSync20, existsSync as existsSync28 } from "node:fs";
16849
+ import { join as join30 } from "node:path";
16114
16850
  async function buildBrainState(projectRoot, opts, accessor) {
16115
16851
  const speed = opts?.speed || "fast";
16116
16852
  const brain = {
@@ -16122,8 +16858,8 @@ async function buildBrainState(projectRoot, opts, accessor) {
16122
16858
  };
16123
16859
  try {
16124
16860
  const sessionsPath = getSessionsPath(projectRoot);
16125
- if (existsSync29(sessionsPath)) {
16126
- const sessionsData = JSON.parse(readFileSync21(sessionsPath, "utf-8"));
16861
+ if (existsSync28(sessionsPath)) {
16862
+ const sessionsData = JSON.parse(readFileSync20(sessionsPath, "utf-8"));
16127
16863
  const activeSession = (sessionsData.sessions ?? []).find(
16128
16864
  (s) => s.status === "active"
16129
16865
  );
@@ -16178,9 +16914,9 @@ async function buildBrainState(projectRoot, opts, accessor) {
16178
16914
  blockedBy: b.depends || []
16179
16915
  }));
16180
16916
  try {
16181
- const decisionLogPath = join31(projectRoot, ".cleo", "decision-log.jsonl");
16182
- if (existsSync29(decisionLogPath)) {
16183
- const content = readFileSync21(decisionLogPath, "utf-8").trim();
16917
+ const decisionLogPath = join30(projectRoot, ".cleo", "decision-log.jsonl");
16918
+ if (existsSync28(decisionLogPath)) {
16919
+ const content = readFileSync20(decisionLogPath, "utf-8").trim();
16184
16920
  if (content) {
16185
16921
  const entries = content.split("\n").filter((l) => l.trim()).map((l) => {
16186
16922
  try {
@@ -16200,9 +16936,9 @@ async function buildBrainState(projectRoot, opts, accessor) {
16200
16936
  } catch {
16201
16937
  }
16202
16938
  try {
16203
- const contextStatePath = join31(projectRoot, ".cleo", ".context-state.json");
16204
- if (existsSync29(contextStatePath)) {
16205
- const state = JSON.parse(readFileSync21(contextStatePath, "utf-8"));
16939
+ const contextStatePath = join30(projectRoot, ".cleo", ".context-state.json");
16940
+ if (existsSync28(contextStatePath)) {
16941
+ const state = JSON.parse(readFileSync20(contextStatePath, "utf-8"));
16206
16942
  const percentage = state.contextWindow?.percentage ?? 0;
16207
16943
  const factors = [];
16208
16944
  if (percentage > 80) factors.push("high_context_usage");
@@ -16351,7 +17087,7 @@ async function orchestrateReady(epicId, projectRoot) {
16351
17087
  try {
16352
17088
  const root = projectRoot || resolveProjectRoot();
16353
17089
  const accessor = await getAccessor(root);
16354
- const readyTasks = await getReadyTasks(epicId, root, accessor);
17090
+ const readyTasks = await getReadyTasks2(epicId, root, accessor);
16355
17091
  const ready = readyTasks.filter((t) => t.ready);
16356
17092
  return {
16357
17093
  success: true,
@@ -16390,7 +17126,7 @@ async function orchestrateNext(epicId, projectRoot) {
16390
17126
  }
16391
17127
  };
16392
17128
  }
16393
- const readyTasks = await getReadyTasks(epicId, root, accessor);
17129
+ const readyTasks = await getReadyTasks2(epicId, root, accessor);
16394
17130
  const ready = readyTasks.filter((t) => t.ready);
16395
17131
  return {
16396
17132
  success: true,
@@ -16430,11 +17166,11 @@ async function orchestrateContext(epicId, projectRoot) {
16430
17166
  }
16431
17167
  const estimatedTokens = taskCount * 100;
16432
17168
  const manifestPath = getManifestPath(root);
16433
- let manifestEntries = 0;
16434
- if (existsSync30(manifestPath)) {
17169
+ let manifestEntries2 = 0;
17170
+ if (existsSync29(manifestPath)) {
16435
17171
  try {
16436
- const content = readFileSync22(manifestPath, "utf-8");
16437
- manifestEntries = content.split("\n").filter((l) => l.trim()).length;
17172
+ const content = readFileSync21(manifestPath, "utf-8");
17173
+ manifestEntries2 = content.split("\n").filter((l) => l.trim()).length;
16438
17174
  } catch {
16439
17175
  }
16440
17176
  }
@@ -16443,7 +17179,7 @@ async function orchestrateContext(epicId, projectRoot) {
16443
17179
  data: {
16444
17180
  epicId: epicId || null,
16445
17181
  taskCount,
16446
- manifestEntries,
17182
+ manifestEntries: manifestEntries2,
16447
17183
  estimatedTokens,
16448
17184
  recommendation: estimatedTokens > 5e3 ? "Consider using manifest summaries instead of full task details" : "Context usage is within recommended limits",
16449
17185
  limits: {
@@ -16470,7 +17206,7 @@ async function orchestrateValidate(taskId, projectRoot) {
16470
17206
  return engineError("E_VALIDATION", err.message);
16471
17207
  }
16472
17208
  }
16473
- async function orchestrateSpawn(taskId, protocolType, projectRoot) {
17209
+ async function orchestrateSpawn(taskId, protocolType, projectRoot, tier) {
16474
17210
  if (!taskId) {
16475
17211
  return engineError("E_INVALID_INPUT", "taskId is required");
16476
17212
  }
@@ -16491,9 +17227,11 @@ async function orchestrateSpawn(taskId, protocolType, projectRoot) {
16491
17227
  spawnContext: {
16492
17228
  taskId: spawnContext.taskId,
16493
17229
  protocol: spawnContext.protocol,
16494
- protocolType: protocolType || spawnContext.protocol
17230
+ protocolType: protocolType || spawnContext.protocol,
17231
+ tier: tier ?? null
16495
17232
  },
16496
17233
  protocolType: protocolType || spawnContext.protocol,
17234
+ tier: tier ?? null,
16497
17235
  tokenResolution: spawnContext.tokenResolution
16498
17236
  }
16499
17237
  };
@@ -16516,7 +17254,7 @@ async function orchestrateStartup(epicId, projectRoot) {
16516
17254
  }
16517
17255
  const children = tasks2.filter((t) => t.parentId === epicId);
16518
17256
  const waves = computeWaves(children);
16519
- const readyTasks = await getReadyTasks(epicId, root, accessor);
17257
+ const readyTasks = await getReadyTasks2(epicId, root, accessor);
16520
17258
  const ready = readyTasks.filter((t) => t.ready);
16521
17259
  return {
16522
17260
  success: true,
@@ -16652,8 +17390,8 @@ async function orchestrateCheck(projectRoot) {
16652
17390
 
16653
17391
  // src/core/memory/engine-compat.ts
16654
17392
  init_paths();
16655
- import { readFileSync as readFileSync25, writeFileSync as writeFileSync10, appendFileSync as appendFileSync6, existsSync as existsSync33, mkdirSync as mkdirSync14 } from "node:fs";
16656
- import { resolve as resolve6, dirname as dirname11 } from "node:path";
17393
+ import { readFileSync as readFileSync24, writeFileSync as writeFileSync9, appendFileSync as appendFileSync6, existsSync as existsSync32, mkdirSync as mkdirSync13 } from "node:fs";
17394
+ import { resolve as resolve7, dirname as dirname10 } from "node:path";
16657
17395
 
16658
17396
  // src/core/memory/index.ts
16659
17397
  init_json();
@@ -16695,25 +17433,25 @@ function filterManifestEntries(entries, filter) {
16695
17433
 
16696
17434
  // src/core/memory/patterns.ts
16697
17435
  import { randomBytes as randomBytes7 } from "node:crypto";
16698
- import { readFileSync as readFileSync23, writeFileSync as writeFileSync8, appendFileSync as appendFileSync4, mkdirSync as mkdirSync12, existsSync as existsSync31 } from "node:fs";
16699
- import { join as join32 } from "node:path";
17436
+ import { readFileSync as readFileSync22, writeFileSync as writeFileSync7, appendFileSync as appendFileSync4, mkdirSync as mkdirSync11, existsSync as existsSync30 } from "node:fs";
17437
+ import { join as join31 } from "node:path";
16700
17438
  function getMemoryDir(projectRoot) {
16701
- const memDir = join32(projectRoot, ".cleo", "memory");
16702
- if (!existsSync31(memDir)) {
16703
- mkdirSync12(memDir, { recursive: true });
17439
+ const memDir = join31(projectRoot, ".cleo", "memory");
17440
+ if (!existsSync30(memDir)) {
17441
+ mkdirSync11(memDir, { recursive: true });
16704
17442
  }
16705
17443
  return memDir;
16706
17444
  }
16707
17445
  function getPatternsPath(projectRoot) {
16708
- return join32(getMemoryDir(projectRoot), "patterns.jsonl");
17446
+ return join31(getMemoryDir(projectRoot), "patterns.jsonl");
16709
17447
  }
16710
17448
  function generatePatternId() {
16711
17449
  return `P${randomBytes7(4).toString("hex")}`;
16712
17450
  }
16713
17451
  function readPatterns(projectRoot) {
16714
17452
  const path = getPatternsPath(projectRoot);
16715
- if (!existsSync31(path)) return [];
16716
- const content = readFileSync23(path, "utf-8").trim();
17453
+ if (!existsSync30(path)) return [];
17454
+ const content = readFileSync22(path, "utf-8").trim();
16717
17455
  if (!content) return [];
16718
17456
  const entries = [];
16719
17457
  for (const line of content.split("\n")) {
@@ -16750,7 +17488,7 @@ function storePattern(projectRoot, params) {
16750
17488
  }
16751
17489
  const path2 = getPatternsPath(projectRoot);
16752
17490
  const updated = existing.map((e) => JSON.stringify(e)).join("\n") + "\n";
16753
- writeFileSync8(path2, updated, "utf-8");
17491
+ writeFileSync7(path2, updated, "utf-8");
16754
17492
  return duplicate;
16755
17493
  }
16756
17494
  const entry = {
@@ -16822,25 +17560,25 @@ function patternStats(projectRoot) {
16822
17560
 
16823
17561
  // src/core/memory/learnings.ts
16824
17562
  import { randomBytes as randomBytes8 } from "node:crypto";
16825
- import { readFileSync as readFileSync24, writeFileSync as writeFileSync9, appendFileSync as appendFileSync5, mkdirSync as mkdirSync13, existsSync as existsSync32 } from "node:fs";
16826
- import { join as join33 } from "node:path";
17563
+ import { readFileSync as readFileSync23, writeFileSync as writeFileSync8, appendFileSync as appendFileSync5, mkdirSync as mkdirSync12, existsSync as existsSync31 } from "node:fs";
17564
+ import { join as join32 } from "node:path";
16827
17565
  function getMemoryDir2(projectRoot) {
16828
- const memDir = join33(projectRoot, ".cleo", "memory");
16829
- if (!existsSync32(memDir)) {
16830
- mkdirSync13(memDir, { recursive: true });
17566
+ const memDir = join32(projectRoot, ".cleo", "memory");
17567
+ if (!existsSync31(memDir)) {
17568
+ mkdirSync12(memDir, { recursive: true });
16831
17569
  }
16832
17570
  return memDir;
16833
17571
  }
16834
17572
  function getLearningsPath(projectRoot) {
16835
- return join33(getMemoryDir2(projectRoot), "learnings.jsonl");
17573
+ return join32(getMemoryDir2(projectRoot), "learnings.jsonl");
16836
17574
  }
16837
17575
  function generateLearningId() {
16838
17576
  return `L${randomBytes8(4).toString("hex")}`;
16839
17577
  }
16840
17578
  function readLearnings(projectRoot) {
16841
17579
  const path = getLearningsPath(projectRoot);
16842
- if (!existsSync32(path)) return [];
16843
- const content = readFileSync24(path, "utf-8").trim();
17580
+ if (!existsSync31(path)) return [];
17581
+ const content = readFileSync23(path, "utf-8").trim();
16844
17582
  if (!content) return [];
16845
17583
  const entries = [];
16846
17584
  for (const line of content.split("\n")) {
@@ -16879,7 +17617,7 @@ function storeLearning(projectRoot, params) {
16879
17617
  }
16880
17618
  const path2 = getLearningsPath(projectRoot);
16881
17619
  const updated = existing.map((e) => JSON.stringify(e)).join("\n") + "\n";
16882
- writeFileSync9(path2, updated, "utf-8");
17620
+ writeFileSync8(path2, updated, "utf-8");
16883
17621
  return duplicate;
16884
17622
  }
16885
17623
  const entry = {
@@ -16954,7 +17692,7 @@ function resolveRoot(projectRoot) {
16954
17692
  function readManifestEntries(projectRoot) {
16955
17693
  const manifestPath = getManifestPath2(projectRoot);
16956
17694
  try {
16957
- const content = readFileSync25(manifestPath, "utf-8");
17695
+ const content = readFileSync24(manifestPath, "utf-8");
16958
17696
  const entries = [];
16959
17697
  const lines = content.split("\n");
16960
17698
  for (const line of lines) {
@@ -16989,9 +17727,9 @@ function memoryShow(researchId, projectRoot) {
16989
17727
  const root = resolveRoot(projectRoot);
16990
17728
  let fileContent = null;
16991
17729
  try {
16992
- const filePath = resolve6(root, entry.file);
16993
- if (existsSync33(filePath)) {
16994
- fileContent = readFileSync25(filePath, "utf-8");
17730
+ const filePath = resolve7(root, entry.file);
17731
+ if (existsSync32(filePath)) {
17732
+ fileContent = readFileSync24(filePath, "utf-8");
16995
17733
  }
16996
17734
  } catch {
16997
17735
  }
@@ -17119,7 +17857,7 @@ function memoryLink(taskId, researchId, notes, projectRoot) {
17119
17857
  }
17120
17858
  entry.linked_tasks.push(taskId);
17121
17859
  const content = entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
17122
- writeFileSync10(manifestPath, content, "utf-8");
17860
+ writeFileSync9(manifestPath, content, "utf-8");
17123
17861
  return { success: true, data: { taskId, researchId, linked: true, notes: notes || null } };
17124
17862
  }
17125
17863
  function memoryManifestAppend(entry, projectRoot) {
@@ -17139,9 +17877,9 @@ function memoryManifestAppend(entry, projectRoot) {
17139
17877
  return { success: false, error: { code: "E_VALIDATION_FAILED", message: `Invalid manifest entry: ${errors.join(", ")}` } };
17140
17878
  }
17141
17879
  const manifestPath = getManifestPath2(projectRoot);
17142
- const dir = dirname11(manifestPath);
17143
- if (!existsSync33(dir)) {
17144
- mkdirSync14(dir, { recursive: true });
17880
+ const dir = dirname10(manifestPath);
17881
+ if (!existsSync32(dir)) {
17882
+ mkdirSync13(dir, { recursive: true });
17145
17883
  }
17146
17884
  const serialized = JSON.stringify(entry);
17147
17885
  appendFileSync6(manifestPath, serialized + "\n", "utf-8");
@@ -17160,14 +17898,14 @@ function memoryManifestArchive(beforeDate, projectRoot) {
17160
17898
  if (toArchive.length === 0) {
17161
17899
  return { success: true, data: { archived: 0, remaining: entries.length, message: "No entries found before the specified date" } };
17162
17900
  }
17163
- const archiveDir = dirname11(archivePath);
17164
- if (!existsSync33(archiveDir)) {
17165
- mkdirSync14(archiveDir, { recursive: true });
17901
+ const archiveDir = dirname10(archivePath);
17902
+ if (!existsSync32(archiveDir)) {
17903
+ mkdirSync13(archiveDir, { recursive: true });
17166
17904
  }
17167
17905
  const archiveContent = toArchive.map((e) => JSON.stringify(e)).join("\n") + "\n";
17168
17906
  appendFileSync6(archivePath, archiveContent, "utf-8");
17169
17907
  const remainingContent = toKeep.length > 0 ? toKeep.map((e) => JSON.stringify(e)).join("\n") + "\n" : "";
17170
- writeFileSync10(manifestPath, remainingContent, "utf-8");
17908
+ writeFileSync9(manifestPath, remainingContent, "utf-8");
17171
17909
  return { success: true, data: { archived: toArchive.length, remaining: toKeep.length, archiveFile: getManifestArchivePath() } };
17172
17910
  }
17173
17911
  function memoryContradictions(projectRoot, params) {
@@ -17249,16 +17987,16 @@ function memoryInject(protocolType, params, projectRoot) {
17249
17987
  }
17250
17988
  const root = resolveRoot(projectRoot);
17251
17989
  const protocolLocations = [
17252
- resolve6(root, "protocols", `${protocolType}.md`),
17253
- resolve6(root, "skills", "_shared", `${protocolType}.md`),
17254
- resolve6(root, "agents", "cleo-subagent", "protocols", `${protocolType}.md`)
17990
+ resolve7(root, "protocols", `${protocolType}.md`),
17991
+ resolve7(root, "skills", "_shared", `${protocolType}.md`),
17992
+ resolve7(root, "agents", "cleo-subagent", "protocols", `${protocolType}.md`)
17255
17993
  ];
17256
17994
  let protocolContent = null;
17257
17995
  let protocolPath = null;
17258
17996
  for (const loc of protocolLocations) {
17259
- if (existsSync33(loc)) {
17997
+ if (existsSync32(loc)) {
17260
17998
  try {
17261
- protocolContent = readFileSync25(loc, "utf-8");
17999
+ protocolContent = readFileSync24(loc, "utf-8");
17262
18000
  protocolPath = loc.replace(root + "/", "");
17263
18001
  break;
17264
18002
  } catch {
@@ -17347,11 +18085,11 @@ init_data_accessor();
17347
18085
  // src/core/release/release-manifest.ts
17348
18086
  init_json();
17349
18087
  init_paths();
17350
- import { existsSync as existsSync34, mkdirSync as mkdirSync15 } from "node:fs";
18088
+ import { existsSync as existsSync33, mkdirSync as mkdirSync14 } from "node:fs";
17351
18089
  import { execFileSync as execFileSync3 } from "node:child_process";
17352
- import { dirname as dirname12, join as join34 } from "node:path";
18090
+ import { dirname as dirname11, join as join33 } from "node:path";
17353
18091
  function getReleasesPath(cwd) {
17354
- return join34(getCleoDirAbsolute(cwd), "releases.json");
18092
+ return join33(getCleoDirAbsolute(cwd), "releases.json");
17355
18093
  }
17356
18094
  async function readReleases(cwd) {
17357
18095
  const data = await readJson(getReleasesPath(cwd));
@@ -17359,9 +18097,9 @@ async function readReleases(cwd) {
17359
18097
  }
17360
18098
  async function writeReleases(index2, cwd) {
17361
18099
  const releasesPath = getReleasesPath(cwd);
17362
- const dir = dirname12(releasesPath);
17363
- if (!existsSync34(dir)) {
17364
- mkdirSync15(dir, { recursive: true });
18100
+ const dir = dirname11(releasesPath);
18101
+ if (!existsSync33(dir)) {
18102
+ mkdirSync14(dir, { recursive: true });
17365
18103
  }
17366
18104
  await saveJson(releasesPath, index2);
17367
18105
  }
@@ -17628,7 +18366,7 @@ async function rollbackRelease(version, reason, cwd) {
17628
18366
  };
17629
18367
  }
17630
18368
  async function readPushPolicy(cwd) {
17631
- const configPath = join34(getCleoDirAbsolute(cwd), "config.json");
18369
+ const configPath = join33(getCleoDirAbsolute(cwd), "config.json");
17632
18370
  const config = await readJson(configPath);
17633
18371
  if (!config) return void 0;
17634
18372
  const release2 = config.release;
@@ -17712,8 +18450,8 @@ async function hasManifestEntry(version, projectRoot) {
17712
18450
  }
17713
18451
  async function loadTasks2(projectRoot) {
17714
18452
  if (projectRoot) {
17715
- const taskPath = getDataPath2(projectRoot, "todo.json");
17716
- const taskData = readJsonFile2(taskPath);
18453
+ const taskPath = getDataPath(projectRoot, "todo.json");
18454
+ const taskData = readJsonFile(taskPath);
17717
18455
  return taskData?.tasks ?? [];
17718
18456
  }
17719
18457
  try {
@@ -17722,8 +18460,8 @@ async function loadTasks2(projectRoot) {
17722
18460
  return taskFile?.tasks ?? [];
17723
18461
  } catch {
17724
18462
  const root = resolveProjectRoot();
17725
- const taskPath = getDataPath2(root, "todo.json");
17726
- const taskData = readJsonFile2(taskPath);
18463
+ const taskPath = getDataPath(root, "todo.json");
18464
+ const taskData = readJsonFile(taskPath);
17727
18465
  return taskData?.tasks ?? [];
17728
18466
  }
17729
18467
  }
@@ -17843,8 +18581,8 @@ async function releasePush(version, remote, projectRoot, opts) {
17843
18581
 
17844
18582
  // src/dispatch/engines/template-parser.ts
17845
18583
  init_platform();
17846
- import { readFileSync as readFileSync26, readdirSync as readdirSync9, existsSync as existsSync35 } from "fs";
17847
- import { join as join35 } from "path";
18584
+ import { readFileSync as readFileSync25, readdirSync as readdirSync9, existsSync as existsSync34 } from "fs";
18585
+ import { join as join34 } from "path";
17848
18586
  import { parse as parseYaml } from "yaml";
17849
18587
  var SUFFIX_PATTERNS = ["_report", "_request", "_question"];
17850
18588
  function deriveSubcommand(filename) {
@@ -17859,8 +18597,8 @@ function deriveSubcommand(filename) {
17859
18597
  return firstWord.toLowerCase();
17860
18598
  }
17861
18599
  function parseTemplateFile(templateDir, filename) {
17862
- const filePath = join35(templateDir, filename);
17863
- const raw = readFileSync26(filePath, "utf-8");
18600
+ const filePath = join34(templateDir, filename);
18601
+ const raw = readFileSync25(filePath, "utf-8");
17864
18602
  const parsed = parseYaml(raw);
17865
18603
  const name = typeof parsed.name === "string" ? parsed.name : filename;
17866
18604
  const titlePrefix = typeof parsed.title === "string" ? parsed.title : "";
@@ -17909,8 +18647,8 @@ function parseTemplateFile(templateDir, filename) {
17909
18647
  };
17910
18648
  }
17911
18649
  function parseIssueTemplates(projectRoot) {
17912
- const templateDir = join35(projectRoot, ".github", "ISSUE_TEMPLATE");
17913
- if (!existsSync35(templateDir)) {
18650
+ const templateDir = join34(projectRoot, ".github", "ISSUE_TEMPLATE");
18651
+ if (!existsSync34(templateDir)) {
17914
18652
  return engineError("E_NOT_FOUND", `Issue template directory not found: ${templateDir}`);
17915
18653
  }
17916
18654
  let files;
@@ -17962,7 +18700,7 @@ async function generateTemplateConfig(projectRoot) {
17962
18700
  if (!result.success || !result.data) {
17963
18701
  return result.error ? { success: false, error: result.error } : engineError("E_PARSE_ERROR", "Failed to parse issue templates");
17964
18702
  }
17965
- const outputPath = getDataPath2(projectRoot, "issue-templates.json");
18703
+ const outputPath = getDataPath(projectRoot, "issue-templates.json");
17966
18704
  try {
17967
18705
  writeJsonFileAtomic(outputPath, result.data);
17968
18706
  } catch (error) {
@@ -17990,7 +18728,6 @@ function validateLabels2(labels, repoLabels) {
17990
18728
  }
17991
18729
 
17992
18730
  // src/dispatch/domains/tasks.ts
17993
- var logger2 = getLogger("domain:tasks");
17994
18731
  var TasksHandler = class {
17995
18732
  projectRoot;
17996
18733
  constructor() {
@@ -18012,14 +18749,26 @@ var TasksHandler = class {
18012
18749
  return this.wrapEngineResult(result, "query", "tasks", operation, startTime);
18013
18750
  }
18014
18751
  case "list": {
18015
- const result = await taskList(this.projectRoot, params);
18752
+ const result = await taskList(this.projectRoot, {
18753
+ parent: params?.parent,
18754
+ status: params?.status,
18755
+ limit: params?.limit,
18756
+ compact: params?.compact
18757
+ });
18016
18758
  return this.wrapEngineResult(result, "query", "tasks", operation, startTime);
18017
18759
  }
18018
18760
  case "find": {
18019
18761
  const result = await taskFind(
18020
18762
  this.projectRoot,
18021
18763
  params?.query,
18022
- params?.limit
18764
+ params?.limit,
18765
+ {
18766
+ id: params?.id,
18767
+ exact: params?.exact,
18768
+ status: params?.status,
18769
+ includeArchive: params?.includeArchive,
18770
+ offset: params?.offset
18771
+ }
18023
18772
  );
18024
18773
  return this.wrapEngineResult(result, "query", "tasks", operation, startTime);
18025
18774
  }
@@ -18041,12 +18790,22 @@ var TasksHandler = class {
18041
18790
  return this.wrapEngineResult(result, "query", "tasks", operation, startTime);
18042
18791
  }
18043
18792
  case "depends": {
18793
+ const action = params?.action;
18794
+ if (action === "overview") {
18795
+ const result2 = await taskDepsOverview(this.projectRoot);
18796
+ return this.wrapEngineResult(result2, "query", "tasks", operation, startTime);
18797
+ }
18798
+ if (action === "cycles") {
18799
+ const result2 = await taskDepsCycles(this.projectRoot);
18800
+ return this.wrapEngineResult(result2, "query", "tasks", operation, startTime);
18801
+ }
18044
18802
  const taskId = params?.taskId;
18045
18803
  if (!taskId) {
18046
- return this.errorResponse("query", "tasks", operation, "E_INVALID_INPUT", "taskId is required", startTime);
18804
+ return this.errorResponse("query", "tasks", operation, "E_INVALID_INPUT", "taskId is required (or use action: overview|cycles)", startTime);
18047
18805
  }
18048
18806
  const direction = params?.direction;
18049
- const result = await taskDepends(this.projectRoot, taskId, direction);
18807
+ const tree = params?.tree;
18808
+ const result = await taskDepends(this.projectRoot, taskId, direction, tree);
18050
18809
  return this.wrapEngineResult(result, "query", "tasks", operation, startTime);
18051
18810
  }
18052
18811
  case "analyze": {
@@ -18216,10 +18975,10 @@ var TasksHandler = class {
18216
18975
  }
18217
18976
  case "relates.add": {
18218
18977
  const taskId = params?.taskId;
18219
- const relatedId = params?.relatedId;
18978
+ const relatedId = params?.relatedId ?? params?.targetId;
18220
18979
  const type = params?.type;
18221
18980
  if (!taskId || !relatedId || !type) {
18222
- return this.errorResponse("mutate", "tasks", operation, "E_INVALID_INPUT", "taskId, relatedId, and type are required", startTime);
18981
+ return this.errorResponse("mutate", "tasks", operation, "E_INVALID_INPUT", "taskId, relatedId (or targetId), and type are required", startTime);
18223
18982
  }
18224
18983
  const result = await taskRelatesAdd(
18225
18984
  this.projectRoot,
@@ -18313,7 +19072,7 @@ var TasksHandler = class {
18313
19072
  }
18314
19073
  handleError(gateway, domain, operation, error, startTime) {
18315
19074
  const message = error instanceof Error ? error.message : String(error);
18316
- logger2.error({ gateway, domain, operation, err: error }, message);
19075
+ getLogger("domain:tasks").error({ gateway, domain, operation, err: error }, message);
18317
19076
  return {
18318
19077
  _meta: dispatchMeta(gateway, domain, operation, startTime),
18319
19078
  success: false,
@@ -18351,7 +19110,6 @@ function unbindSession() {
18351
19110
  }
18352
19111
 
18353
19112
  // src/dispatch/domains/session.ts
18354
- var logger3 = getLogger("domain:session");
18355
19113
  var SessionHandler = class {
18356
19114
  projectRoot;
18357
19115
  constructor() {
@@ -18472,7 +19230,7 @@ var SessionHandler = class {
18472
19230
  gradeMode: params?.grade ?? false
18473
19231
  });
18474
19232
  } catch {
18475
- logger3.warn({ sessionId: session.id }, "Session context already bound, skipping bindSession");
19233
+ getLogger("domain:session").warn({ sessionId: session.id }, "Session context already bound, skipping bindSession");
18476
19234
  }
18477
19235
  }
18478
19236
  return this.wrapEngineResult(result, "mutate", "session", operation, startTime);
@@ -18583,7 +19341,7 @@ var SessionHandler = class {
18583
19341
  }
18584
19342
  handleError(gateway, domain, operation, error, startTime) {
18585
19343
  const message = error instanceof Error ? error.message : String(error);
18586
- logger3.error({ gateway, domain, operation, err: error }, message);
19344
+ getLogger("domain:session").error({ gateway, domain, operation, err: error }, message);
18587
19345
  return {
18588
19346
  _meta: dispatchMeta(gateway, domain, operation, startTime),
18589
19347
  success: false,
@@ -18595,7 +19353,6 @@ var SessionHandler = class {
18595
19353
  // src/dispatch/domains/check.ts
18596
19354
  init_paths();
18597
19355
  init_logger();
18598
- var logger4 = getLogger("domain:check");
18599
19356
  var CheckHandler = class {
18600
19357
  projectRoot;
18601
19358
  constructor() {
@@ -18754,7 +19511,7 @@ var CheckHandler = class {
18754
19511
  }
18755
19512
  handleError(gateway, domain, operation, error, startTime) {
18756
19513
  const message = error instanceof Error ? error.message : String(error);
18757
- logger4.error({ gateway, domain, operation, err: error }, message);
19514
+ getLogger("domain:check").error({ gateway, domain, operation, err: error }, message);
18758
19515
  return {
18759
19516
  _meta: dispatchMeta(gateway, domain, operation, startTime),
18760
19517
  success: false,
@@ -18768,8 +19525,8 @@ init_paths();
18768
19525
  init_logger();
18769
19526
 
18770
19527
  // src/core/adrs/parse.ts
18771
- import { readFileSync as readFileSync27 } from "node:fs";
18772
- import { join as join36 } from "node:path";
19528
+ import { readFileSync as readFileSync26 } from "node:fs";
19529
+ import { join as join35 } from "node:path";
18773
19530
  function extractAdrId(filename) {
18774
19531
  const match = filename.match(/^(ADR-\d+)/);
18775
19532
  return match ? match[1] : filename.replace(".md", "");
@@ -18791,8 +19548,8 @@ function extractTitle(content) {
18791
19548
  return match ? match[1].trim() : "Untitled";
18792
19549
  }
18793
19550
  function parseAdrFile(filePath, projectRoot) {
18794
- const absolutePath = filePath.startsWith("/") ? filePath : join36(projectRoot, filePath);
18795
- const content = readFileSync27(absolutePath, "utf-8");
19551
+ const absolutePath = filePath.startsWith("/") ? filePath : join35(projectRoot, filePath);
19552
+ const content = readFileSync26(absolutePath, "utf-8");
18796
19553
  const filename = filePath.split("/").pop();
18797
19554
  return {
18798
19555
  id: extractAdrId(filename),
@@ -18803,27 +19560,27 @@ function parseAdrFile(filePath, projectRoot) {
18803
19560
  }
18804
19561
 
18805
19562
  // src/core/adrs/validate.ts
18806
- import { readFileSync as readFileSync28, readdirSync as readdirSync10, existsSync as existsSync36 } from "node:fs";
18807
- import { join as join37 } from "node:path";
19563
+ import { readFileSync as readFileSync27, readdirSync as readdirSync10, existsSync as existsSync35 } from "node:fs";
19564
+ import { join as join36 } from "node:path";
18808
19565
  import AjvModule3 from "ajv";
18809
19566
  var Ajv3 = AjvModule3.default ?? AjvModule3;
18810
19567
  async function validateAllAdrs(projectRoot) {
18811
- const adrsDir = join37(projectRoot, ".cleo", "adrs");
18812
- const schemaPath = join37(projectRoot, "schemas", "adr-frontmatter.schema.json");
18813
- if (!existsSync36(schemaPath)) {
19568
+ const adrsDir = join36(projectRoot, ".cleo", "adrs");
19569
+ const schemaPath = join36(projectRoot, "schemas", "adr-frontmatter.schema.json");
19570
+ if (!existsSync35(schemaPath)) {
18814
19571
  return {
18815
19572
  valid: false,
18816
19573
  errors: [{ file: "schemas/adr-frontmatter.schema.json", field: "schema", message: "Schema file not found" }],
18817
19574
  checked: 0
18818
19575
  };
18819
19576
  }
18820
- if (!existsSync36(adrsDir)) {
19577
+ if (!existsSync35(adrsDir)) {
18821
19578
  return { valid: true, errors: [], checked: 0 };
18822
19579
  }
18823
- const schema = JSON.parse(readFileSync28(schemaPath, "utf-8"));
19580
+ const schema = JSON.parse(readFileSync27(schemaPath, "utf-8"));
18824
19581
  const ajv = new Ajv3({ allErrors: true });
18825
19582
  const validate = ajv.compile(schema);
18826
- const files = readdirSync10(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).map((f) => join37(adrsDir, f));
19583
+ const files = readdirSync10(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).map((f) => join36(adrsDir, f));
18827
19584
  const errors = [];
18828
19585
  for (const filePath of files) {
18829
19586
  const record = parseAdrFile(filePath, projectRoot);
@@ -18844,8 +19601,8 @@ async function validateAllAdrs(projectRoot) {
18844
19601
  // src/core/adrs/sync.ts
18845
19602
  init_sqlite();
18846
19603
  init_schema();
18847
- import { readFileSync as readFileSync29, readdirSync as readdirSync11, writeFileSync as writeFileSync11, existsSync as existsSync37 } from "node:fs";
18848
- import { join as join38 } from "node:path";
19604
+ import { readFileSync as readFileSync28, readdirSync as readdirSync11, writeFileSync as writeFileSync10, existsSync as existsSync36 } from "node:fs";
19605
+ import { join as join37 } from "node:path";
18849
19606
  import { eq as eq7 } from "drizzle-orm";
18850
19607
  function extractAdrIdFromRef(ref) {
18851
19608
  const m = ref.match(/^(ADR-\d+)/);
@@ -18858,7 +19615,7 @@ function collectAdrFiles(dir) {
18858
19615
  const results = [];
18859
19616
  for (const entry of readdirSync11(dir, { withFileTypes: true })) {
18860
19617
  if (entry.isDirectory()) {
18861
- const sub = join38(dir, entry.name);
19618
+ const sub = join37(dir, entry.name);
18862
19619
  for (const f of readdirSync11(sub)) {
18863
19620
  if (f.endsWith(".md") && /^ADR-\d+/.test(f)) {
18864
19621
  results.push({ file: f, relPath: `${entry.name}/${f}` });
@@ -18871,23 +19628,23 @@ function collectAdrFiles(dir) {
18871
19628
  return results.sort((a, b) => a.file.localeCompare(b.file));
18872
19629
  }
18873
19630
  async function syncAdrsToDb(projectRoot) {
18874
- const adrsDir = join38(projectRoot, ".cleo", "adrs");
19631
+ const adrsDir = join37(projectRoot, ".cleo", "adrs");
18875
19632
  const result = { inserted: 0, updated: 0, skipped: 0, errors: [] };
18876
- if (!existsSync37(adrsDir)) {
19633
+ if (!existsSync36(adrsDir)) {
18877
19634
  return result;
18878
19635
  }
18879
19636
  const db = await getDb(projectRoot);
18880
19637
  const now = (/* @__PURE__ */ new Date()).toISOString();
18881
19638
  const allFiles = collectAdrFiles(adrsDir);
18882
19639
  const activeFiles = allFiles.filter((f) => !f.relPath.includes("/"));
18883
- const manifestEntries = [];
19640
+ const manifestEntries2 = [];
18884
19641
  for (const { file, relPath } of activeFiles) {
18885
19642
  try {
18886
- const filePath = join38(adrsDir, relPath);
19643
+ const filePath = join37(adrsDir, relPath);
18887
19644
  const record = parseAdrFile(filePath, projectRoot);
18888
19645
  const fm = record.frontmatter;
18889
19646
  const dbRelPath = `.cleo/adrs/${relPath}`;
18890
- const content = readFileSync29(filePath, "utf-8");
19647
+ const content = readFileSync28(filePath, "utf-8");
18891
19648
  const supersedesId = fm.Supersedes ? extractAdrIdFromRef(fm.Supersedes) : null;
18892
19649
  const supersededById = fm["Superseded By"] ? extractAdrIdFromRef(fm["Superseded By"]) : null;
18893
19650
  const amendsId = fm.Amends ? extractAdrIdFromRef(fm.Amends) : null;
@@ -18929,7 +19686,7 @@ async function syncAdrsToDb(projectRoot) {
18929
19686
  }
18930
19687
  for (const { relPath } of allFiles) {
18931
19688
  try {
18932
- const filePath = join38(adrsDir, relPath);
19689
+ const filePath = join37(adrsDir, relPath);
18933
19690
  const record = parseAdrFile(filePath, projectRoot);
18934
19691
  const fm = record.frontmatter;
18935
19692
  const entry = {
@@ -18952,28 +19709,28 @@ async function syncAdrsToDb(projectRoot) {
18952
19709
  if (fm.Summary) entry["summary"] = fm.Summary;
18953
19710
  if (fm.Keywords) entry["keywords"] = fm.Keywords.split(",").map((s) => s.trim()).filter(Boolean);
18954
19711
  if (fm.Topics) entry["topics"] = fm.Topics.split(",").map((s) => s.trim()).filter(Boolean);
18955
- manifestEntries.push(entry);
19712
+ manifestEntries2.push(entry);
18956
19713
  } catch {
18957
19714
  }
18958
19715
  }
18959
- writeFileSync11(
18960
- join38(adrsDir, "MANIFEST.jsonl"),
18961
- manifestEntries.map((e) => JSON.stringify(e)).join("\n") + "\n",
19716
+ writeFileSync10(
19717
+ join37(adrsDir, "MANIFEST.jsonl"),
19718
+ manifestEntries2.map((e) => JSON.stringify(e)).join("\n") + "\n",
18962
19719
  "utf-8"
18963
19720
  );
18964
19721
  return result;
18965
19722
  }
18966
19723
 
18967
19724
  // src/core/adrs/list.ts
18968
- import { readdirSync as readdirSync12, existsSync as existsSync38 } from "node:fs";
18969
- import { join as join39 } from "node:path";
19725
+ import { readdirSync as readdirSync12, existsSync as existsSync37 } from "node:fs";
19726
+ import { join as join38 } from "node:path";
18970
19727
  async function listAdrs(projectRoot, opts) {
18971
- const adrsDir = join39(projectRoot, ".cleo", "adrs");
18972
- if (!existsSync38(adrsDir)) {
19728
+ const adrsDir = join38(projectRoot, ".cleo", "adrs");
19729
+ if (!existsSync37(adrsDir)) {
18973
19730
  return { adrs: [], total: 0 };
18974
19731
  }
18975
19732
  const files = readdirSync12(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).sort();
18976
- const records = files.map((f) => parseAdrFile(join39(adrsDir, f), projectRoot));
19733
+ const records = files.map((f) => parseAdrFile(join38(adrsDir, f), projectRoot));
18977
19734
  const filtered = records.filter((r) => {
18978
19735
  if (opts?.status && r.frontmatter.Status !== opts.status) return false;
18979
19736
  if (opts?.since && r.frontmatter.Date < opts.since) return false;
@@ -18992,20 +19749,20 @@ async function listAdrs(projectRoot, opts) {
18992
19749
  }
18993
19750
 
18994
19751
  // src/core/adrs/show.ts
18995
- import { existsSync as existsSync39, readdirSync as readdirSync13 } from "node:fs";
18996
- import { join as join40 } from "node:path";
19752
+ import { existsSync as existsSync38, readdirSync as readdirSync13 } from "node:fs";
19753
+ import { join as join39 } from "node:path";
18997
19754
  async function showAdr(projectRoot, adrId) {
18998
- const adrsDir = join40(projectRoot, ".cleo", "adrs");
18999
- if (!existsSync39(adrsDir)) return null;
19755
+ const adrsDir = join39(projectRoot, ".cleo", "adrs");
19756
+ if (!existsSync38(adrsDir)) return null;
19000
19757
  const files = readdirSync13(adrsDir).filter((f) => f.startsWith(adrId) && f.endsWith(".md"));
19001
19758
  if (files.length === 0) return null;
19002
- const filePath = join40(adrsDir, files[0]);
19759
+ const filePath = join39(adrsDir, files[0]);
19003
19760
  return parseAdrFile(filePath, projectRoot);
19004
19761
  }
19005
19762
 
19006
19763
  // src/core/adrs/find.ts
19007
- import { readdirSync as readdirSync14, existsSync as existsSync40 } from "node:fs";
19008
- import { join as join41 } from "node:path";
19764
+ import { readdirSync as readdirSync14, existsSync as existsSync39 } from "node:fs";
19765
+ import { join as join40 } from "node:path";
19009
19766
  function normalise(s) {
19010
19767
  return s.toLowerCase().replace(/[^a-z0-9\s]/g, " ").replace(/\s+/g, " ").trim();
19011
19768
  }
@@ -19022,8 +19779,8 @@ function matchedTerms(target, terms) {
19022
19779
  return terms.filter((term) => t.includes(term));
19023
19780
  }
19024
19781
  async function findAdrs(projectRoot, query, opts) {
19025
- const adrsDir = join41(projectRoot, ".cleo", "adrs");
19026
- if (!existsSync40(adrsDir)) {
19782
+ const adrsDir = join40(projectRoot, ".cleo", "adrs");
19783
+ if (!existsSync39(adrsDir)) {
19027
19784
  return { adrs: [], query, total: 0 };
19028
19785
  }
19029
19786
  const files = readdirSync14(adrsDir).filter((f) => f.endsWith(".md") && f.startsWith("ADR-")).sort();
@@ -19032,7 +19789,7 @@ async function findAdrs(projectRoot, query, opts) {
19032
19789
  const filterKeywords = opts?.keywords ? parseTags(opts.keywords) : null;
19033
19790
  const results = [];
19034
19791
  for (const file of files) {
19035
- const record = parseAdrFile(join41(adrsDir, file), projectRoot);
19792
+ const record = parseAdrFile(join40(adrsDir, file), projectRoot);
19036
19793
  const fm = record.frontmatter;
19037
19794
  if (opts?.status && fm.Status !== opts.status) continue;
19038
19795
  if (filterTopics && filterTopics.length > 0) {
@@ -19095,7 +19852,6 @@ init_schema();
19095
19852
  import { eq as eq8, and as and2 } from "drizzle-orm";
19096
19853
 
19097
19854
  // src/dispatch/domains/admin.ts
19098
- var logger5 = getLogger("domain:admin");
19099
19855
  var AdminHandler = class {
19100
19856
  projectRoot;
19101
19857
  constructor() {
@@ -19178,6 +19934,14 @@ var AdminHandler = class {
19178
19934
  case "help": {
19179
19935
  const tier = typeof params?.tier === "number" ? params.tier : 0;
19180
19936
  const ops = OPERATIONS.filter((op) => op.tier <= tier);
19937
+ const getCostHint = (domain, op) => {
19938
+ const key = `${domain}.${op}`;
19939
+ const heavyOps = ["tasks.list", "tasks.tree", "admin.log", "admin.stats", "tasks.analyze"];
19940
+ const moderateOps = ["tasks.show", "tasks.blockers", "tasks.depends", "admin.health", "admin.dash", "admin.help"];
19941
+ if (heavyOps.includes(key)) return "heavy";
19942
+ if (moderateOps.includes(key)) return "moderate";
19943
+ return "minimal";
19944
+ };
19181
19945
  const tierGuidance = {
19182
19946
  0: "Tier 0: Core task and session operations (tasks, session, admin). 80% of use cases.",
19183
19947
  1: "Tier 1: + memory/research and check/validate operations. 15% of use cases.",
@@ -19189,14 +19953,22 @@ var AdminHandler = class {
19189
19953
  data: {
19190
19954
  tier,
19191
19955
  operationCount: ops.length,
19956
+ quickStart: tier === 0 ? [
19957
+ "cleo_query tasks.current \u2014 check active task (~100 tokens)",
19958
+ "cleo_query tasks.next \u2014 get suggestion (~300 tokens)",
19959
+ "cleo_query tasks.find {query} \u2014 search tasks (~200 tokens)",
19960
+ "cleo_mutate tasks.start {taskId} \u2014 begin work (~100 tokens)",
19961
+ "cleo_mutate tasks.complete {taskId} \u2014 finish task (~200 tokens)"
19962
+ ] : void 0,
19192
19963
  operations: ops.map((op) => ({
19193
19964
  gateway: op.gateway,
19194
19965
  domain: op.domain,
19195
19966
  operation: op.operation,
19196
- description: op.description
19967
+ description: op.description,
19968
+ costHint: getCostHint(op.domain, op.operation)
19197
19969
  })),
19198
19970
  guidance: tierGuidance[tier] ?? tierGuidance[0],
19199
- 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."
19971
+ escalation: tier < 2 ? `For more operations: cleo_query({domain:"admin",operation:"help",params:{tier:${tier + 1}}})` : "Full operation set displayed."
19200
19972
  }
19201
19973
  };
19202
19974
  }
@@ -19447,7 +20219,7 @@ var AdminHandler = class {
19447
20219
  }
19448
20220
  handleError(gateway, domain, operation, error, startTime) {
19449
20221
  const message = error instanceof Error ? error.message : String(error);
19450
- logger5.error({ gateway, domain, operation, err: error }, message);
20222
+ getLogger("domain:admin").error({ gateway, domain, operation, err: error }, message);
19451
20223
  return {
19452
20224
  _meta: dispatchMeta(gateway, domain, operation, startTime),
19453
20225
  success: false,
@@ -19459,7 +20231,6 @@ var AdminHandler = class {
19459
20231
  // src/dispatch/domains/memory.ts
19460
20232
  init_paths();
19461
20233
  init_logger();
19462
- var logger6 = getLogger("domain:memory");
19463
20234
  var MemoryHandler = class {
19464
20235
  projectRoot;
19465
20236
  constructor() {
@@ -19692,7 +20463,7 @@ var MemoryHandler = class {
19692
20463
  }
19693
20464
  handleError(gateway, domain, operation, error, startTime) {
19694
20465
  const message = error instanceof Error ? error.message : String(error);
19695
- logger6.error({ gateway, domain, operation, err: error }, message);
20466
+ getLogger("domain:memory").error({ gateway, domain, operation, err: error }, message);
19696
20467
  return {
19697
20468
  _meta: dispatchMeta(gateway, domain, operation, startTime),
19698
20469
  success: false,
@@ -19704,7 +20475,6 @@ var MemoryHandler = class {
19704
20475
  // src/dispatch/domains/orchestrate.ts
19705
20476
  init_paths();
19706
20477
  init_logger();
19707
- var logger7 = getLogger("domain:orchestrate");
19708
20478
  var OrchestrateHandler = class {
19709
20479
  projectRoot;
19710
20480
  constructor() {
@@ -19839,7 +20609,8 @@ var OrchestrateHandler = class {
19839
20609
  );
19840
20610
  }
19841
20611
  const protocolType = params?.protocolType;
19842
- const result = await orchestrateSpawn(taskId, protocolType, this.projectRoot);
20612
+ const tier = params?.tier;
20613
+ const result = await orchestrateSpawn(taskId, protocolType, this.projectRoot, tier);
19843
20614
  return this.wrapEngineResult(result, "mutate", operation, startTime);
19844
20615
  }
19845
20616
  case "validate": {
@@ -19973,7 +20744,7 @@ var OrchestrateHandler = class {
19973
20744
  }
19974
20745
  handleError(gateway, operation, error, startTime) {
19975
20746
  const message = error instanceof Error ? error.message : String(error);
19976
- logger7.error({ gateway, domain: "orchestrate", operation, err: error }, message);
20747
+ getLogger("domain:orchestrate").error({ gateway, domain: "orchestrate", operation, err: error }, message);
19977
20748
  return this.errorResponse(
19978
20749
  gateway,
19979
20750
  operation,
@@ -19987,7 +20758,6 @@ var OrchestrateHandler = class {
19987
20758
  // src/dispatch/domains/pipeline.ts
19988
20759
  init_paths();
19989
20760
  init_logger();
19990
- var logger8 = getLogger("domain:pipeline");
19991
20761
  var PipelineHandler = class {
19992
20762
  projectRoot;
19993
20763
  constructor() {
@@ -20388,7 +21158,7 @@ var PipelineHandler = class {
20388
21158
  }
20389
21159
  handleError(gateway, operation, error, startTime) {
20390
21160
  const message = error instanceof Error ? error.message : String(error);
20391
- logger8.error({ gateway, domain: "pipeline", operation, err: error }, message);
21161
+ getLogger("domain:pipeline").error({ gateway, domain: "pipeline", operation, err: error }, message);
20392
21162
  return this.errorResponse(
20393
21163
  gateway,
20394
21164
  operation,
@@ -20451,8 +21221,8 @@ import { execFileSync as execFileSync5 } from "node:child_process";
20451
21221
 
20452
21222
  // src/core/issue/template-parser.ts
20453
21223
  init_paths();
20454
- import { existsSync as existsSync42, readFileSync as readFileSync30, readdirSync as readdirSync15, writeFileSync as writeFileSync12 } from "node:fs";
20455
- import { join as join43, basename as basename7 } from "node:path";
21224
+ import { existsSync as existsSync41, readFileSync as readFileSync29, readdirSync as readdirSync15, writeFileSync as writeFileSync11 } from "node:fs";
21225
+ import { join as join42, basename as basename6 } from "node:path";
20456
21226
  var TEMPLATE_DIR = ".github/ISSUE_TEMPLATE";
20457
21227
  var CACHE_FILE = "issue-templates.json";
20458
21228
  var SUBCOMMAND_MAP = {
@@ -20509,10 +21279,10 @@ function extractYamlArray(content, field) {
20509
21279
  return items;
20510
21280
  }
20511
21281
  function parseTemplateFile2(filePath) {
20512
- if (!existsSync42(filePath)) return null;
21282
+ if (!existsSync41(filePath)) return null;
20513
21283
  try {
20514
- const content = readFileSync30(filePath, "utf-8");
20515
- const fileName = basename7(filePath);
21284
+ const content = readFileSync29(filePath, "utf-8");
21285
+ const fileName = basename6(filePath);
20516
21286
  const stem = fileName.replace(/\.ya?ml$/, "");
20517
21287
  const name = extractYamlField(content, "name");
20518
21288
  const description = extractYamlField(content, "description");
@@ -20527,12 +21297,12 @@ function parseTemplateFile2(filePath) {
20527
21297
  }
20528
21298
  function parseIssueTemplates2(projectDir) {
20529
21299
  const dir = projectDir ?? getProjectRoot();
20530
- const templateDir = join43(dir, TEMPLATE_DIR);
20531
- if (!existsSync42(templateDir)) return [];
21300
+ const templateDir = join42(dir, TEMPLATE_DIR);
21301
+ if (!existsSync41(templateDir)) return [];
20532
21302
  const templates = [];
20533
21303
  for (const file of readdirSync15(templateDir)) {
20534
21304
  if (!file.endsWith(".yml") && !file.endsWith(".yaml")) continue;
20535
- const template = parseTemplateFile2(join43(templateDir, file));
21305
+ const template = parseTemplateFile2(join42(templateDir, file));
20536
21306
  if (template) templates.push(template);
20537
21307
  }
20538
21308
  return templates;
@@ -20541,10 +21311,10 @@ function getTemplateConfig(cwd) {
20541
21311
  const projectDir = cwd ?? getProjectRoot();
20542
21312
  const liveTemplates = parseIssueTemplates2(projectDir);
20543
21313
  if (liveTemplates.length > 0) return liveTemplates;
20544
- const cachePath = join43(getCleoDir(cwd), CACHE_FILE);
20545
- if (existsSync42(cachePath)) {
21314
+ const cachePath = join42(getCleoDir(cwd), CACHE_FILE);
21315
+ if (existsSync41(cachePath)) {
20546
21316
  try {
20547
- const cached = JSON.parse(readFileSync30(cachePath, "utf-8"));
21317
+ const cached = JSON.parse(readFileSync29(cachePath, "utf-8"));
20548
21318
  if (cached.templates?.length > 0) return cached.templates;
20549
21319
  } catch {
20550
21320
  }
@@ -20669,7 +21439,6 @@ import {
20669
21439
  injectAll,
20670
21440
  buildInjectionContent
20671
21441
  } from "@cleocode/caamp";
20672
- var logger9 = getLogger("domain:tools");
20673
21442
  var ToolsHandler = class {
20674
21443
  projectRoot;
20675
21444
  constructor() {
@@ -21304,7 +22073,7 @@ var ToolsHandler = class {
21304
22073
  }
21305
22074
  handleError(gateway, domain, operation, error, startTime) {
21306
22075
  const message = error instanceof Error ? error.message : String(error);
21307
- logger9.error({ gateway, domain, operation, err: error }, message);
22076
+ getLogger("domain:tools").error({ gateway, domain, operation, err: error }, message);
21308
22077
  return this.errorResponse(
21309
22078
  gateway,
21310
22079
  domain,
@@ -21318,11 +22087,10 @@ var ToolsHandler = class {
21318
22087
 
21319
22088
  // src/dispatch/domains/nexus.ts
21320
22089
  init_logger();
21321
- var logger10 = getLogger("domain:nexus");
21322
22090
  var NexusHandler = class {
21323
22091
  async query(operation, _params) {
21324
22092
  const startTime = Date.now();
21325
- logger10.warn({ operation }, `Nexus domain not yet implemented: ${operation}`);
22093
+ getLogger("domain:nexus").warn({ operation }, `Nexus domain not yet implemented: ${operation}`);
21326
22094
  return {
21327
22095
  _meta: dispatchMeta("query", "nexus", operation, startTime),
21328
22096
  success: false,
@@ -21331,7 +22099,7 @@ var NexusHandler = class {
21331
22099
  }
21332
22100
  async mutate(operation, _params) {
21333
22101
  const startTime = Date.now();
21334
- logger10.warn({ operation }, `Nexus domain not yet implemented: ${operation}`);
22102
+ getLogger("domain:nexus").warn({ operation }, `Nexus domain not yet implemented: ${operation}`);
21335
22103
  return {
21336
22104
  _meta: dispatchMeta("mutate", "nexus", operation, startTime),
21337
22105
  success: false,
@@ -21349,8 +22117,8 @@ init_logger();
21349
22117
 
21350
22118
  // src/core/sharing/index.ts
21351
22119
  import { readFile as readFile7, writeFile as writeFile4 } from "node:fs/promises";
21352
- import { existsSync as existsSync43, readdirSync as readdirSync16, statSync as statSync5 } from "node:fs";
21353
- import { join as join44, relative } from "node:path";
22120
+ import { existsSync as existsSync42, readdirSync as readdirSync16, statSync as statSync5 } from "node:fs";
22121
+ import { join as join43, relative } from "node:path";
21354
22122
  init_paths();
21355
22123
  var GITIGNORE_START = "# CLEO:SHARING:START - Auto-managed by cleo sharing sync";
21356
22124
  var GITIGNORE_END = "# CLEO:SHARING:END";
@@ -21378,7 +22146,7 @@ function collectCleoFiles(cleoDir) {
21378
22146
  const entries = readdirSync16(dir);
21379
22147
  for (const entry of entries) {
21380
22148
  if (entry === ".git") continue;
21381
- const fullPath = join44(dir, entry);
22149
+ const fullPath = join43(dir, entry);
21382
22150
  const relPath = relative(cleoDir, fullPath);
21383
22151
  try {
21384
22152
  const stat2 = statSync5(fullPath);
@@ -21438,7 +22206,7 @@ function generateGitignoreEntries(sharing) {
21438
22206
  async function syncGitignore(cwd) {
21439
22207
  const config = await loadConfig2(cwd);
21440
22208
  const projectRoot = getProjectRoot(cwd);
21441
- const gitignorePath = join44(projectRoot, ".gitignore");
22209
+ const gitignorePath = join43(projectRoot, ".gitignore");
21442
22210
  const entries = generateGitignoreEntries(config.sharing);
21443
22211
  const managedSection = [
21444
22212
  "",
@@ -21448,7 +22216,7 @@ async function syncGitignore(cwd) {
21448
22216
  ""
21449
22217
  ].join("\n");
21450
22218
  let content = "";
21451
- if (existsSync43(gitignorePath)) {
22219
+ if (existsSync42(gitignorePath)) {
21452
22220
  content = await readFile7(gitignorePath, "utf-8");
21453
22221
  }
21454
22222
  const startIdx = content.indexOf(GITIGNORE_START);
@@ -21473,8 +22241,8 @@ init_data_accessor();
21473
22241
  init_paths();
21474
22242
  import { createHash as createHash6 } from "node:crypto";
21475
22243
  import { readFile as readFile8, writeFile as writeFile5, mkdir as mkdir7 } from "node:fs/promises";
21476
- import { existsSync as existsSync44 } from "node:fs";
21477
- import { join as join45, dirname as dirname13 } from "node:path";
22244
+ import { existsSync as existsSync43 } from "node:fs";
22245
+ import { join as join44, dirname as dirname12 } from "node:path";
21478
22246
  var SNAPSHOT_FORMAT_VERSION = "1.0.0";
21479
22247
  function toSnapshotTask(task) {
21480
22248
  return {
@@ -21527,8 +22295,8 @@ async function exportSnapshot(cwd) {
21527
22295
  };
21528
22296
  }
21529
22297
  async function writeSnapshot(snapshot, outputPath) {
21530
- const dir = dirname13(outputPath);
21531
- if (!existsSync44(dir)) {
22298
+ const dir = dirname12(outputPath);
22299
+ if (!existsSync43(dir)) {
21532
22300
  await mkdir7(dir, { recursive: true });
21533
22301
  }
21534
22302
  await writeFile5(outputPath, JSON.stringify(snapshot, null, 2) + "\n");
@@ -21544,7 +22312,7 @@ async function readSnapshot(inputPath) {
21544
22312
  function getDefaultSnapshotPath(cwd) {
21545
22313
  const cleoDir = getCleoDirAbsolute(cwd);
21546
22314
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
21547
- return join45(cleoDir, "snapshots", `snapshot-${timestamp}.json`);
22315
+ return join44(cleoDir, "snapshots", `snapshot-${timestamp}.json`);
21548
22316
  }
21549
22317
  async function importSnapshot(snapshot, cwd) {
21550
22318
  const accessor = await getAccessor(cwd);
@@ -21611,10 +22379,10 @@ init_paths();
21611
22379
  init_git_checkpoint();
21612
22380
  import { execFile as execFile6 } from "node:child_process";
21613
22381
  import { promisify as promisify6 } from "node:util";
21614
- import { resolve as resolve7 } from "node:path";
22382
+ import { resolve as resolve8 } from "node:path";
21615
22383
  var execFileAsync4 = promisify6(execFile6);
21616
22384
  async function cleoGitExec(args, cleoDir) {
21617
- const abs = resolve7(cleoDir);
22385
+ const abs = resolve8(cleoDir);
21618
22386
  const result = await execFileAsync4("git", args, {
21619
22387
  cwd: abs,
21620
22388
  env: makeCleoGitEnv(cleoDir),
@@ -21801,7 +22569,6 @@ async function getSyncStatus(remote = "origin", cwd) {
21801
22569
  }
21802
22570
 
21803
22571
  // src/dispatch/domains/sharing.ts
21804
- var logger11 = getLogger("domain:sharing");
21805
22572
  var SharingHandler = class {
21806
22573
  projectRoot;
21807
22574
  constructor() {
@@ -21986,7 +22753,7 @@ var SharingHandler = class {
21986
22753
  }
21987
22754
  handleError(gateway, operation, error, startTime) {
21988
22755
  const message = error instanceof Error ? error.message : String(error);
21989
- logger11.error({ gateway, operation, err: error }, message);
22756
+ getLogger("domain:sharing").error({ gateway, operation, err: error }, message);
21990
22757
  return {
21991
22758
  _meta: dispatchMeta(gateway, "sharing", operation, startTime),
21992
22759
  success: false,
@@ -22049,7 +22816,7 @@ function createSessionResolver(cliSessionLookup) {
22049
22816
  // src/dispatch/lib/security.ts
22050
22817
  init_schema();
22051
22818
  init_status_registry();
22052
- import { resolve as resolve8, normalize, relative as relative2, isAbsolute } from "path";
22819
+ import { resolve as resolve9, normalize, relative as relative2, isAbsolute } from "path";
22053
22820
  var SecurityError = class extends Error {
22054
22821
  constructor(message, code = "E_SECURITY_VIOLATION", field) {
22055
22822
  super(message);
@@ -22125,12 +22892,12 @@ function sanitizePath(path, projectRoot) {
22125
22892
  "path"
22126
22893
  );
22127
22894
  }
22128
- const normalizedRoot = resolve8(projectRoot);
22895
+ const normalizedRoot = resolve9(projectRoot);
22129
22896
  let resolvedPath;
22130
22897
  if (isAbsolute(trimmedPath)) {
22131
22898
  resolvedPath = normalize(trimmedPath);
22132
22899
  } else {
22133
- resolvedPath = resolve8(normalizedRoot, trimmedPath);
22900
+ resolvedPath = resolve9(normalizedRoot, trimmedPath);
22134
22901
  }
22135
22902
  const relativePath = relative2(normalizedRoot, resolvedPath);
22136
22903
  if (relativePath.startsWith("..") || isAbsolute(relativePath)) {
@@ -22179,6 +22946,27 @@ function validateEnum(value, allowed, fieldName) {
22179
22946
  }
22180
22947
  var ALL_VALID_STATUSES = [...TASK_STATUSES, ...MANIFEST_STATUSES];
22181
22948
  var VALID_PRIORITIES2 = TASK_PRIORITIES;
22949
+ var ARRAY_PARAMS = /* @__PURE__ */ new Set([
22950
+ "labels",
22951
+ "addLabels",
22952
+ "removeLabels",
22953
+ "depends",
22954
+ "addDepends",
22955
+ "removeDepends",
22956
+ "files",
22957
+ "acceptance",
22958
+ "findings",
22959
+ "sources",
22960
+ "tasks",
22961
+ "removeTasks",
22962
+ "taskIds"
22963
+ ]);
22964
+ function ensureArray(value, separator = ",") {
22965
+ if (value === void 0 || value === null) return void 0;
22966
+ if (Array.isArray(value)) return value.map((v) => typeof v === "string" ? v.trim() : String(v));
22967
+ if (typeof value === "string") return value.split(separator).map((s) => s.trim()).filter(Boolean);
22968
+ return [String(value)];
22969
+ }
22182
22970
  function sanitizeParams(params, projectRoot, context) {
22183
22971
  if (!params) {
22184
22972
  return params;
@@ -22238,6 +23026,11 @@ function sanitizeParams(params, projectRoot, context) {
22238
23026
  continue;
22239
23027
  }
22240
23028
  }
23029
+ for (const key of Object.keys(sanitized)) {
23030
+ if (ARRAY_PARAMS.has(key) && sanitized[key] !== void 0) {
23031
+ sanitized[key] = ensureArray(sanitized[key]);
23032
+ }
23033
+ }
22241
23034
  return sanitized;
22242
23035
  }
22243
23036
 
@@ -24172,6 +24965,142 @@ function createProtocolEnforcement(strictMode = true) {
24172
24965
 
24173
24966
  // src/dispatch/adapters/mcp.ts
24174
24967
  init_audit();
24968
+
24969
+ // src/dispatch/lib/projections.ts
24970
+ var PROJECTIONS = {
24971
+ minimal: {
24972
+ allowedDomains: ["tasks", "session", "admin"],
24973
+ excludeFields: ["notes", "history", "metadata._internal", "auditLog"],
24974
+ maxDepth: 2
24975
+ },
24976
+ standard: {
24977
+ allowedDomains: ["tasks", "session", "admin", "memory", "check", "pipeline", "tools", "validate"],
24978
+ excludeFields: ["metadata._internal", "auditLog"],
24979
+ maxDepth: 4
24980
+ },
24981
+ orchestrator: {
24982
+ allowedDomains: [
24983
+ "tasks",
24984
+ "session",
24985
+ "admin",
24986
+ "memory",
24987
+ "check",
24988
+ "pipeline",
24989
+ "orchestrate",
24990
+ "tools",
24991
+ "sharing",
24992
+ "nexus",
24993
+ "validate",
24994
+ "lifecycle",
24995
+ "release",
24996
+ "system"
24997
+ ],
24998
+ maxDepth: 8
24999
+ }
25000
+ };
25001
+ var VALID_TIERS = /* @__PURE__ */ new Set(["minimal", "standard", "orchestrator"]);
25002
+ function resolveTier(params, sessionScope) {
25003
+ const mvi = params?._mviTier;
25004
+ if (typeof mvi === "string" && VALID_TIERS.has(mvi)) return mvi;
25005
+ if (sessionScope?.type === "epic") return "orchestrator";
25006
+ return "standard";
25007
+ }
25008
+
25009
+ // src/dispatch/middleware/projection.ts
25010
+ function pruneDepth(data, maxDepth, currentDepth = 0) {
25011
+ if (data === null || data === void 0 || typeof data !== "object") {
25012
+ return data;
25013
+ }
25014
+ if (currentDepth >= maxDepth) {
25015
+ if (Array.isArray(data)) {
25016
+ return `[Array(${data.length})]`;
25017
+ }
25018
+ return "[Object]";
25019
+ }
25020
+ if (Array.isArray(data)) {
25021
+ return data.map((item) => pruneDepth(item, maxDepth, currentDepth + 1));
25022
+ }
25023
+ const result = {};
25024
+ for (const [key, value] of Object.entries(data)) {
25025
+ result[key] = pruneDepth(value, maxDepth, currentDepth + 1);
25026
+ }
25027
+ return result;
25028
+ }
25029
+ function applyProjection(data, config) {
25030
+ if (!data || typeof data !== "object") return data;
25031
+ let result = data;
25032
+ if (config.excludeFields?.length) {
25033
+ const obj = { ...data };
25034
+ for (const field of config.excludeFields) {
25035
+ const parts = field.split(".");
25036
+ if (parts.length === 1) {
25037
+ delete obj[parts[0]];
25038
+ } else {
25039
+ let current = obj;
25040
+ for (let i = 0; i < parts.length - 1; i++) {
25041
+ const val = current?.[parts[i]];
25042
+ if (val && typeof val === "object") {
25043
+ current[parts[i]] = { ...val };
25044
+ current = current[parts[i]];
25045
+ } else {
25046
+ current = void 0;
25047
+ break;
25048
+ }
25049
+ }
25050
+ if (current) {
25051
+ delete current[parts[parts.length - 1]];
25052
+ }
25053
+ }
25054
+ }
25055
+ result = obj;
25056
+ }
25057
+ if (config.maxDepth !== void 0) {
25058
+ result = pruneDepth(result, config.maxDepth);
25059
+ }
25060
+ return result;
25061
+ }
25062
+ function createProjectionMiddleware() {
25063
+ return async (req, next) => {
25064
+ const session = getBoundSession();
25065
+ const tier = resolveTier(req.params, session?.scope ?? null);
25066
+ const config = PROJECTIONS[tier];
25067
+ if (req.params) {
25068
+ delete req.params["_mviTier"];
25069
+ }
25070
+ if (req.source === "mcp" && req.domain === "tasks" && req.operation === "list") {
25071
+ if (req.params && req.params["compact"] === void 0) {
25072
+ req.params["compact"] = true;
25073
+ }
25074
+ }
25075
+ if (!config.allowedDomains.includes(req.domain)) {
25076
+ return {
25077
+ _meta: {
25078
+ gateway: req.gateway,
25079
+ domain: req.domain,
25080
+ operation: req.operation,
25081
+ version: "1.0.0",
25082
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
25083
+ duration_ms: 0,
25084
+ source: req.source,
25085
+ requestId: req.requestId
25086
+ },
25087
+ success: false,
25088
+ error: {
25089
+ code: "E_INVALID_OPERATION",
25090
+ exitCode: 2,
25091
+ message: `Operation not available at '${tier}' tier. Domain '${req.domain}' requires a higher tier.`
25092
+ }
25093
+ };
25094
+ }
25095
+ const response = await next();
25096
+ if (response.success && response.data !== void 0) {
25097
+ response.data = applyProjection(response.data, config);
25098
+ }
25099
+ return response;
25100
+ };
25101
+ }
25102
+
25103
+ // src/dispatch/adapters/mcp.ts
24175
25104
  init_paths();
24176
25105
  var _dispatcher = null;
24177
25106
  function initMcpDispatcher(config = {}) {
@@ -24184,6 +25113,8 @@ function initMcpDispatcher(config = {}) {
24184
25113
  createSessionResolver(),
24185
25114
  // T4959: session identity first
24186
25115
  createSanitizer(() => getProjectRoot()),
25116
+ createProjectionMiddleware(),
25117
+ // T5096: MVI tier-based domain/field projection
24187
25118
  createFieldFilter(),
24188
25119
  createRateLimiter(config.rateLimiting),
24189
25120
  createVerificationGates(strictMode),