@cleocode/cleo 2026.2.9 → 2026.3.0

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