@cleocode/core 2026.4.7 → 2026.4.11

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 (903) hide show
  1. package/dist/adapters/adapter-registry.js +64 -0
  2. package/dist/adapters/adapter-registry.js.map +1 -0
  3. package/dist/adapters/discovery.js +83 -0
  4. package/dist/adapters/discovery.js.map +1 -0
  5. package/dist/adapters/index.js +9 -0
  6. package/dist/adapters/index.js.map +1 -0
  7. package/dist/adapters/manager.js +260 -0
  8. package/dist/adapters/manager.js.map +1 -0
  9. package/dist/admin/export-tasks.js +171 -0
  10. package/dist/admin/export-tasks.js.map +1 -0
  11. package/dist/admin/export.js +103 -0
  12. package/dist/admin/export.js.map +1 -0
  13. package/dist/admin/help.js +106 -0
  14. package/dist/admin/help.js.map +1 -0
  15. package/dist/admin/import-tasks.js +182 -0
  16. package/dist/admin/import-tasks.js.map +1 -0
  17. package/dist/admin/import.js +129 -0
  18. package/dist/admin/import.js.map +1 -0
  19. package/dist/admin/index.js +13 -0
  20. package/dist/admin/index.js.map +1 -0
  21. package/dist/adrs/find.js +134 -0
  22. package/dist/adrs/find.js.map +1 -0
  23. package/dist/adrs/index.js +15 -0
  24. package/dist/adrs/index.js.map +1 -0
  25. package/dist/adrs/link-pipeline.js +160 -0
  26. package/dist/adrs/link-pipeline.js.map +1 -0
  27. package/dist/adrs/list.js +43 -0
  28. package/dist/adrs/list.js.map +1 -0
  29. package/dist/adrs/parse.js +51 -0
  30. package/dist/adrs/parse.js.map +1 -0
  31. package/dist/adrs/show.js +22 -0
  32. package/dist/adrs/show.js.map +1 -0
  33. package/dist/adrs/sync.js +188 -0
  34. package/dist/adrs/sync.js.map +1 -0
  35. package/dist/adrs/types.js +9 -0
  36. package/dist/adrs/types.js.map +1 -0
  37. package/dist/adrs/validate.js +57 -0
  38. package/dist/adrs/validate.js.map +1 -0
  39. package/dist/agents/agent-registry.js +288 -0
  40. package/dist/agents/agent-registry.js.map +1 -0
  41. package/dist/agents/agent-schema.d.ts +2 -2
  42. package/dist/agents/agent-schema.js +82 -0
  43. package/dist/agents/agent-schema.js.map +1 -0
  44. package/dist/agents/capacity.js +116 -0
  45. package/dist/agents/capacity.js.map +1 -0
  46. package/dist/agents/execution-learning.js +474 -0
  47. package/dist/agents/execution-learning.js.map +1 -0
  48. package/dist/agents/health-monitor.js +217 -0
  49. package/dist/agents/health-monitor.js.map +1 -0
  50. package/dist/agents/index.js +29 -0
  51. package/dist/agents/index.js.map +1 -0
  52. package/dist/agents/registry.js +314 -0
  53. package/dist/agents/registry.js.map +1 -0
  54. package/dist/agents/retry.js +215 -0
  55. package/dist/agents/retry.js.map +1 -0
  56. package/dist/audit-prune.js +94 -0
  57. package/dist/audit-prune.js.map +1 -0
  58. package/dist/audit.js +68 -0
  59. package/dist/audit.js.map +1 -0
  60. package/dist/backfill/index.js +229 -0
  61. package/dist/backfill/index.js.map +1 -0
  62. package/dist/bootstrap.js +344 -0
  63. package/dist/bootstrap.js.map +1 -0
  64. package/dist/caamp/adapter.js +259 -0
  65. package/dist/caamp/adapter.js.map +1 -0
  66. package/dist/caamp/capability-check.js +38 -0
  67. package/dist/caamp/capability-check.js.map +1 -0
  68. package/dist/caamp/index.js +21 -0
  69. package/dist/caamp/index.js.map +1 -0
  70. package/dist/caamp-init.js +16 -0
  71. package/dist/caamp-init.js.map +1 -0
  72. package/dist/cleo.js +322 -0
  73. package/dist/cleo.js.map +1 -0
  74. package/dist/code/index.js +10 -0
  75. package/dist/code/index.js.map +1 -0
  76. package/dist/code/outline.js +165 -0
  77. package/dist/code/outline.js.map +1 -0
  78. package/dist/code/parser.js +295 -0
  79. package/dist/code/parser.js.map +1 -0
  80. package/dist/code/search.js +135 -0
  81. package/dist/code/search.js.map +1 -0
  82. package/dist/code/unfold.js +155 -0
  83. package/dist/code/unfold.js.map +1 -0
  84. package/dist/codebase-map/analyzers/architecture.js +130 -0
  85. package/dist/codebase-map/analyzers/architecture.js.map +1 -0
  86. package/dist/codebase-map/analyzers/concerns.js +122 -0
  87. package/dist/codebase-map/analyzers/concerns.js.map +1 -0
  88. package/dist/codebase-map/analyzers/conventions.js +149 -0
  89. package/dist/codebase-map/analyzers/conventions.js.map +1 -0
  90. package/dist/codebase-map/analyzers/integrations.js +108 -0
  91. package/dist/codebase-map/analyzers/integrations.js.map +1 -0
  92. package/dist/codebase-map/analyzers/stack.js +117 -0
  93. package/dist/codebase-map/analyzers/stack.js.map +1 -0
  94. package/dist/codebase-map/analyzers/structure.js +137 -0
  95. package/dist/codebase-map/analyzers/structure.js.map +1 -0
  96. package/dist/codebase-map/analyzers/testing.js +118 -0
  97. package/dist/codebase-map/analyzers/testing.js.map +1 -0
  98. package/dist/codebase-map/index.js +57 -0
  99. package/dist/codebase-map/index.js.map +1 -0
  100. package/dist/codebase-map/store.js +122 -0
  101. package/dist/codebase-map/store.js.map +1 -0
  102. package/dist/codebase-map/summary.js +152 -0
  103. package/dist/codebase-map/summary.js.map +1 -0
  104. package/dist/compliance/index.js +288 -0
  105. package/dist/compliance/index.js.map +1 -0
  106. package/dist/compliance/protocol-enforcement.js +332 -0
  107. package/dist/compliance/protocol-enforcement.js.map +1 -0
  108. package/dist/compliance/protocol-rules.js +786 -0
  109. package/dist/compliance/protocol-rules.js.map +1 -0
  110. package/dist/compliance/protocol-types.js +79 -0
  111. package/dist/compliance/protocol-types.js.map +1 -0
  112. package/dist/compliance/store.js +53 -0
  113. package/dist/compliance/store.js.map +1 -0
  114. package/dist/conduit/conduit-client.js +107 -0
  115. package/dist/conduit/conduit-client.js.map +1 -0
  116. package/dist/conduit/factory.js +52 -0
  117. package/dist/conduit/factory.js.map +1 -0
  118. package/dist/conduit/http-transport.js +155 -0
  119. package/dist/conduit/http-transport.js.map +1 -0
  120. package/dist/conduit/index.js +15 -0
  121. package/dist/conduit/index.js.map +1 -0
  122. package/dist/conduit/local-transport.js +245 -0
  123. package/dist/conduit/local-transport.js.map +1 -0
  124. package/dist/conduit/sse-transport.js +299 -0
  125. package/dist/conduit/sse-transport.js.map +1 -0
  126. package/dist/config/build-config.js +29 -0
  127. package/dist/config/build-config.js.map +1 -0
  128. package/dist/config.js +401 -0
  129. package/dist/config.js.map +1 -0
  130. package/dist/constants.js +18 -0
  131. package/dist/constants.js.map +1 -0
  132. package/dist/context/index.js +137 -0
  133. package/dist/context/index.js.map +1 -0
  134. package/dist/crypto/credentials.js +191 -0
  135. package/dist/crypto/credentials.js.map +1 -0
  136. package/dist/discovery.js +182 -0
  137. package/dist/discovery.js.map +1 -0
  138. package/dist/engine-result.js +12 -0
  139. package/dist/engine-result.js.map +1 -0
  140. package/dist/error-catalog.js +404 -0
  141. package/dist/error-catalog.js.map +1 -0
  142. package/dist/error-registry.js +393 -0
  143. package/dist/error-registry.js.map +1 -0
  144. package/dist/errors.js +167 -0
  145. package/dist/errors.js.map +1 -0
  146. package/dist/hooks/handlers/agent-hooks.js +106 -0
  147. package/dist/hooks/handlers/agent-hooks.js.map +1 -0
  148. package/dist/hooks/handlers/context-hooks.js +111 -0
  149. package/dist/hooks/handlers/context-hooks.js.map +1 -0
  150. package/dist/hooks/handlers/error-hooks.js +52 -0
  151. package/dist/hooks/handlers/error-hooks.js.map +1 -0
  152. package/dist/hooks/handlers/file-hooks.js +104 -0
  153. package/dist/hooks/handlers/file-hooks.js.map +1 -0
  154. package/dist/hooks/handlers/handler-helpers.js +61 -0
  155. package/dist/hooks/handlers/handler-helpers.js.map +1 -0
  156. package/dist/hooks/handlers/index.js +28 -0
  157. package/dist/hooks/handlers/index.js.map +1 -0
  158. package/dist/hooks/handlers/memory-bridge-refresh.js +42 -0
  159. package/dist/hooks/handlers/memory-bridge-refresh.js.map +1 -0
  160. package/dist/hooks/handlers/notification-hooks.js +62 -0
  161. package/dist/hooks/handlers/notification-hooks.js.map +1 -0
  162. package/dist/hooks/handlers/session-hooks.d.ts +21 -0
  163. package/dist/hooks/handlers/session-hooks.d.ts.map +1 -1
  164. package/dist/hooks/handlers/session-hooks.js +142 -0
  165. package/dist/hooks/handlers/session-hooks.js.map +1 -0
  166. package/dist/hooks/handlers/task-hooks.js +65 -0
  167. package/dist/hooks/handlers/task-hooks.js.map +1 -0
  168. package/dist/hooks/handlers/work-capture-hooks.js +165 -0
  169. package/dist/hooks/handlers/work-capture-hooks.js.map +1 -0
  170. package/dist/hooks/index.js +13 -0
  171. package/dist/hooks/index.js.map +1 -0
  172. package/dist/hooks/payload-schemas.d.ts +2 -2
  173. package/dist/hooks/payload-schemas.js +220 -0
  174. package/dist/hooks/payload-schemas.js.map +1 -0
  175. package/dist/hooks/provider-hooks.js +66 -0
  176. package/dist/hooks/provider-hooks.js.map +1 -0
  177. package/dist/hooks/registry.js +229 -0
  178. package/dist/hooks/registry.js.map +1 -0
  179. package/dist/hooks/types.js +66 -0
  180. package/dist/hooks/types.js.map +1 -0
  181. package/dist/hooks.js +136 -0
  182. package/dist/hooks.js.map +1 -0
  183. package/dist/index.js +3361 -3095
  184. package/dist/index.js.map +4 -4
  185. package/dist/init.js +852 -0
  186. package/dist/init.js.map +1 -0
  187. package/dist/inject/index.js +82 -0
  188. package/dist/inject/index.js.map +1 -0
  189. package/dist/injection.js +343 -0
  190. package/dist/injection.js.map +1 -0
  191. package/dist/intelligence/adaptive-validation.js +497 -0
  192. package/dist/intelligence/adaptive-validation.js.map +1 -0
  193. package/dist/intelligence/impact.js +675 -0
  194. package/dist/intelligence/impact.js.map +1 -0
  195. package/dist/intelligence/index.js +22 -0
  196. package/dist/intelligence/index.js.map +1 -0
  197. package/dist/intelligence/patterns.js +492 -0
  198. package/dist/intelligence/patterns.js.map +1 -0
  199. package/dist/intelligence/prediction.js +499 -0
  200. package/dist/intelligence/prediction.js.map +1 -0
  201. package/dist/intelligence/types.js +13 -0
  202. package/dist/intelligence/types.js.map +1 -0
  203. package/dist/internal.d.ts +7 -2
  204. package/dist/internal.d.ts.map +1 -1
  205. package/dist/internal.js +299 -0
  206. package/dist/internal.js.map +1 -0
  207. package/dist/issue/create.js +121 -0
  208. package/dist/issue/create.js.map +1 -0
  209. package/dist/issue/diagnostics.js +59 -0
  210. package/dist/issue/diagnostics.js.map +1 -0
  211. package/dist/issue/index.js +10 -0
  212. package/dist/issue/index.js.map +1 -0
  213. package/dist/issue/template-parser.js +267 -0
  214. package/dist/issue/template-parser.js.map +1 -0
  215. package/dist/json-schema-validator.js +76 -0
  216. package/dist/json-schema-validator.js.map +1 -0
  217. package/dist/lib/index.js +11 -0
  218. package/dist/lib/index.js.map +1 -0
  219. package/dist/lib/retry.js +152 -0
  220. package/dist/lib/retry.js.map +1 -0
  221. package/dist/lib/tree-sitter-languages.js +71 -0
  222. package/dist/lib/tree-sitter-languages.js.map +1 -0
  223. package/dist/lifecycle/chain-composition.js +152 -0
  224. package/dist/lifecycle/chain-composition.js.map +1 -0
  225. package/dist/lifecycle/chain-store.js +246 -0
  226. package/dist/lifecycle/chain-store.js.map +1 -0
  227. package/dist/lifecycle/consolidate-rcasd.js +352 -0
  228. package/dist/lifecycle/consolidate-rcasd.js.map +1 -0
  229. package/dist/lifecycle/default-chain.js +176 -0
  230. package/dist/lifecycle/default-chain.js.map +1 -0
  231. package/dist/lifecycle/evidence.js +180 -0
  232. package/dist/lifecycle/evidence.js.map +1 -0
  233. package/dist/lifecycle/frontmatter.js +363 -0
  234. package/dist/lifecycle/frontmatter.js.map +1 -0
  235. package/dist/lifecycle/index.js +756 -0
  236. package/dist/lifecycle/index.js.map +1 -0
  237. package/dist/lifecycle/pipeline.js +656 -0
  238. package/dist/lifecycle/pipeline.js.map +1 -0
  239. package/dist/lifecycle/rcasd-index.js +326 -0
  240. package/dist/lifecycle/rcasd-index.js.map +1 -0
  241. package/dist/lifecycle/rcasd-paths.js +220 -0
  242. package/dist/lifecycle/rcasd-paths.js.map +1 -0
  243. package/dist/lifecycle/resume.js +864 -0
  244. package/dist/lifecycle/resume.js.map +1 -0
  245. package/dist/lifecycle/stage-artifacts.js +94 -0
  246. package/dist/lifecycle/stage-artifacts.js.map +1 -0
  247. package/dist/lifecycle/stage-guidance.js +234 -0
  248. package/dist/lifecycle/stage-guidance.js.map +1 -0
  249. package/dist/lifecycle/stages.js +534 -0
  250. package/dist/lifecycle/stages.js.map +1 -0
  251. package/dist/lifecycle/state-machine.js +516 -0
  252. package/dist/lifecycle/state-machine.js.map +1 -0
  253. package/dist/lifecycle/tessera-engine.js +249 -0
  254. package/dist/lifecycle/tessera-engine.js.map +1 -0
  255. package/dist/logger.js +140 -0
  256. package/dist/logger.js.map +1 -0
  257. package/dist/memory/auto-extract.js +177 -0
  258. package/dist/memory/auto-extract.js.map +1 -0
  259. package/dist/memory/brain-embedding.js +66 -0
  260. package/dist/memory/brain-embedding.js.map +1 -0
  261. package/dist/memory/brain-lifecycle.js +298 -0
  262. package/dist/memory/brain-lifecycle.js.map +1 -0
  263. package/dist/memory/brain-links.js +161 -0
  264. package/dist/memory/brain-links.js.map +1 -0
  265. package/dist/memory/brain-maintenance.js +114 -0
  266. package/dist/memory/brain-maintenance.js.map +1 -0
  267. package/dist/memory/brain-migration.js +149 -0
  268. package/dist/memory/brain-migration.js.map +1 -0
  269. package/dist/memory/brain-reasoning.js +215 -0
  270. package/dist/memory/brain-reasoning.js.map +1 -0
  271. package/dist/memory/brain-retrieval.js +542 -0
  272. package/dist/memory/brain-retrieval.js.map +1 -0
  273. package/dist/memory/brain-row-types.js +10 -0
  274. package/dist/memory/brain-row-types.js.map +1 -0
  275. package/dist/memory/brain-search.js +519 -0
  276. package/dist/memory/brain-search.js.map +1 -0
  277. package/dist/memory/brain-similarity.js +145 -0
  278. package/dist/memory/brain-similarity.js.map +1 -0
  279. package/dist/memory/claude-mem-migration.js +277 -0
  280. package/dist/memory/claude-mem-migration.js.map +1 -0
  281. package/dist/memory/decisions.js +162 -0
  282. package/dist/memory/decisions.js.map +1 -0
  283. package/dist/memory/embedding-local.js +97 -0
  284. package/dist/memory/embedding-local.js.map +1 -0
  285. package/dist/memory/embedding-queue.js +271 -0
  286. package/dist/memory/embedding-queue.js.map +1 -0
  287. package/dist/memory/embedding-worker.js +58 -0
  288. package/dist/memory/embedding-worker.js.map +1 -0
  289. package/dist/memory/engine-compat.js +1397 -0
  290. package/dist/memory/engine-compat.js.map +1 -0
  291. package/dist/memory/index.js +1140 -0
  292. package/dist/memory/index.js.map +1 -0
  293. package/dist/memory/learnings.d.ts +4 -4
  294. package/dist/memory/learnings.js +121 -0
  295. package/dist/memory/learnings.js.map +1 -0
  296. package/dist/memory/memory-bridge.js +370 -0
  297. package/dist/memory/memory-bridge.js.map +1 -0
  298. package/dist/memory/patterns.d.ts +6 -6
  299. package/dist/memory/patterns.js +122 -0
  300. package/dist/memory/patterns.js.map +1 -0
  301. package/dist/memory/pipeline-manifest-sqlite.js +975 -0
  302. package/dist/memory/pipeline-manifest-sqlite.js.map +1 -0
  303. package/dist/memory/session-memory.js +331 -0
  304. package/dist/memory/session-memory.js.map +1 -0
  305. package/dist/metrics/ab-test.js +260 -0
  306. package/dist/metrics/ab-test.js.map +1 -0
  307. package/dist/metrics/aggregation.js +363 -0
  308. package/dist/metrics/aggregation.js.map +1 -0
  309. package/dist/metrics/common.js +64 -0
  310. package/dist/metrics/common.js.map +1 -0
  311. package/dist/metrics/enums.js +78 -0
  312. package/dist/metrics/enums.js.map +1 -0
  313. package/dist/metrics/index.js +19 -0
  314. package/dist/metrics/index.js.map +1 -0
  315. package/dist/metrics/model-provider-registry.js +88 -0
  316. package/dist/metrics/model-provider-registry.js.map +1 -0
  317. package/dist/metrics/otel-integration.js +263 -0
  318. package/dist/metrics/otel-integration.js.map +1 -0
  319. package/dist/metrics/provider-detection.js +103 -0
  320. package/dist/metrics/provider-detection.js.map +1 -0
  321. package/dist/metrics/token-estimation.js +253 -0
  322. package/dist/metrics/token-estimation.js.map +1 -0
  323. package/dist/metrics/token-service.js +450 -0
  324. package/dist/metrics/token-service.js.map +1 -0
  325. package/dist/migration/agent-outputs.js +316 -0
  326. package/dist/migration/agent-outputs.js.map +1 -0
  327. package/dist/migration/checksum.js +92 -0
  328. package/dist/migration/checksum.js.map +1 -0
  329. package/dist/migration/index.js +282 -0
  330. package/dist/migration/index.js.map +1 -0
  331. package/dist/migration/logger.js +360 -0
  332. package/dist/migration/logger.js.map +1 -0
  333. package/dist/migration/preflight.js +9 -0
  334. package/dist/migration/preflight.js.map +1 -0
  335. package/dist/migration/state.js +421 -0
  336. package/dist/migration/state.js.map +1 -0
  337. package/dist/migration/validate.js +241 -0
  338. package/dist/migration/validate.js.map +1 -0
  339. package/dist/mvi-helpers.js +74 -0
  340. package/dist/mvi-helpers.js.map +1 -0
  341. package/dist/nexus/deps.js +375 -0
  342. package/dist/nexus/deps.js.map +1 -0
  343. package/dist/nexus/discover.js +288 -0
  344. package/dist/nexus/discover.js.map +1 -0
  345. package/dist/nexus/hash.js +10 -0
  346. package/dist/nexus/hash.js.map +1 -0
  347. package/dist/nexus/index.js +40 -0
  348. package/dist/nexus/index.js.map +1 -0
  349. package/dist/nexus/migrate-json-to-sqlite.js +115 -0
  350. package/dist/nexus/migrate-json-to-sqlite.js.map +1 -0
  351. package/dist/nexus/permissions.js +105 -0
  352. package/dist/nexus/permissions.js.map +1 -0
  353. package/dist/nexus/query.js +175 -0
  354. package/dist/nexus/query.js.map +1 -0
  355. package/dist/nexus/registry.js +584 -0
  356. package/dist/nexus/registry.js.map +1 -0
  357. package/dist/nexus/sharing/index.js +288 -0
  358. package/dist/nexus/sharing/index.js.map +1 -0
  359. package/dist/nexus/transfer-types.js +8 -0
  360. package/dist/nexus/transfer-types.js.map +1 -0
  361. package/dist/nexus/transfer.js +263 -0
  362. package/dist/nexus/transfer.js.map +1 -0
  363. package/dist/nexus/workspace.js +355 -0
  364. package/dist/nexus/workspace.js.map +1 -0
  365. package/dist/observability/index.js +103 -0
  366. package/dist/observability/index.js.map +1 -0
  367. package/dist/observability/log-filter.js +63 -0
  368. package/dist/observability/log-filter.js.map +1 -0
  369. package/dist/observability/log-parser.js +99 -0
  370. package/dist/observability/log-parser.js.map +1 -0
  371. package/dist/observability/log-reader.js +139 -0
  372. package/dist/observability/log-reader.js.map +1 -0
  373. package/dist/observability/types.js +19 -0
  374. package/dist/observability/types.js.map +1 -0
  375. package/dist/orchestration/analyze.js +107 -0
  376. package/dist/orchestration/analyze.js.map +1 -0
  377. package/dist/orchestration/bootstrap.js +132 -0
  378. package/dist/orchestration/bootstrap.js.map +1 -0
  379. package/dist/orchestration/context.js +56 -0
  380. package/dist/orchestration/context.js.map +1 -0
  381. package/dist/orchestration/critical-path.js +100 -0
  382. package/dist/orchestration/critical-path.js.map +1 -0
  383. package/dist/orchestration/hierarchy.js +183 -0
  384. package/dist/orchestration/hierarchy.js.map +1 -0
  385. package/dist/orchestration/index.js +287 -0
  386. package/dist/orchestration/index.js.map +1 -0
  387. package/dist/orchestration/parallel.js +89 -0
  388. package/dist/orchestration/parallel.js.map +1 -0
  389. package/dist/orchestration/protocol-validators.js +815 -0
  390. package/dist/orchestration/protocol-validators.js.map +1 -0
  391. package/dist/orchestration/skill-ops.js +98 -0
  392. package/dist/orchestration/skill-ops.js.map +1 -0
  393. package/dist/orchestration/status.js +107 -0
  394. package/dist/orchestration/status.js.map +1 -0
  395. package/dist/orchestration/unblock.js +103 -0
  396. package/dist/orchestration/unblock.js.map +1 -0
  397. package/dist/orchestration/validate-spawn.js +67 -0
  398. package/dist/orchestration/validate-spawn.js.map +1 -0
  399. package/dist/orchestration/waves.js +86 -0
  400. package/dist/orchestration/waves.js.map +1 -0
  401. package/dist/otel/index.js +163 -0
  402. package/dist/otel/index.js.map +1 -0
  403. package/dist/output.js +164 -0
  404. package/dist/output.js.map +1 -0
  405. package/dist/pagination.js +64 -0
  406. package/dist/pagination.js.map +1 -0
  407. package/dist/paths.d.ts +39 -9
  408. package/dist/paths.d.ts.map +1 -1
  409. package/dist/paths.js +776 -0
  410. package/dist/paths.js.map +1 -0
  411. package/dist/phases/deps.js +372 -0
  412. package/dist/phases/deps.js.map +1 -0
  413. package/dist/phases/index.js +349 -0
  414. package/dist/phases/index.js.map +1 -0
  415. package/dist/pipeline/index.js +10 -0
  416. package/dist/pipeline/index.js.map +1 -0
  417. package/dist/pipeline/phase.js +45 -0
  418. package/dist/pipeline/phase.js.map +1 -0
  419. package/dist/platform.js +211 -0
  420. package/dist/platform.js.map +1 -0
  421. package/dist/project-info.js +84 -0
  422. package/dist/project-info.js.map +1 -0
  423. package/dist/reconciliation/index.js +10 -0
  424. package/dist/reconciliation/index.js.map +1 -0
  425. package/dist/reconciliation/link-store.js +129 -0
  426. package/dist/reconciliation/link-store.js.map +1 -0
  427. package/dist/reconciliation/reconciliation-engine.js +298 -0
  428. package/dist/reconciliation/reconciliation-engine.js.map +1 -0
  429. package/dist/release/artifacts.js +427 -0
  430. package/dist/release/artifacts.js.map +1 -0
  431. package/dist/release/changelog-writer.js +151 -0
  432. package/dist/release/changelog-writer.js.map +1 -0
  433. package/dist/release/channel.js +144 -0
  434. package/dist/release/channel.js.map +1 -0
  435. package/dist/release/ci.js +166 -0
  436. package/dist/release/ci.js.map +1 -0
  437. package/dist/release/github-pr.js +225 -0
  438. package/dist/release/github-pr.js.map +1 -0
  439. package/dist/release/guards.js +116 -0
  440. package/dist/release/guards.js.map +1 -0
  441. package/dist/release/index.js +22 -0
  442. package/dist/release/index.js.map +1 -0
  443. package/dist/release/release-config.js +158 -0
  444. package/dist/release/release-config.js.map +1 -0
  445. package/dist/release/release-manifest.js +1019 -0
  446. package/dist/release/release-manifest.js.map +1 -0
  447. package/dist/release/version-bump.js +255 -0
  448. package/dist/release/version-bump.js.map +1 -0
  449. package/dist/remote/index.js +257 -0
  450. package/dist/remote/index.js.map +1 -0
  451. package/dist/repair.js +130 -0
  452. package/dist/repair.js.map +1 -0
  453. package/dist/research/index.js +2 -0
  454. package/dist/research/index.js.map +1 -0
  455. package/dist/roadmap/index.js +59 -0
  456. package/dist/roadmap/index.js.map +1 -0
  457. package/dist/routing/capability-matrix.js +1556 -0
  458. package/dist/routing/capability-matrix.js.map +1 -0
  459. package/dist/routing/index.js +9 -0
  460. package/dist/routing/index.js.map +1 -0
  461. package/dist/scaffold.d.ts +15 -2
  462. package/dist/scaffold.d.ts.map +1 -1
  463. package/dist/scaffold.js +1759 -0
  464. package/dist/scaffold.js.map +1 -0
  465. package/dist/schema-management.js +295 -0
  466. package/dist/schema-management.js.map +1 -0
  467. package/dist/security/index.js +9 -0
  468. package/dist/security/index.js.map +1 -0
  469. package/dist/security/input-sanitization.js +321 -0
  470. package/dist/security/input-sanitization.js.map +1 -0
  471. package/dist/sequence/index.js +295 -0
  472. package/dist/sequence/index.js.map +1 -0
  473. package/dist/sessions/assumptions.js +54 -0
  474. package/dist/sessions/assumptions.js.map +1 -0
  475. package/dist/sessions/briefing.js +377 -0
  476. package/dist/sessions/briefing.js.map +1 -0
  477. package/dist/sessions/context-alert.js +222 -0
  478. package/dist/sessions/context-alert.js.map +1 -0
  479. package/dist/sessions/context-inject.js +61 -0
  480. package/dist/sessions/context-inject.js.map +1 -0
  481. package/dist/sessions/context-monitor.js +98 -0
  482. package/dist/sessions/context-monitor.js.map +1 -0
  483. package/dist/sessions/decisions.js +65 -0
  484. package/dist/sessions/decisions.js.map +1 -0
  485. package/dist/sessions/find.js +65 -0
  486. package/dist/sessions/find.js.map +1 -0
  487. package/dist/sessions/handoff.js +328 -0
  488. package/dist/sessions/handoff.js.map +1 -0
  489. package/dist/sessions/hitl-warnings.js +254 -0
  490. package/dist/sessions/hitl-warnings.js.map +1 -0
  491. package/dist/sessions/index.js +327 -0
  492. package/dist/sessions/index.js.map +1 -0
  493. package/dist/sessions/session-archive.js +40 -0
  494. package/dist/sessions/session-archive.js.map +1 -0
  495. package/dist/sessions/session-cleanup.js +59 -0
  496. package/dist/sessions/session-cleanup.js.map +1 -0
  497. package/dist/sessions/session-drift.js +134 -0
  498. package/dist/sessions/session-drift.js.map +1 -0
  499. package/dist/sessions/session-enforcement.js +144 -0
  500. package/dist/sessions/session-enforcement.js.map +1 -0
  501. package/dist/sessions/session-grade.js +253 -0
  502. package/dist/sessions/session-grade.js.map +1 -0
  503. package/dist/sessions/session-history.js +42 -0
  504. package/dist/sessions/session-history.js.map +1 -0
  505. package/dist/sessions/session-id.js +81 -0
  506. package/dist/sessions/session-id.js.map +1 -0
  507. package/dist/sessions/session-memory-bridge.js +52 -0
  508. package/dist/sessions/session-memory-bridge.js.map +1 -0
  509. package/dist/sessions/session-show.js +24 -0
  510. package/dist/sessions/session-show.js.map +1 -0
  511. package/dist/sessions/session-stats.js +69 -0
  512. package/dist/sessions/session-stats.js.map +1 -0
  513. package/dist/sessions/session-suspend.js +39 -0
  514. package/dist/sessions/session-suspend.js.map +1 -0
  515. package/dist/sessions/session-switch.js +51 -0
  516. package/dist/sessions/session-switch.js.map +1 -0
  517. package/dist/sessions/session-view.js +76 -0
  518. package/dist/sessions/session-view.js.map +1 -0
  519. package/dist/sessions/snapshot.js +213 -0
  520. package/dist/sessions/snapshot.js.map +1 -0
  521. package/dist/sessions/statusline-setup.js +85 -0
  522. package/dist/sessions/statusline-setup.js.map +1 -0
  523. package/dist/sessions/types.js +8 -0
  524. package/dist/sessions/types.js.map +1 -0
  525. package/dist/skills/agents/config.js +94 -0
  526. package/dist/skills/agents/config.js.map +1 -0
  527. package/dist/skills/agents/install.js +116 -0
  528. package/dist/skills/agents/install.js.map +1 -0
  529. package/dist/skills/agents/registry.js +161 -0
  530. package/dist/skills/agents/registry.js.map +1 -0
  531. package/dist/skills/discovery.js +333 -0
  532. package/dist/skills/discovery.js.map +1 -0
  533. package/dist/skills/dispatch.js +347 -0
  534. package/dist/skills/dispatch.js.map +1 -0
  535. package/dist/skills/dynamic-skill-generator.js +87 -0
  536. package/dist/skills/dynamic-skill-generator.js.map +1 -0
  537. package/dist/skills/index.js +44 -0
  538. package/dist/skills/index.js.map +1 -0
  539. package/dist/skills/injection/subagent.js +195 -0
  540. package/dist/skills/injection/subagent.js.map +1 -0
  541. package/dist/skills/injection/token.js +260 -0
  542. package/dist/skills/injection/token.js.map +1 -0
  543. package/dist/skills/install.js +40 -0
  544. package/dist/skills/install.js.map +1 -0
  545. package/dist/skills/manifests/contribution.js +175 -0
  546. package/dist/skills/manifests/contribution.js.map +1 -0
  547. package/dist/skills/manifests/research.js +281 -0
  548. package/dist/skills/manifests/research.js.map +1 -0
  549. package/dist/skills/manifests/resolver.js +146 -0
  550. package/dist/skills/manifests/resolver.js.map +1 -0
  551. package/dist/skills/marketplace.js +90 -0
  552. package/dist/skills/marketplace.js.map +1 -0
  553. package/dist/skills/orchestrator/spawn.js +178 -0
  554. package/dist/skills/orchestrator/spawn.js.map +1 -0
  555. package/dist/skills/orchestrator/startup.js +451 -0
  556. package/dist/skills/orchestrator/startup.js.map +1 -0
  557. package/dist/skills/orchestrator/validator.js +301 -0
  558. package/dist/skills/orchestrator/validator.js.map +1 -0
  559. package/dist/skills/precedence-integration.js +73 -0
  560. package/dist/skills/precedence-integration.js.map +1 -0
  561. package/dist/skills/precedence-types.js +16 -0
  562. package/dist/skills/precedence-types.js.map +1 -0
  563. package/dist/skills/routing-table.js +63 -0
  564. package/dist/skills/routing-table.js.map +1 -0
  565. package/dist/skills/skill-paths.js +217 -0
  566. package/dist/skills/skill-paths.js.map +1 -0
  567. package/dist/skills/test-utility.js +55 -0
  568. package/dist/skills/test-utility.js.map +1 -0
  569. package/dist/skills/types.js +118 -0
  570. package/dist/skills/types.js.map +1 -0
  571. package/dist/skills/validation.js +183 -0
  572. package/dist/skills/validation.js.map +1 -0
  573. package/dist/skills/version.js +57 -0
  574. package/dist/skills/version.js.map +1 -0
  575. package/dist/snapshot/index.js +188 -0
  576. package/dist/snapshot/index.js.map +1 -0
  577. package/dist/spawn/adapter-registry.js +246 -0
  578. package/dist/spawn/adapter-registry.js.map +1 -0
  579. package/dist/spawn/index.js +10 -0
  580. package/dist/spawn/index.js.map +1 -0
  581. package/dist/stats/index.js +343 -0
  582. package/dist/stats/index.js.map +1 -0
  583. package/dist/stats/workflow-telemetry.js +400 -0
  584. package/dist/stats/workflow-telemetry.js.map +1 -0
  585. package/dist/sticky/archive.js +47 -0
  586. package/dist/sticky/archive.js.map +1 -0
  587. package/dist/sticky/convert.js +235 -0
  588. package/dist/sticky/convert.js.map +1 -0
  589. package/dist/sticky/create.js +48 -0
  590. package/dist/sticky/create.js.map +1 -0
  591. package/dist/sticky/id.js +35 -0
  592. package/dist/sticky/id.js.map +1 -0
  593. package/dist/sticky/index.js +16 -0
  594. package/dist/sticky/index.js.map +1 -0
  595. package/dist/sticky/list.js +44 -0
  596. package/dist/sticky/list.js.map +1 -0
  597. package/dist/sticky/purge.js +45 -0
  598. package/dist/sticky/purge.js.map +1 -0
  599. package/dist/sticky/show.js +42 -0
  600. package/dist/sticky/show.js.map +1 -0
  601. package/dist/sticky/types.js +10 -0
  602. package/dist/sticky/types.js.map +1 -0
  603. package/dist/store/agent-registry-accessor.js +265 -0
  604. package/dist/store/agent-registry-accessor.js.map +1 -0
  605. package/dist/store/atomic.js +167 -0
  606. package/dist/store/atomic.js.map +1 -0
  607. package/dist/store/backup.js +94 -0
  608. package/dist/store/backup.js.map +1 -0
  609. package/dist/store/brain-accessor.js +397 -0
  610. package/dist/store/brain-accessor.js.map +1 -0
  611. package/dist/store/brain-schema.d.ts +8 -8
  612. package/dist/store/brain-schema.js +215 -0
  613. package/dist/store/brain-schema.js.map +1 -0
  614. package/dist/store/brain-sqlite.js +222 -0
  615. package/dist/store/brain-sqlite.js.map +1 -0
  616. package/dist/store/cache.js +168 -0
  617. package/dist/store/cache.js.map +1 -0
  618. package/dist/store/chain-schema.js +51 -0
  619. package/dist/store/chain-schema.js.map +1 -0
  620. package/dist/store/cleanup-legacy.d.ts +128 -0
  621. package/dist/store/cleanup-legacy.d.ts.map +1 -0
  622. package/dist/store/converters.js +124 -0
  623. package/dist/store/converters.js.map +1 -0
  624. package/dist/store/cross-db-cleanup.js +319 -0
  625. package/dist/store/cross-db-cleanup.js.map +1 -0
  626. package/dist/store/data-accessor.js +26 -0
  627. package/dist/store/data-accessor.js.map +1 -0
  628. package/dist/store/data-safety-central.js +269 -0
  629. package/dist/store/data-safety-central.js.map +1 -0
  630. package/dist/store/data-safety.js +274 -0
  631. package/dist/store/data-safety.js.map +1 -0
  632. package/dist/store/db-helpers.js +224 -0
  633. package/dist/store/db-helpers.js.map +1 -0
  634. package/dist/store/export.js +155 -0
  635. package/dist/store/export.js.map +1 -0
  636. package/dist/store/file-utils.js +270 -0
  637. package/dist/store/file-utils.js.map +1 -0
  638. package/dist/store/git-checkpoint.js +365 -0
  639. package/dist/store/git-checkpoint.js.map +1 -0
  640. package/dist/store/import-logging.js +139 -0
  641. package/dist/store/import-logging.js.map +1 -0
  642. package/dist/store/import-remap.js +145 -0
  643. package/dist/store/import-remap.js.map +1 -0
  644. package/dist/store/import-sort.js +121 -0
  645. package/dist/store/import-sort.js.map +1 -0
  646. package/dist/store/index.d.ts +1 -0
  647. package/dist/store/index.d.ts.map +1 -1
  648. package/dist/store/index.js +29 -0
  649. package/dist/store/index.js.map +1 -0
  650. package/dist/store/json.js +208 -0
  651. package/dist/store/json.js.map +1 -0
  652. package/dist/store/lifecycle-store.js +249 -0
  653. package/dist/store/lifecycle-store.js.map +1 -0
  654. package/dist/store/lock.js +70 -0
  655. package/dist/store/lock.js.map +1 -0
  656. package/dist/store/migration-manager.js +151 -0
  657. package/dist/store/migration-manager.js.map +1 -0
  658. package/dist/store/migration-sqlite.js +676 -0
  659. package/dist/store/migration-sqlite.js.map +1 -0
  660. package/dist/store/nexus-schema.js +62 -0
  661. package/dist/store/nexus-schema.js.map +1 -0
  662. package/dist/store/nexus-sqlite.d.ts +14 -2
  663. package/dist/store/nexus-sqlite.d.ts.map +1 -1
  664. package/dist/store/nexus-sqlite.js +217 -0
  665. package/dist/store/nexus-sqlite.js.map +1 -0
  666. package/dist/store/nexus-validation-schemas.js +40 -0
  667. package/dist/store/nexus-validation-schemas.js.map +1 -0
  668. package/dist/store/parsers.js +37 -0
  669. package/dist/store/parsers.js.map +1 -0
  670. package/dist/store/project-detect.js +457 -0
  671. package/dist/store/project-detect.js.map +1 -0
  672. package/dist/store/provider.js +101 -0
  673. package/dist/store/provider.js.map +1 -0
  674. package/dist/store/safety-data-accessor.js +257 -0
  675. package/dist/store/safety-data-accessor.js.map +1 -0
  676. package/dist/store/schema.js +7 -0
  677. package/dist/store/schema.js.map +1 -0
  678. package/dist/store/session-store.js +219 -0
  679. package/dist/store/session-store.js.map +1 -0
  680. package/dist/store/signaldock-sqlite.js +400 -0
  681. package/dist/store/signaldock-sqlite.js.map +1 -0
  682. package/dist/store/sqlite-backup.d.ts +121 -10
  683. package/dist/store/sqlite-backup.d.ts.map +1 -1
  684. package/dist/store/sqlite-backup.js +241 -0
  685. package/dist/store/sqlite-backup.js.map +1 -0
  686. package/dist/store/sqlite-data-accessor.js +787 -0
  687. package/dist/store/sqlite-data-accessor.js.map +1 -0
  688. package/dist/store/sqlite.d.ts.map +1 -1
  689. package/dist/store/sqlite.js +481 -0
  690. package/dist/store/sqlite.js.map +1 -0
  691. package/dist/store/status-registry.js +8 -0
  692. package/dist/store/status-registry.js.map +1 -0
  693. package/dist/store/task-store.js +358 -0
  694. package/dist/store/task-store.js.map +1 -0
  695. package/dist/store/tasks-schema.d.ts +8 -8
  696. package/dist/store/tasks-schema.js +610 -0
  697. package/dist/store/tasks-schema.js.map +1 -0
  698. package/dist/store/typed-query.js +15 -0
  699. package/dist/store/typed-query.js.map +1 -0
  700. package/dist/store/validation-schemas.d.ts +37 -37
  701. package/dist/store/validation-schemas.js +278 -0
  702. package/dist/store/validation-schemas.js.map +1 -0
  703. package/dist/system/archive-analytics.js +277 -0
  704. package/dist/system/archive-analytics.js.map +1 -0
  705. package/dist/system/archive-stats.js +64 -0
  706. package/dist/system/archive-stats.js.map +1 -0
  707. package/dist/system/audit.js +145 -0
  708. package/dist/system/audit.js.map +1 -0
  709. package/dist/system/backup.d.ts +91 -3
  710. package/dist/system/backup.d.ts.map +1 -1
  711. package/dist/system/backup.js +280 -0
  712. package/dist/system/backup.js.map +1 -0
  713. package/dist/system/cleanup.js +134 -0
  714. package/dist/system/cleanup.js.map +1 -0
  715. package/dist/system/health.js +1100 -0
  716. package/dist/system/health.js.map +1 -0
  717. package/dist/system/index.js +18 -0
  718. package/dist/system/index.js.map +1 -0
  719. package/dist/system/inject-generate.js +122 -0
  720. package/dist/system/inject-generate.js.map +1 -0
  721. package/dist/system/labels.js +38 -0
  722. package/dist/system/labels.js.map +1 -0
  723. package/dist/system/metrics.js +61 -0
  724. package/dist/system/metrics.js.map +1 -0
  725. package/dist/system/migrate.js +43 -0
  726. package/dist/system/migrate.js.map +1 -0
  727. package/dist/system/platform-paths.js +80 -0
  728. package/dist/system/platform-paths.js.map +1 -0
  729. package/dist/system/runtime.js +161 -0
  730. package/dist/system/runtime.js.map +1 -0
  731. package/dist/system/safestop.js +99 -0
  732. package/dist/system/safestop.js.map +1 -0
  733. package/dist/system/storage-preflight.js +123 -0
  734. package/dist/system/storage-preflight.js.map +1 -0
  735. package/dist/task-work/index.js +159 -0
  736. package/dist/task-work/index.js.map +1 -0
  737. package/dist/tasks/add.js +661 -0
  738. package/dist/tasks/add.js.map +1 -0
  739. package/dist/tasks/analyze.js +85 -0
  740. package/dist/tasks/analyze.js.map +1 -0
  741. package/dist/tasks/archive.js +90 -0
  742. package/dist/tasks/archive.js.map +1 -0
  743. package/dist/tasks/atomicity.js +83 -0
  744. package/dist/tasks/atomicity.js.map +1 -0
  745. package/dist/tasks/cancel-ops.js +83 -0
  746. package/dist/tasks/cancel-ops.js.map +1 -0
  747. package/dist/tasks/complete.js +211 -0
  748. package/dist/tasks/complete.js.map +1 -0
  749. package/dist/tasks/crossref-extract.js +73 -0
  750. package/dist/tasks/crossref-extract.js.map +1 -0
  751. package/dist/tasks/delete-preview.js +192 -0
  752. package/dist/tasks/delete-preview.js.map +1 -0
  753. package/dist/tasks/delete.js +120 -0
  754. package/dist/tasks/delete.js.map +1 -0
  755. package/dist/tasks/deletion-strategy.js +200 -0
  756. package/dist/tasks/deletion-strategy.js.map +1 -0
  757. package/dist/tasks/dependency-check.js +278 -0
  758. package/dist/tasks/dependency-check.js.map +1 -0
  759. package/dist/tasks/deps-ready.js +32 -0
  760. package/dist/tasks/deps-ready.js.map +1 -0
  761. package/dist/tasks/enforcement.js +86 -0
  762. package/dist/tasks/enforcement.js.map +1 -0
  763. package/dist/tasks/epic-enforcement.js +294 -0
  764. package/dist/tasks/epic-enforcement.js.map +1 -0
  765. package/dist/tasks/find.js +154 -0
  766. package/dist/tasks/find.js.map +1 -0
  767. package/dist/tasks/graph-cache.js +127 -0
  768. package/dist/tasks/graph-cache.js.map +1 -0
  769. package/dist/tasks/graph-ops.js +171 -0
  770. package/dist/tasks/graph-ops.js.map +1 -0
  771. package/dist/tasks/graph-rag.js +328 -0
  772. package/dist/tasks/graph-rag.js.map +1 -0
  773. package/dist/tasks/hierarchy-policy.js +149 -0
  774. package/dist/tasks/hierarchy-policy.js.map +1 -0
  775. package/dist/tasks/hierarchy.js +185 -0
  776. package/dist/tasks/hierarchy.js.map +1 -0
  777. package/dist/tasks/id-generator.js +65 -0
  778. package/dist/tasks/id-generator.js.map +1 -0
  779. package/dist/tasks/index.js +14 -0
  780. package/dist/tasks/index.js.map +1 -0
  781. package/dist/tasks/labels.js +52 -0
  782. package/dist/tasks/labels.js.map +1 -0
  783. package/dist/tasks/list.js +75 -0
  784. package/dist/tasks/list.js.map +1 -0
  785. package/dist/tasks/phase-tracking.js +133 -0
  786. package/dist/tasks/phase-tracking.js.map +1 -0
  787. package/dist/tasks/pipeline-stage.js +248 -0
  788. package/dist/tasks/pipeline-stage.js.map +1 -0
  789. package/dist/tasks/plan.js +268 -0
  790. package/dist/tasks/plan.js.map +1 -0
  791. package/dist/tasks/relates.js +89 -0
  792. package/dist/tasks/relates.js.map +1 -0
  793. package/dist/tasks/show.js +80 -0
  794. package/dist/tasks/show.js.map +1 -0
  795. package/dist/tasks/size-weighting.js +86 -0
  796. package/dist/tasks/size-weighting.js.map +1 -0
  797. package/dist/tasks/staleness.js +86 -0
  798. package/dist/tasks/staleness.js.map +1 -0
  799. package/dist/tasks/task-ops.js +1741 -0
  800. package/dist/tasks/task-ops.js.map +1 -0
  801. package/dist/tasks/update.js +277 -0
  802. package/dist/tasks/update.js.map +1 -0
  803. package/dist/templates/index.js +10 -0
  804. package/dist/templates/index.js.map +1 -0
  805. package/dist/templates/parser.js +254 -0
  806. package/dist/templates/parser.js.map +1 -0
  807. package/dist/ui/aliases.js +153 -0
  808. package/dist/ui/aliases.js.map +1 -0
  809. package/dist/ui/changelog.js +184 -0
  810. package/dist/ui/changelog.js.map +1 -0
  811. package/dist/ui/command-registry.js +168 -0
  812. package/dist/ui/command-registry.js.map +1 -0
  813. package/dist/ui/flags.js +94 -0
  814. package/dist/ui/flags.js.map +1 -0
  815. package/dist/ui/index.js +24 -0
  816. package/dist/ui/index.js.map +1 -0
  817. package/dist/upgrade.js +1148 -0
  818. package/dist/upgrade.js.map +1 -0
  819. package/dist/validation/chain-validation.js +146 -0
  820. package/dist/validation/chain-validation.js.map +1 -0
  821. package/dist/validation/compliance.js +155 -0
  822. package/dist/validation/compliance.js.map +1 -0
  823. package/dist/validation/docs-sync.js +212 -0
  824. package/dist/validation/docs-sync.js.map +1 -0
  825. package/dist/validation/doctor/checks.js +1069 -0
  826. package/dist/validation/doctor/checks.js.map +1 -0
  827. package/dist/validation/doctor/index.js +9 -0
  828. package/dist/validation/doctor/index.js.map +1 -0
  829. package/dist/validation/doctor/project-cache.js +160 -0
  830. package/dist/validation/doctor/project-cache.js.map +1 -0
  831. package/dist/validation/doctor/utils.js +155 -0
  832. package/dist/validation/doctor/utils.js.map +1 -0
  833. package/dist/validation/engine.js +902 -0
  834. package/dist/validation/engine.js.map +1 -0
  835. package/dist/validation/gap-check.js +175 -0
  836. package/dist/validation/gap-check.js.map +1 -0
  837. package/dist/validation/index.js +40 -0
  838. package/dist/validation/index.js.map +1 -0
  839. package/dist/validation/manifest.js +237 -0
  840. package/dist/validation/manifest.js.map +1 -0
  841. package/dist/validation/operation-gate-validators.js +724 -0
  842. package/dist/validation/operation-gate-validators.js.map +1 -0
  843. package/dist/validation/operation-verification-gates.js +532 -0
  844. package/dist/validation/operation-verification-gates.js.map +1 -0
  845. package/dist/validation/param-utils.js +139 -0
  846. package/dist/validation/param-utils.js.map +1 -0
  847. package/dist/validation/protocol-common.js +300 -0
  848. package/dist/validation/protocol-common.js.map +1 -0
  849. package/dist/validation/protocols/_shared.js +75 -0
  850. package/dist/validation/protocols/_shared.js.map +1 -0
  851. package/dist/validation/protocols/architecture-decision.js +31 -0
  852. package/dist/validation/protocols/architecture-decision.js.map +1 -0
  853. package/dist/validation/protocols/artifact-publish.js +28 -0
  854. package/dist/validation/protocols/artifact-publish.js.map +1 -0
  855. package/dist/validation/protocols/consensus.js +41 -0
  856. package/dist/validation/protocols/consensus.js.map +1 -0
  857. package/dist/validation/protocols/contribution.js +27 -0
  858. package/dist/validation/protocols/contribution.js.map +1 -0
  859. package/dist/validation/protocols/decomposition.js +28 -0
  860. package/dist/validation/protocols/decomposition.js.map +1 -0
  861. package/dist/validation/protocols/implementation.js +24 -0
  862. package/dist/validation/protocols/implementation.js.map +1 -0
  863. package/dist/validation/protocols/provenance.js +29 -0
  864. package/dist/validation/protocols/provenance.js.map +1 -0
  865. package/dist/validation/protocols/release.js +29 -0
  866. package/dist/validation/protocols/release.js.map +1 -0
  867. package/dist/validation/protocols/research.js +24 -0
  868. package/dist/validation/protocols/research.js.map +1 -0
  869. package/dist/validation/protocols/specification.js +27 -0
  870. package/dist/validation/protocols/specification.js.map +1 -0
  871. package/dist/validation/protocols/testing.js +30 -0
  872. package/dist/validation/protocols/testing.js.map +1 -0
  873. package/dist/validation/protocols/validation.js +30 -0
  874. package/dist/validation/protocols/validation.js.map +1 -0
  875. package/dist/validation/schema-integrity.js +170 -0
  876. package/dist/validation/schema-integrity.js.map +1 -0
  877. package/dist/validation/schema-validator.js +176 -0
  878. package/dist/validation/schema-validator.js.map +1 -0
  879. package/dist/validation/validate-ops.js +937 -0
  880. package/dist/validation/validate-ops.js.map +1 -0
  881. package/dist/validation/validation-rules.js +226 -0
  882. package/dist/validation/validation-rules.js.map +1 -0
  883. package/dist/validation/verification.js +321 -0
  884. package/dist/validation/verification.js.map +1 -0
  885. package/package.json +10 -8
  886. package/src/__tests__/paths-walkup.test.ts +305 -0
  887. package/src/__tests__/paths.test.ts +61 -17
  888. package/src/hooks/handlers/session-hooks.ts +42 -0
  889. package/src/internal.ts +19 -2
  890. package/src/paths.ts +91 -14
  891. package/src/scaffold.ts +22 -3
  892. package/src/store/__tests__/cleanup-legacy.test.ts +268 -0
  893. package/src/store/__tests__/database-topology-integration.test.ts +504 -0
  894. package/src/store/__tests__/sqlite-backup-global.test.ts +281 -0
  895. package/src/store/__tests__/sqlite-backup.test.ts +118 -10
  896. package/src/store/cleanup-legacy.ts +208 -0
  897. package/src/store/index.ts +7 -0
  898. package/src/store/nexus-sqlite.ts +32 -3
  899. package/src/store/sqlite-backup.ts +368 -37
  900. package/src/store/sqlite.ts +19 -3
  901. package/src/system/__tests__/backup.test.ts +237 -0
  902. package/src/system/backup.ts +248 -28
  903. package/templates/cleo-gitignore +19 -3
@@ -0,0 +1,1759 @@
1
+ /**
2
+ * Directory & file scaffolding utilities.
3
+ *
4
+ * Shared ensure/check functions extracted from init.ts for reuse
5
+ * by init.ts, upgrade.ts, and doctor health checks.
6
+ *
7
+ * Rules:
8
+ * - All ensure functions are idempotent (safe to call multiple times)
9
+ * - All check functions are read-only (no side effects)
10
+ * - Uses imports from ./paths.js for path resolution
11
+ */
12
+ import { execFile } from 'node:child_process';
13
+ import { randomUUID } from 'node:crypto';
14
+ import { existsSync, constants as fsConstants, readFileSync, statSync } from 'node:fs';
15
+ import { access, copyFile, mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises';
16
+ import { homedir as getHomedir } from 'node:os';
17
+ import { dirname, join, resolve } from 'node:path';
18
+ import { fileURLToPath } from 'node:url';
19
+ import { promisify } from 'node:util';
20
+ import { generateProjectHash } from './nexus/hash.js';
21
+ import { getCleoCantWorkflowsDir, getCleoDirAbsolute, getCleoGlobalAgentsDir, getCleoGlobalRecipesDir, getCleoHome, getCleoPiExtensionsDir, getCleoTemplatesDir, getConfigPath, } from './paths.js';
22
+ import { saveJson } from './store/json.js';
23
+ const execFileAsync = promisify(execFile);
24
+ // ── Constants ────────────────────────────────────────────────────────
25
+ /** Required subdirectories under .cleo/. */
26
+ export const REQUIRED_CLEO_SUBDIRS = [
27
+ 'backups/operational',
28
+ 'backups/safety',
29
+ 'agent-outputs',
30
+ 'logs',
31
+ 'rcasd',
32
+ 'adrs',
33
+ ];
34
+ /**
35
+ * Embedded fallback for .cleo/.gitignore content (deny-by-default).
36
+ *
37
+ * Must stay in sync with `packages/core/templates/cleo-gitignore`. The
38
+ * template file is the source of truth; this constant is only used when
39
+ * the template file cannot be located at runtime (bundled binary edge
40
+ * cases, air-gapped installs, etc.).
41
+ *
42
+ * ADR-013 §9 (2026-04-07, T5158): config.json / project-info.json are
43
+ * NOT re-included here. Runtime state files (tasks.db, brain.db,
44
+ * config.json, project-info.json) are protected by explicit deny rules
45
+ * so that nested-.gitignore allow rules cannot unignore them at the
46
+ * project repository level.
47
+ */
48
+ export const CLEO_GITIGNORE_FALLBACK = `# .cleo/.gitignore — Deny-by-default for CLEO project data
49
+ # Ignore everything, then explicitly allow only tracked files.
50
+ #
51
+ # ADR-013 §9 (2026-04-07, T5158): config.json + project-info.json are
52
+ # runtime snapshots regenerated by \`cleo init\`, not tracked in git.
53
+ # Recovery for all four runtime files (tasks.db, brain.db, config.json,
54
+ # project-info.json) is provided by \`cleo backup add\` snapshots under
55
+ # .cleo/backups/. See .cleo/adrs/ADR-013 for the full recovery story.
56
+
57
+ # Step 1: Ignore everything
58
+ *
59
+
60
+ # Allow list
61
+ !.gitignore
62
+ !project-context.json
63
+ !setup-otel.sh
64
+ !DATA-SAFETY-IMPLEMENTATION-SUMMARY.md
65
+ !adrs/
66
+ !adrs/**
67
+ !rcasd/
68
+ !rcasd/**
69
+ !agent-outputs/
70
+ !agent-outputs/**
71
+
72
+ # Explicit deny safety net
73
+ *.db
74
+ *.db-shm
75
+ *.db-wal
76
+ *.db-journal
77
+ config.json
78
+ project-info.json
79
+ log.json
80
+ bypass-log.json
81
+ qa-log.json
82
+ .deps-cache/
83
+ .context-alert-state.json
84
+ .context-state*.json
85
+ context-states/
86
+ .git-checkpoint-state
87
+ .migration-state.json
88
+ migrations.json
89
+ sync/
90
+ metrics/
91
+ .backups/
92
+ backups/
93
+ `;
94
+ // ── Pure helpers ─────────────────────────────────────────────────────
95
+ /**
96
+ * Check if a file exists and is readable.
97
+ *
98
+ * @param path - Absolute path to the file to check
99
+ * @returns True if the file exists and is readable, false otherwise
100
+ *
101
+ * @remarks
102
+ * Uses `fs.access` with `R_OK` to verify both existence and read permission.
103
+ * Swallows all errors and returns false on failure.
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * if (await fileExists('/project/.cleo/config.json')) {
108
+ * // safe to read
109
+ * }
110
+ * ```
111
+ */
112
+ export async function fileExists(path) {
113
+ try {
114
+ await access(path, fsConstants.R_OK);
115
+ return true;
116
+ }
117
+ catch {
118
+ return false;
119
+ }
120
+ }
121
+ /**
122
+ * Strip legacy CLEO:START/CLEO:END blocks from a file.
123
+ * Called before CAAMP injection to prevent competing blocks.
124
+ *
125
+ * @param filePath - Absolute path to the file to strip
126
+ *
127
+ * @remarks
128
+ * Handles both bare markers (`<!-- CLEO:START -->`) and versioned markers
129
+ * (`<!-- CLEO:START v0.53.4 -->`). No-op if the file does not exist or
130
+ * contains no CLEO blocks.
131
+ *
132
+ * @example
133
+ * ```typescript
134
+ * await stripCLEOBlocks('/project/CLAUDE.md');
135
+ * ```
136
+ */
137
+ export async function stripCLEOBlocks(filePath) {
138
+ if (!existsSync(filePath))
139
+ return;
140
+ const content = await readFile(filePath, 'utf8');
141
+ const stripped = content.replace(/\n?<!-- CLEO:START[^>]*-->[\s\S]*?<!-- CLEO:END -->\n?/g, '');
142
+ if (stripped !== content)
143
+ await writeFile(filePath, stripped, 'utf8');
144
+ }
145
+ /**
146
+ * Remove .cleo/ or .cleo entries from the project root .gitignore.
147
+ *
148
+ * @param projectRoot - Absolute path to the project root directory
149
+ * @returns Whether any lines were removed from the .gitignore
150
+ *
151
+ * @remarks
152
+ * Filters out lines matching `/.cleo/`, `.cleo/`, `.cleo`, etc. from the
153
+ * root-level `.gitignore`. Returns `{ removed: false }` if the file does not
154
+ * exist or contains no matching entries.
155
+ *
156
+ * @example
157
+ * ```typescript
158
+ * const { removed } = await removeCleoFromRootGitignore('/project');
159
+ * if (removed) console.log('.cleo entries cleaned from .gitignore');
160
+ * ```
161
+ */
162
+ export async function removeCleoFromRootGitignore(projectRoot) {
163
+ const rootGitignorePath = join(projectRoot, '.gitignore');
164
+ if (!(await fileExists(rootGitignorePath))) {
165
+ return { removed: false };
166
+ }
167
+ const content = await readFile(rootGitignorePath, 'utf-8');
168
+ const lines = content.split('\n');
169
+ const filtered = lines.filter((line) => {
170
+ const trimmed = line.trim();
171
+ return !/^\/?\.cleo\/?(\*)?$/.test(trimmed);
172
+ });
173
+ if (filtered.length === lines.length) {
174
+ return { removed: false };
175
+ }
176
+ await writeFile(rootGitignorePath, filtered.join('\n'));
177
+ return { removed: true };
178
+ }
179
+ // generateProjectHash moved to src/core/nexus/hash.ts (canonical location)
180
+ export { generateProjectHash } from './nexus/hash.js';
181
+ /**
182
+ * Resolve the package root directory (where schemas/ and templates/ live).
183
+ * scaffold.ts lives in packages/core/src/, so 1 level up reaches the package root.
184
+ *
185
+ * @returns Absolute path to the @cleocode/core package root
186
+ *
187
+ * @remarks
188
+ * Uses `import.meta.url` to determine the file location at runtime, which
189
+ * works correctly in both development and installed npm package contexts.
190
+ *
191
+ * @example
192
+ * ```typescript
193
+ * const root = getPackageRoot();
194
+ * const schemaPath = join(root, 'schemas', 'config.schema.json');
195
+ * ```
196
+ */
197
+ export function getPackageRoot() {
198
+ const thisFile = fileURLToPath(import.meta.url);
199
+ return resolve(dirname(thisFile), '..');
200
+ }
201
+ /**
202
+ * Load the gitignore template from the package's templates/ directory.
203
+ * Falls back to embedded content if file not found.
204
+ *
205
+ * @returns The .cleo/.gitignore template content string
206
+ *
207
+ * @remarks
208
+ * First attempts to read from the package's `templates/cleo-gitignore` file.
209
+ * Falls back to the embedded `CLEO_GITIGNORE_FALLBACK` constant if the
210
+ * template file is missing or unreadable.
211
+ *
212
+ * @example
213
+ * ```typescript
214
+ * const content = getGitignoreContent();
215
+ * await writeFile('.cleo/.gitignore', content);
216
+ * ```
217
+ */
218
+ export function getGitignoreContent() {
219
+ try {
220
+ const packageRoot = getPackageRoot();
221
+ const templatePath = join(packageRoot, 'templates', 'cleo-gitignore');
222
+ if (existsSync(templatePath)) {
223
+ return readFileSync(templatePath, 'utf-8');
224
+ }
225
+ }
226
+ catch {
227
+ // fallback
228
+ }
229
+ return CLEO_GITIGNORE_FALLBACK;
230
+ }
231
+ /**
232
+ * Read CLEO version from package.json.
233
+ *
234
+ * @returns Semver version string, or "0.0.0" if unavailable
235
+ *
236
+ * @remarks
237
+ * Reads the `version` field from the @cleocode/core package.json at runtime.
238
+ * Returns "0.0.0" if the file cannot be read or parsed.
239
+ *
240
+ * @example
241
+ * ```typescript
242
+ * const version = getCleoVersion(); // "2026.4.0"
243
+ * ```
244
+ */
245
+ export function getCleoVersion() {
246
+ try {
247
+ const pkgPath = join(getPackageRoot(), 'package.json');
248
+ const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
249
+ return pkg.version ?? '0.0.0';
250
+ }
251
+ catch {
252
+ return '0.0.0';
253
+ }
254
+ }
255
+ /**
256
+ * Detect whether projectRoot is the CLEO source repository itself.
257
+ * Verified by fingerprinting the expected source layout and package identity.
258
+ * Only the canonical CLEO repository matches all criteria (ADR-029).
259
+ */
260
+ function isCleoContributorProject(projectRoot) {
261
+ const exists = (p) => existsSync(join(projectRoot, p));
262
+ // Must have all three canonical source directories
263
+ if (!exists('src/dispatch') || !exists('src/core'))
264
+ return false;
265
+ // Must have package.json identifying as @cleocode/cleo
266
+ try {
267
+ const pkg = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf-8'));
268
+ return pkg.name === '@cleocode/cleo';
269
+ }
270
+ catch {
271
+ return false;
272
+ }
273
+ }
274
+ /**
275
+ * Create default config.json content.
276
+ *
277
+ * @returns A plain object with the default CLEO configuration structure
278
+ *
279
+ * @remarks
280
+ * Returns a version-stamped config with defaults for output formatting, backup
281
+ * limits, hierarchy depth, session behavior, and lifecycle mode.
282
+ *
283
+ * @example
284
+ * ```typescript
285
+ * const config = createDefaultConfig();
286
+ * await saveJson(configPath, config);
287
+ * ```
288
+ */
289
+ export function createDefaultConfig() {
290
+ return {
291
+ version: '2.10.0',
292
+ output: {
293
+ defaultFormat: 'json',
294
+ showColor: true,
295
+ showUnicode: true,
296
+ dateFormat: 'relative',
297
+ },
298
+ backup: {
299
+ maxOperationalBackups: 10,
300
+ maxSafetyBackups: 5,
301
+ },
302
+ hierarchy: {
303
+ maxDepth: 3,
304
+ maxSiblings: 0,
305
+ },
306
+ session: {
307
+ autoStart: false,
308
+ multiSession: false,
309
+ },
310
+ lifecycle: {
311
+ mode: 'strict',
312
+ },
313
+ };
314
+ }
315
+ // ── ensure* functions (idempotent) ───────────────────────────────────
316
+ /**
317
+ * Create .cleo/ directory and all required subdirectories.
318
+ * Idempotent: skips directories that already exist.
319
+ *
320
+ * @param projectRoot - Absolute path to the project root directory
321
+ * @returns Scaffold result indicating whether the directory was created or already existed
322
+ *
323
+ * @remarks
324
+ * Refuses to scaffold inside the global CLEO home to prevent pollution.
325
+ * Creates all directories listed in `REQUIRED_CLEO_SUBDIRS`.
326
+ *
327
+ * @example
328
+ * ```typescript
329
+ * const result = await ensureCleoStructure('/my/project');
330
+ * console.log(result.action); // "created" or "skipped"
331
+ * ```
332
+ */
333
+ export async function ensureCleoStructure(projectRoot) {
334
+ // Guard: reject global home as project root to prevent project-level
335
+ // subdirectories (adrs/, rcasd/, backups/, etc.) from polluting ~/.cleo/
336
+ const resolvedRoot = resolve(projectRoot);
337
+ if (resolvedRoot === resolve(getCleoHome())) {
338
+ return {
339
+ action: 'skipped',
340
+ path: join(resolvedRoot, '.cleo'),
341
+ details: 'Refused to scaffold project structure inside global CLEO home',
342
+ };
343
+ }
344
+ const cleoDir = getCleoDirAbsolute(projectRoot);
345
+ const alreadyExists = existsSync(cleoDir);
346
+ await mkdir(cleoDir, { recursive: true });
347
+ for (const subdir of REQUIRED_CLEO_SUBDIRS) {
348
+ await mkdir(join(cleoDir, subdir), { recursive: true });
349
+ }
350
+ return {
351
+ action: alreadyExists ? 'skipped' : 'created',
352
+ path: cleoDir,
353
+ details: alreadyExists
354
+ ? 'Directory already existed, ensured subdirs'
355
+ : `Created .cleo/ with ${REQUIRED_CLEO_SUBDIRS.length} subdirectories`,
356
+ };
357
+ }
358
+ /**
359
+ * Create or repair .cleo/.gitignore from template.
360
+ * Idempotent: skips if file already exists with correct content.
361
+ *
362
+ * @param projectRoot - Absolute path to the project root directory
363
+ * @returns Scaffold result indicating whether the gitignore was created, repaired, or skipped
364
+ *
365
+ * @remarks
366
+ * Compares normalized content (trimmed, LF line endings) against the template.
367
+ * If the file exists but differs, it is overwritten with the canonical template.
368
+ *
369
+ * @example
370
+ * ```typescript
371
+ * const result = await ensureGitignore('/my/project');
372
+ * if (result.action === 'repaired') console.log('Gitignore updated');
373
+ * ```
374
+ */
375
+ export async function ensureGitignore(projectRoot) {
376
+ const cleoDir = getCleoDirAbsolute(projectRoot);
377
+ const gitignorePath = join(cleoDir, '.gitignore');
378
+ const templateContent = getGitignoreContent();
379
+ if (existsSync(gitignorePath)) {
380
+ const existing = readFileSync(gitignorePath, 'utf-8');
381
+ const normalize = (s) => s.trim().replace(/\r\n/g, '\n');
382
+ if (normalize(existing) === normalize(templateContent)) {
383
+ return { action: 'skipped', path: gitignorePath, details: 'Already matches template' };
384
+ }
385
+ await writeFile(gitignorePath, templateContent);
386
+ return { action: 'repaired', path: gitignorePath, details: 'Updated to match template' };
387
+ }
388
+ await writeFile(gitignorePath, templateContent);
389
+ return { action: 'created', path: gitignorePath };
390
+ }
391
+ /**
392
+ * Create default config.json if missing.
393
+ * Idempotent: skips if file already exists.
394
+ *
395
+ * @param projectRoot - Absolute path to the project root directory
396
+ * @param opts - Optional configuration
397
+ * @param opts.force - When true, overwrite the existing config
398
+ * @returns Scaffold result indicating the action taken
399
+ *
400
+ * @remarks
401
+ * On skip, still checks for and backfills the ADR-029 contributor block
402
+ * if the project is detected as the CLEO source repository.
403
+ *
404
+ * @example
405
+ * ```typescript
406
+ * const result = await ensureConfig('/my/project', { force: true });
407
+ * console.log(result.path);
408
+ * ```
409
+ */
410
+ export async function ensureConfig(projectRoot, opts) {
411
+ const configPath = getConfigPath(projectRoot);
412
+ if (existsSync(configPath) && !opts?.force) {
413
+ // Backfill contributor block for existing configs that predate ADR-029
414
+ try {
415
+ const existing = JSON.parse(readFileSync(configPath, 'utf-8'));
416
+ if (!existing['contributor'] && isCleoContributorProject(projectRoot)) {
417
+ existing['contributor'] = {
418
+ isContributorProject: true,
419
+ devCli: 'cleo-dev',
420
+ verifiedAt: new Date().toISOString(),
421
+ };
422
+ await writeFile(configPath, JSON.stringify(existing, null, 2));
423
+ return {
424
+ action: 'repaired',
425
+ path: configPath,
426
+ details: 'Added contributor block (ADR-029)',
427
+ };
428
+ }
429
+ }
430
+ catch {
431
+ /* non-fatal */
432
+ }
433
+ return { action: 'skipped', path: configPath, details: 'Config already exists' };
434
+ }
435
+ const config = createDefaultConfig();
436
+ if (isCleoContributorProject(projectRoot)) {
437
+ config['contributor'] = {
438
+ isContributorProject: true,
439
+ devCli: 'cleo-dev',
440
+ verifiedAt: new Date().toISOString(),
441
+ };
442
+ }
443
+ await saveJson(configPath, config);
444
+ return {
445
+ action: existsSync(configPath) ? 'repaired' : 'created',
446
+ path: configPath,
447
+ };
448
+ }
449
+ /**
450
+ * Create or refresh project-info.json.
451
+ * Idempotent: skips if file already exists (unless force).
452
+ *
453
+ * @param projectRoot - Absolute path to the project root directory
454
+ * @param opts - Optional configuration
455
+ * @param opts.force - When true, regenerate even if the file exists
456
+ * @returns Scaffold result indicating the action taken
457
+ *
458
+ * @remarks
459
+ * Backfills a `projectId` (UUID) on existing files that lack one (T5333).
460
+ * The generated file includes project hash, CLEO version, schema versions,
461
+ * and initial health/feature flags.
462
+ *
463
+ * @example
464
+ * ```typescript
465
+ * const result = await ensureProjectInfo('/my/project');
466
+ * console.log(result.action); // "created", "repaired", or "skipped"
467
+ * ```
468
+ */
469
+ export async function ensureProjectInfo(projectRoot, opts) {
470
+ const cleoDir = getCleoDirAbsolute(projectRoot);
471
+ const projectInfoPath = join(cleoDir, 'project-info.json');
472
+ // Backfill projectId on existing files that lack it (T5333)
473
+ if (existsSync(projectInfoPath) && !opts?.force) {
474
+ try {
475
+ const existing = JSON.parse(readFileSync(projectInfoPath, 'utf-8'));
476
+ if (typeof existing.projectId !== 'string' || existing.projectId.length === 0) {
477
+ existing.projectId = randomUUID();
478
+ existing.lastUpdated = new Date().toISOString();
479
+ await writeFile(projectInfoPath, JSON.stringify(existing, null, 2));
480
+ return { action: 'repaired', path: projectInfoPath, details: 'Added projectId' };
481
+ }
482
+ }
483
+ catch {
484
+ // If parse fails, fall through to regenerate
485
+ }
486
+ return { action: 'skipped', path: projectInfoPath, details: 'Already exists' };
487
+ }
488
+ const projectHash = generateProjectHash(projectRoot);
489
+ const cleoVersion = getCleoVersion();
490
+ const now = new Date().toISOString();
491
+ const { readSchemaVersionFromFile } = await import('./validation/schema-integrity.js');
492
+ const { SQLITE_SCHEMA_VERSION } = await import('./store/sqlite.js');
493
+ const configSchemaVersion = readSchemaVersionFromFile('config.schema.json') ?? cleoVersion;
494
+ const projectContextSchemaVersion = readSchemaVersionFromFile('project-context.schema.json') ?? '1.0.0';
495
+ const projectInfo = {
496
+ $schema: './schemas/project-info.schema.json',
497
+ schemaVersion: '1.0.0',
498
+ projectId: randomUUID(),
499
+ projectHash,
500
+ cleoVersion,
501
+ lastUpdated: now,
502
+ schemas: {
503
+ config: configSchemaVersion,
504
+ sqlite: SQLITE_SCHEMA_VERSION,
505
+ projectContext: projectContextSchemaVersion,
506
+ },
507
+ injection: {
508
+ 'CLAUDE.md': null,
509
+ 'AGENTS.md': null,
510
+ 'GEMINI.md': null,
511
+ },
512
+ health: {
513
+ status: 'unknown',
514
+ lastCheck: null,
515
+ issues: [],
516
+ },
517
+ features: {
518
+ multiSession: false,
519
+ verification: false,
520
+ contextAlerts: false,
521
+ },
522
+ };
523
+ await writeFile(projectInfoPath, JSON.stringify(projectInfo, null, 2));
524
+ return { action: 'created', path: projectInfoPath };
525
+ }
526
+ /**
527
+ * No-op. Kept for API compatibility.
528
+ *
529
+ * @param projectRoot - Absolute path to the project root directory
530
+ * @returns Scaffold result with action "skipped"
531
+ *
532
+ * @remarks
533
+ * This function was removed in Phase 2 production readiness but the export
534
+ * is preserved to avoid breaking downstream callers.
535
+ *
536
+ * @example
537
+ * ```typescript
538
+ * const result = await ensureContributorMcp('/my/project');
539
+ * // result.action === 'skipped'
540
+ * ```
541
+ */
542
+ export async function ensureContributorMcp(projectRoot) {
543
+ return {
544
+ action: 'skipped',
545
+ path: projectRoot,
546
+ details: 'Removed (Phase 2 production readiness)',
547
+ };
548
+ }
549
+ /**
550
+ * Detect and write project-context.json.
551
+ * Idempotent: skips if file exists and is less than staleDays old (default: 30).
552
+ *
553
+ * @param projectRoot - Absolute path to the project root directory
554
+ * @param opts - Optional configuration
555
+ * @param opts.force - When true, regenerate even if the file is fresh
556
+ * @param opts.staleDays - Age threshold in days before regeneration (default: 30)
557
+ * @returns Scaffold result indicating the action taken
558
+ *
559
+ * @remarks
560
+ * Runs project type detection (Node, Python, Rust, etc.) and writes the result.
561
+ * Validates against the project-context schema before writing (best-effort).
562
+ *
563
+ * @example
564
+ * ```typescript
565
+ * const result = await ensureProjectContext('/my/project', { staleDays: 7 });
566
+ * if (result.action === 'repaired') console.log('Context refreshed');
567
+ * ```
568
+ */
569
+ export async function ensureProjectContext(projectRoot, opts) {
570
+ const cleoDir = getCleoDirAbsolute(projectRoot);
571
+ const contextPath = join(cleoDir, 'project-context.json');
572
+ const staleDays = opts?.staleDays ?? 30;
573
+ if (existsSync(contextPath) && !opts?.force) {
574
+ try {
575
+ const content = JSON.parse(readFileSync(contextPath, 'utf-8'));
576
+ if (content.detectedAt) {
577
+ const detectedAt = new Date(content.detectedAt);
578
+ const ageMs = Date.now() - detectedAt.getTime();
579
+ const ageDays = ageMs / (1000 * 60 * 60 * 24);
580
+ if (ageDays < staleDays) {
581
+ return {
582
+ action: 'skipped',
583
+ path: contextPath,
584
+ details: `Fresh (${Math.floor(ageDays)}d old)`,
585
+ };
586
+ }
587
+ }
588
+ }
589
+ catch {
590
+ // If we can't parse it, regenerate
591
+ }
592
+ }
593
+ const { detectProjectType } = await import('./store/project-detect.js');
594
+ const context = detectProjectType(projectRoot);
595
+ // Validate against schema before writing (best-effort, never blocks write)
596
+ try {
597
+ const schemaPath = join(dirname(fileURLToPath(import.meta.url)), '../schemas/project-context.schema.json');
598
+ if (existsSync(schemaPath)) {
599
+ const AjvModule = await import('ajv');
600
+ const ajvMod = AjvModule;
601
+ const AjvClass = (typeof ajvMod.default === 'function' ? ajvMod.default : AjvModule.default);
602
+ const schema = JSON.parse(readFileSync(schemaPath, 'utf-8'));
603
+ const ajv = new AjvClass({ strict: false });
604
+ const valid = ajv.validate(schema, context);
605
+ if (!valid) {
606
+ // eslint-disable-next-line no-console
607
+ console.warn('[CLEO] project-context.json schema validation warnings:', ajv.errors);
608
+ }
609
+ }
610
+ }
611
+ catch {
612
+ // Schema validation is best-effort — never block the write
613
+ }
614
+ await writeFile(contextPath, JSON.stringify(context, null, 2));
615
+ return {
616
+ action: existsSync(contextPath) ? 'repaired' : 'created',
617
+ path: contextPath,
618
+ };
619
+ }
620
+ /**
621
+ * Initialize isolated .cleo/.git checkpoint repository.
622
+ * Idempotent: skips if .cleo/.git already exists.
623
+ *
624
+ * @param projectRoot - Absolute path to the project root directory
625
+ * @returns Scaffold result indicating the action taken
626
+ *
627
+ * @remarks
628
+ * Creates an isolated git repository inside .cleo/ for checkpoint tracking.
629
+ * The repo uses `GIT_DIR` and `GIT_WORK_TREE` environment variables to avoid
630
+ * interfering with the project's own git repository.
631
+ *
632
+ * @example
633
+ * ```typescript
634
+ * const result = await ensureCleoGitRepo('/my/project');
635
+ * console.log(result.action); // "created" or "skipped"
636
+ * ```
637
+ */
638
+ export async function ensureCleoGitRepo(projectRoot) {
639
+ const cleoDir = getCleoDirAbsolute(projectRoot);
640
+ const cleoGitDir = join(cleoDir, '.git');
641
+ if (existsSync(cleoGitDir)) {
642
+ return { action: 'skipped', path: cleoGitDir, details: 'Already initialized' };
643
+ }
644
+ const gitEnv = {
645
+ ...process.env,
646
+ GIT_DIR: cleoGitDir,
647
+ GIT_WORK_TREE: cleoDir,
648
+ };
649
+ await execFileAsync('git', ['init', '--quiet'], { cwd: cleoDir, env: gitEnv });
650
+ await execFileAsync('git', ['config', 'user.email', 'cleo@local'], { cwd: cleoDir, env: gitEnv });
651
+ await execFileAsync('git', ['config', 'user.name', 'CLEO'], { cwd: cleoDir, env: gitEnv });
652
+ return { action: 'created', path: cleoGitDir, details: 'Isolated checkpoint repository' };
653
+ }
654
+ /**
655
+ * Create SQLite database if missing.
656
+ * Idempotent: skips if tasks.db already exists.
657
+ *
658
+ * @param projectRoot - Absolute path to the project root directory
659
+ * @returns Scaffold result indicating the action taken
660
+ *
661
+ * @remarks
662
+ * Initializes the tasks.db SQLite database by calling `getDb()` which runs
663
+ * schema migrations. Returns a skipped result with error details if initialization fails.
664
+ *
665
+ * @example
666
+ * ```typescript
667
+ * const result = await ensureSqliteDb('/my/project');
668
+ * if (result.action === 'created') console.log('Database ready');
669
+ * ```
670
+ */
671
+ export async function ensureSqliteDb(projectRoot) {
672
+ const cleoDir = getCleoDirAbsolute(projectRoot);
673
+ const dbPath = join(cleoDir, 'tasks.db');
674
+ if (existsSync(dbPath)) {
675
+ return { action: 'skipped', path: dbPath, details: 'tasks.db already exists' };
676
+ }
677
+ try {
678
+ const { getDb } = await import('./store/sqlite.js');
679
+ await getDb(projectRoot);
680
+ return { action: 'created', path: dbPath, details: 'SQLite database initialized' };
681
+ }
682
+ catch (err) {
683
+ return {
684
+ action: 'skipped',
685
+ path: dbPath,
686
+ details: `Failed to initialize SQLite: ${err instanceof Error ? err.message : String(err)}`,
687
+ };
688
+ }
689
+ }
690
+ // ── check* functions (read-only) ─────────────────────────────────────
691
+ /**
692
+ * Verify all required .cleo/ subdirectories exist.
693
+ *
694
+ * @param projectRoot - Absolute path to the project root directory
695
+ * @returns Check result with status and list of any missing subdirectories
696
+ *
697
+ * @remarks
698
+ * Read-only diagnostic. Checks for the .cleo/ directory and all entries
699
+ * in `REQUIRED_CLEO_SUBDIRS`. Reports "failed" if .cleo/ is missing,
700
+ * "warning" if subdirectories are missing, "passed" otherwise.
701
+ *
702
+ * @example
703
+ * ```typescript
704
+ * const check = checkCleoStructure('/my/project');
705
+ * if (check.status === 'failed') console.log(check.fix);
706
+ * ```
707
+ */
708
+ export function checkCleoStructure(projectRoot) {
709
+ const cleoDir = getCleoDirAbsolute(projectRoot);
710
+ const missing = [];
711
+ if (!existsSync(cleoDir)) {
712
+ return {
713
+ id: 'cleo_structure',
714
+ category: 'scaffold',
715
+ status: 'failed',
716
+ message: '.cleo/ directory does not exist',
717
+ details: { path: cleoDir, exists: false },
718
+ fix: 'cleo init',
719
+ };
720
+ }
721
+ for (const subdir of REQUIRED_CLEO_SUBDIRS) {
722
+ if (!existsSync(join(cleoDir, subdir))) {
723
+ missing.push(subdir);
724
+ }
725
+ }
726
+ if (missing.length > 0) {
727
+ return {
728
+ id: 'cleo_structure',
729
+ category: 'scaffold',
730
+ status: 'warning',
731
+ message: `Missing subdirectories: ${missing.join(', ')}`,
732
+ details: { path: cleoDir, missing },
733
+ fix: 'cleo init',
734
+ };
735
+ }
736
+ return {
737
+ id: 'cleo_structure',
738
+ category: 'scaffold',
739
+ status: 'passed',
740
+ message: 'All required .cleo/ subdirectories exist',
741
+ details: { path: cleoDir, subdirs: [...REQUIRED_CLEO_SUBDIRS] },
742
+ fix: null,
743
+ };
744
+ }
745
+ /**
746
+ * Verify .cleo/.gitignore exists and matches template.
747
+ *
748
+ * @param projectRoot - Absolute path to the project root directory
749
+ * @returns Check result indicating whether the gitignore matches the template
750
+ *
751
+ * @remarks
752
+ * Read-only diagnostic. Normalizes whitespace before comparison.
753
+ * Reports "warning" if the file is missing or drifted from the template.
754
+ *
755
+ * @example
756
+ * ```typescript
757
+ * const check = checkGitignore('/my/project');
758
+ * if (check.status === 'warning') console.log('Gitignore drifted');
759
+ * ```
760
+ */
761
+ export function checkGitignore(projectRoot) {
762
+ const cleoDir = getCleoDirAbsolute(projectRoot);
763
+ const gitignorePath = join(cleoDir, '.gitignore');
764
+ if (!existsSync(gitignorePath)) {
765
+ return {
766
+ id: 'cleo_gitignore',
767
+ category: 'scaffold',
768
+ status: 'warning',
769
+ message: '.cleo/.gitignore not found',
770
+ details: { path: gitignorePath, exists: false },
771
+ fix: 'cleo init --force',
772
+ };
773
+ }
774
+ const installed = readFileSync(gitignorePath, 'utf-8');
775
+ const template = getGitignoreContent();
776
+ const normalize = (s) => s.trim().replace(/\r\n/g, '\n');
777
+ const matches = normalize(installed) === normalize(template);
778
+ return {
779
+ id: 'cleo_gitignore',
780
+ category: 'scaffold',
781
+ status: matches ? 'passed' : 'warning',
782
+ message: matches
783
+ ? '.cleo/.gitignore matches template'
784
+ : '.cleo/.gitignore has drifted from template',
785
+ details: { path: gitignorePath, matchesTemplate: matches },
786
+ fix: matches ? null : 'cleo upgrade',
787
+ };
788
+ }
789
+ /**
790
+ * Verify config.json exists and is valid JSON.
791
+ *
792
+ * @param projectRoot - Absolute path to the project root directory
793
+ * @returns Check result indicating whether config.json is present and valid
794
+ *
795
+ * @remarks
796
+ * Read-only diagnostic. Reports "failed" if the file is missing or contains
797
+ * invalid JSON. Does not validate config schema, only JSON syntax.
798
+ *
799
+ * @example
800
+ * ```typescript
801
+ * const check = checkConfig('/my/project');
802
+ * if (check.status === 'passed') console.log('Config OK');
803
+ * ```
804
+ */
805
+ export function checkConfig(projectRoot) {
806
+ const configPath = getConfigPath(projectRoot);
807
+ if (!existsSync(configPath)) {
808
+ return {
809
+ id: 'cleo_config',
810
+ category: 'scaffold',
811
+ status: 'failed',
812
+ message: 'config.json not found',
813
+ details: { path: configPath, exists: false },
814
+ fix: 'cleo init',
815
+ };
816
+ }
817
+ try {
818
+ JSON.parse(readFileSync(configPath, 'utf-8'));
819
+ }
820
+ catch (err) {
821
+ return {
822
+ id: 'cleo_config',
823
+ category: 'scaffold',
824
+ status: 'failed',
825
+ message: `config.json is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
826
+ details: { path: configPath, valid: false },
827
+ fix: 'cleo init --force',
828
+ };
829
+ }
830
+ return {
831
+ id: 'cleo_config',
832
+ category: 'scaffold',
833
+ status: 'passed',
834
+ message: 'config.json exists and is valid JSON',
835
+ details: { path: configPath, valid: true },
836
+ fix: null,
837
+ };
838
+ }
839
+ /**
840
+ * Verify project-info.json exists with required fields.
841
+ *
842
+ * @param projectRoot - Absolute path to the project root directory
843
+ * @returns Check result indicating whether project-info.json is valid
844
+ *
845
+ * @remarks
846
+ * Read-only diagnostic. Checks for presence and required fields: projectHash,
847
+ * cleoVersion, and lastUpdated. Reports "warning" on missing fields, "failed" on
848
+ * missing file or invalid JSON.
849
+ *
850
+ * @example
851
+ * ```typescript
852
+ * const check = checkProjectInfo('/my/project');
853
+ * if (check.status !== 'passed') console.log(check.fix);
854
+ * ```
855
+ */
856
+ export function checkProjectInfo(projectRoot) {
857
+ const cleoDir = getCleoDirAbsolute(projectRoot);
858
+ const infoPath = join(cleoDir, 'project-info.json');
859
+ if (!existsSync(infoPath)) {
860
+ return {
861
+ id: 'cleo_project_info',
862
+ category: 'scaffold',
863
+ status: 'warning',
864
+ message: 'project-info.json not found',
865
+ details: { path: infoPath, exists: false },
866
+ fix: 'cleo init',
867
+ };
868
+ }
869
+ try {
870
+ const content = JSON.parse(readFileSync(infoPath, 'utf-8'));
871
+ const requiredFields = ['projectHash', 'cleoVersion', 'lastUpdated'];
872
+ const missing = requiredFields.filter((f) => !(f in content));
873
+ if (missing.length > 0) {
874
+ return {
875
+ id: 'cleo_project_info',
876
+ category: 'scaffold',
877
+ status: 'warning',
878
+ message: `project-info.json missing fields: ${missing.join(', ')}`,
879
+ details: { path: infoPath, missingFields: missing },
880
+ fix: 'cleo init --force',
881
+ };
882
+ }
883
+ return {
884
+ id: 'cleo_project_info',
885
+ category: 'scaffold',
886
+ status: 'passed',
887
+ message: 'project-info.json exists with all required fields',
888
+ details: { path: infoPath, valid: true },
889
+ fix: null,
890
+ };
891
+ }
892
+ catch (err) {
893
+ return {
894
+ id: 'cleo_project_info',
895
+ category: 'scaffold',
896
+ status: 'failed',
897
+ message: `project-info.json is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
898
+ details: { path: infoPath, valid: false },
899
+ fix: 'cleo init --force',
900
+ };
901
+ }
902
+ }
903
+ /**
904
+ * Verify project-context.json exists and is not stale (default: 30 days).
905
+ *
906
+ * @param projectRoot - Absolute path to the project root directory
907
+ * @param staleDays - Age threshold in days before reporting as stale (default: 30)
908
+ * @returns Check result with freshness assessment
909
+ *
910
+ * @remarks
911
+ * Read-only diagnostic. Checks for the `detectedAt` timestamp and compares
912
+ * its age against the staleness threshold. Reports "warning" if stale or
913
+ * missing, "failed" if the file contains invalid JSON.
914
+ *
915
+ * @example
916
+ * ```typescript
917
+ * const check = checkProjectContext('/my/project', 14);
918
+ * if (check.status === 'warning') console.log('Context is stale');
919
+ * ```
920
+ */
921
+ export function checkProjectContext(projectRoot, staleDays = 30) {
922
+ const cleoDir = getCleoDirAbsolute(projectRoot);
923
+ const contextPath = join(cleoDir, 'project-context.json');
924
+ if (!existsSync(contextPath)) {
925
+ return {
926
+ id: 'cleo_project_context',
927
+ category: 'scaffold',
928
+ status: 'warning',
929
+ message: 'project-context.json not found',
930
+ details: { path: contextPath, exists: false },
931
+ fix: 'cleo init --detect',
932
+ };
933
+ }
934
+ try {
935
+ const content = JSON.parse(readFileSync(contextPath, 'utf-8'));
936
+ if (!content.detectedAt) {
937
+ return {
938
+ id: 'cleo_project_context',
939
+ category: 'scaffold',
940
+ status: 'warning',
941
+ message: 'project-context.json missing detectedAt timestamp',
942
+ details: { path: contextPath, hasTimestamp: false },
943
+ fix: 'cleo init --detect',
944
+ };
945
+ }
946
+ const detectedAt = new Date(content.detectedAt);
947
+ const ageMs = Date.now() - detectedAt.getTime();
948
+ const ageDays = ageMs / (1000 * 60 * 60 * 24);
949
+ if (ageDays > staleDays) {
950
+ return {
951
+ id: 'cleo_project_context',
952
+ category: 'scaffold',
953
+ status: 'warning',
954
+ message: `project-context.json is stale (${Math.floor(ageDays)} days old, threshold: ${staleDays})`,
955
+ details: { path: contextPath, ageDays: Math.floor(ageDays), staleDays },
956
+ fix: 'cleo init --detect',
957
+ };
958
+ }
959
+ return {
960
+ id: 'cleo_project_context',
961
+ category: 'scaffold',
962
+ status: 'passed',
963
+ message: `project-context.json is fresh (${Math.floor(ageDays)} days old)`,
964
+ details: { path: contextPath, ageDays: Math.floor(ageDays), staleDays },
965
+ fix: null,
966
+ };
967
+ }
968
+ catch (err) {
969
+ return {
970
+ id: 'cleo_project_context',
971
+ category: 'scaffold',
972
+ status: 'failed',
973
+ message: `project-context.json is not valid JSON: ${err instanceof Error ? err.message : String(err)}`,
974
+ details: { path: contextPath, valid: false },
975
+ fix: 'cleo init --detect',
976
+ };
977
+ }
978
+ }
979
+ /**
980
+ * Verify .cleo/.git checkpoint repository exists.
981
+ *
982
+ * @param projectRoot - Absolute path to the project root directory
983
+ * @returns Check result indicating whether the checkpoint repo is present
984
+ *
985
+ * @remarks
986
+ * Read-only diagnostic. Reports "warning" if the .cleo/.git directory is missing.
987
+ *
988
+ * @example
989
+ * ```typescript
990
+ * const check = checkCleoGitRepo('/my/project');
991
+ * console.log(check.status); // "passed" or "warning"
992
+ * ```
993
+ */
994
+ export function checkCleoGitRepo(projectRoot) {
995
+ const cleoDir = getCleoDirAbsolute(projectRoot);
996
+ const cleoGitDir = join(cleoDir, '.git');
997
+ if (!existsSync(cleoGitDir)) {
998
+ return {
999
+ id: 'cleo_git_repo',
1000
+ category: 'scaffold',
1001
+ status: 'warning',
1002
+ message: '.cleo/.git checkpoint repository not found',
1003
+ details: { path: cleoGitDir, exists: false },
1004
+ fix: 'cleo init',
1005
+ };
1006
+ }
1007
+ return {
1008
+ id: 'cleo_git_repo',
1009
+ category: 'scaffold',
1010
+ status: 'passed',
1011
+ message: '.cleo/.git checkpoint repository exists',
1012
+ details: { path: cleoGitDir, exists: true },
1013
+ fix: null,
1014
+ };
1015
+ }
1016
+ /**
1017
+ * Verify .cleo/tasks.db exists and is non-empty.
1018
+ *
1019
+ * @param projectRoot - Absolute path to the project root directory
1020
+ * @returns Check result with database existence and size information
1021
+ *
1022
+ * @remarks
1023
+ * Read-only diagnostic. Reports "failed" if the file is missing, "warning" if
1024
+ * the file exists but is 0 bytes, "passed" otherwise.
1025
+ *
1026
+ * @example
1027
+ * ```typescript
1028
+ * const check = checkSqliteDb('/my/project');
1029
+ * if (check.status === 'failed') console.log('No database:', check.fix);
1030
+ * ```
1031
+ */
1032
+ export function checkSqliteDb(projectRoot) {
1033
+ const cleoDir = getCleoDirAbsolute(projectRoot);
1034
+ const dbPath = join(cleoDir, 'tasks.db');
1035
+ if (!existsSync(dbPath)) {
1036
+ return {
1037
+ id: 'sqlite_db',
1038
+ category: 'scaffold',
1039
+ status: 'failed',
1040
+ message: 'tasks.db not found',
1041
+ details: { path: dbPath, exists: false },
1042
+ fix: 'cleo init',
1043
+ };
1044
+ }
1045
+ const stat = statSync(dbPath);
1046
+ if (stat.size === 0) {
1047
+ return {
1048
+ id: 'sqlite_db',
1049
+ category: 'scaffold',
1050
+ status: 'warning',
1051
+ message: 'tasks.db exists but is empty (0 bytes)',
1052
+ details: { path: dbPath, exists: true, size: 0 },
1053
+ fix: 'cleo upgrade',
1054
+ };
1055
+ }
1056
+ return {
1057
+ id: 'sqlite_db',
1058
+ category: 'scaffold',
1059
+ status: 'passed',
1060
+ message: `tasks.db exists (${stat.size} bytes)`,
1061
+ details: { path: dbPath, exists: true, size: stat.size },
1062
+ fix: null,
1063
+ };
1064
+ }
1065
+ /**
1066
+ * Create brain.db if missing.
1067
+ * Idempotent: skips if brain.db already exists.
1068
+ *
1069
+ * @param projectRoot - Absolute path to the project root directory
1070
+ * @returns Scaffold result indicating the action taken
1071
+ *
1072
+ * @remarks
1073
+ * Initializes brain.db with Drizzle schema and eagerly creates FTS5 virtual
1074
+ * tables for full-text search (T5698). Also ensures FTS5 tables on pre-existing
1075
+ * databases that may lack them.
1076
+ *
1077
+ * @example
1078
+ * ```typescript
1079
+ * const result = await ensureBrainDb('/my/project');
1080
+ * if (result.action === 'created') console.log('Brain database ready');
1081
+ * ```
1082
+ */
1083
+ export async function ensureBrainDb(projectRoot) {
1084
+ const cleoDir = getCleoDirAbsolute(projectRoot);
1085
+ const dbPath = join(cleoDir, 'brain.db');
1086
+ if (existsSync(dbPath)) {
1087
+ // Ensure FTS5 tables exist even for pre-existing brain.db (T5698)
1088
+ try {
1089
+ const { getBrainNativeDb } = await import('./store/brain-sqlite.js');
1090
+ const nativeDb = getBrainNativeDb();
1091
+ if (nativeDb) {
1092
+ const { ensureFts5Tables } = await import('./memory/brain-search.js');
1093
+ ensureFts5Tables(nativeDb);
1094
+ }
1095
+ }
1096
+ catch {
1097
+ // Non-fatal — FTS5 may not be available
1098
+ }
1099
+ return { action: 'skipped', path: dbPath, details: 'brain.db already exists' };
1100
+ }
1101
+ try {
1102
+ const { getBrainDb, getBrainNativeDb } = await import('./store/brain-sqlite.js');
1103
+ await getBrainDb(projectRoot);
1104
+ // Create FTS5 virtual tables eagerly so search works immediately (T5698)
1105
+ try {
1106
+ const nativeDb = getBrainNativeDb();
1107
+ if (nativeDb) {
1108
+ const { ensureFts5Tables } = await import('./memory/brain-search.js');
1109
+ ensureFts5Tables(nativeDb);
1110
+ }
1111
+ }
1112
+ catch {
1113
+ // FTS5 may not be available in all SQLite builds — non-fatal
1114
+ }
1115
+ return { action: 'created', path: dbPath, details: 'Brain database initialized with FTS5' };
1116
+ }
1117
+ catch (err) {
1118
+ return {
1119
+ action: 'skipped',
1120
+ path: dbPath,
1121
+ details: `Failed to initialize brain.db: ${err instanceof Error ? err.message : String(err)}`,
1122
+ };
1123
+ }
1124
+ }
1125
+ /**
1126
+ * Verify .cleo/brain.db exists and is non-empty.
1127
+ *
1128
+ * @param projectRoot - Absolute path to the project root directory
1129
+ * @returns Check result with database existence and size information
1130
+ *
1131
+ * @remarks
1132
+ * Read-only diagnostic. Reports "failed" if the file is missing, "warning" if
1133
+ * the file exists but is 0 bytes, "passed" otherwise.
1134
+ *
1135
+ * @example
1136
+ * ```typescript
1137
+ * const check = checkBrainDb('/my/project');
1138
+ * console.log(check.status);
1139
+ * ```
1140
+ */
1141
+ export function checkBrainDb(projectRoot) {
1142
+ const cleoDir = getCleoDirAbsolute(projectRoot);
1143
+ const dbPath = join(cleoDir, 'brain.db');
1144
+ if (!existsSync(dbPath)) {
1145
+ return {
1146
+ id: 'brain_db',
1147
+ category: 'scaffold',
1148
+ status: 'failed',
1149
+ message: 'brain.db not found',
1150
+ details: { path: dbPath, exists: false },
1151
+ fix: 'cleo init',
1152
+ };
1153
+ }
1154
+ const stat = statSync(dbPath);
1155
+ if (stat.size === 0) {
1156
+ return {
1157
+ id: 'brain_db',
1158
+ category: 'scaffold',
1159
+ status: 'warning',
1160
+ message: 'brain.db exists but is empty (0 bytes)',
1161
+ details: { path: dbPath, exists: true, size: 0 },
1162
+ fix: 'cleo upgrade',
1163
+ };
1164
+ }
1165
+ return {
1166
+ id: 'brain_db',
1167
+ category: 'scaffold',
1168
+ status: 'passed',
1169
+ message: `brain.db exists (${stat.size} bytes)`,
1170
+ details: { path: dbPath, exists: true, size: stat.size },
1171
+ fix: null,
1172
+ };
1173
+ }
1174
+ /**
1175
+ * Verify .cleo/memory-bridge.md exists.
1176
+ * Warning level if missing (not failure) -- it is auto-generated.
1177
+ *
1178
+ * @param projectRoot - Absolute path to the project root directory
1179
+ * @returns Check result indicating presence of the memory bridge file
1180
+ *
1181
+ * @remarks
1182
+ * Read-only diagnostic. The memory bridge is auto-generated from brain.db
1183
+ * content, so its absence is only a warning (not a failure).
1184
+ *
1185
+ * @example
1186
+ * ```typescript
1187
+ * const check = checkMemoryBridge('/my/project');
1188
+ * if (check.status === 'warning') console.log('Run: cleo refresh-memory');
1189
+ * ```
1190
+ */
1191
+ export function checkMemoryBridge(projectRoot) {
1192
+ const cleoDir = getCleoDirAbsolute(projectRoot);
1193
+ const bridgePath = join(cleoDir, 'memory-bridge.md');
1194
+ if (!existsSync(bridgePath)) {
1195
+ return {
1196
+ id: 'memory_bridge',
1197
+ category: 'scaffold',
1198
+ status: 'warning',
1199
+ message: 'memory-bridge.md not found',
1200
+ details: { path: bridgePath, exists: false },
1201
+ fix: 'cleo init or cleo refresh-memory',
1202
+ };
1203
+ }
1204
+ return {
1205
+ id: 'memory_bridge',
1206
+ category: 'scaffold',
1207
+ status: 'passed',
1208
+ message: 'memory-bridge.md exists',
1209
+ details: { path: bridgePath, exists: true },
1210
+ fix: null,
1211
+ };
1212
+ }
1213
+ // ── Global (~/.cleo) scaffold functions ──────────────────────────────
1214
+ /**
1215
+ * Required subdirectories under the global ~/.cleo/ home.
1216
+ * These are infrastructure directories managed by CLEO itself,
1217
+ * not project-specific data.
1218
+ *
1219
+ * Truly global:
1220
+ * - logs — global log output
1221
+ * - templates — CLEO-INJECTION.md symlink target
1222
+ *
1223
+ * Note: nexus.db lives directly in ~/.cleo/, not a subdir.
1224
+ * Schemas are read at runtime from getPackageRoot()/schemas/ — no copy needed.
1225
+ * Project-level dirs (adrs/, rcasd/, agent-outputs/, backups/) live in .cleo/ only.
1226
+ */
1227
+ export const REQUIRED_GLOBAL_SUBDIRS = [
1228
+ 'logs',
1229
+ 'templates',
1230
+ // CleoOS hub (Phase 1): cross-project recipe library, Pi extensions, CANT workflows
1231
+ 'global-recipes',
1232
+ 'pi-extensions',
1233
+ 'cant-workflows',
1234
+ 'agents',
1235
+ ];
1236
+ /**
1237
+ * Stale entries that must NOT exist at the global ~/.cleo/ level.
1238
+ * These were project-level artefacts accidentally placed in the global home
1239
+ * by old versions of CLEO. They are safe to remove because:
1240
+ * - adrs/, rcasd/, agent-outputs/, backups/ — project-level, live in .cleo/
1241
+ * - sandbox/ — unused
1242
+ * - tasks.db / tasks.db-shm / tasks.db-wal — project-level database files
1243
+ * - brain-worker.pid — stale PID file
1244
+ * - VERSION — redundant (read from package.json)
1245
+ * - schemas/ — now read from npm binary at runtime
1246
+ * - bin/ — stale dev symlink directory
1247
+ */
1248
+ export const STALE_GLOBAL_ENTRIES = [
1249
+ 'adrs',
1250
+ 'rcasd',
1251
+ 'agent-outputs',
1252
+ 'backups',
1253
+ 'sandbox',
1254
+ 'tasks.db',
1255
+ 'tasks.db-shm',
1256
+ 'tasks.db-wal',
1257
+ 'brain-worker.pid',
1258
+ 'VERSION',
1259
+ 'schemas',
1260
+ 'bin',
1261
+ '.install-state',
1262
+ 'templates/templates',
1263
+ ];
1264
+ /**
1265
+ * Ensure the global ~/.cleo/ home directory and its required
1266
+ * subdirectories exist. Idempotent: skips directories that already exist.
1267
+ *
1268
+ * Also removes known stale project-level entries that old CLEO versions
1269
+ * incorrectly placed at the global level (see STALE_GLOBAL_ENTRIES).
1270
+ *
1271
+ * This is the SSoT for global home scaffolding, replacing raw mkdirSync
1272
+ * calls that were previously scattered across global-bootstrap.ts.
1273
+ *
1274
+ * @returns Scaffold result indicating the action taken
1275
+ *
1276
+ * @remarks
1277
+ * Also creates a global config.json from template if absent. Cleans up
1278
+ * stale entries from both the current and legacy (~/.cleo/) global home paths.
1279
+ *
1280
+ * @example
1281
+ * ```typescript
1282
+ * const result = await ensureGlobalHome();
1283
+ * console.log(result.action); // "created" or "skipped"
1284
+ * ```
1285
+ */
1286
+ export async function ensureGlobalHome() {
1287
+ const cleoHome = getCleoHome();
1288
+ const alreadyExists = existsSync(cleoHome);
1289
+ await mkdir(cleoHome, { recursive: true });
1290
+ for (const subdir of REQUIRED_GLOBAL_SUBDIRS) {
1291
+ await mkdir(join(cleoHome, subdir), { recursive: true });
1292
+ }
1293
+ // Create global config.json from template if it doesn't exist
1294
+ const globalConfigPath = join(cleoHome, 'config.json');
1295
+ if (!existsSync(globalConfigPath)) {
1296
+ const templatePath = join(getPackageRoot(), 'templates', 'global-config.template.json');
1297
+ if (existsSync(templatePath)) {
1298
+ const template = readFileSync(templatePath, 'utf-8');
1299
+ const resolved = template.replace('{{SCHEMA_VERSION_GLOBAL_CONFIG}}', '1.0.0');
1300
+ await writeFile(globalConfigPath, resolved);
1301
+ }
1302
+ }
1303
+ // Remove stale project-level entries from both the current global home
1304
+ // AND the legacy ~/.cleo/ path (old CLEO versions used ~/.cleo/ before
1305
+ // env-paths moved it to the XDG-compliant location).
1306
+ const homedir = (await import('node:os')).homedir();
1307
+ const legacyCleoHome = join(homedir, '.cleo');
1308
+ const cleanupPaths = [cleoHome];
1309
+ if (legacyCleoHome !== cleoHome && existsSync(legacyCleoHome)) {
1310
+ cleanupPaths.push(legacyCleoHome);
1311
+ }
1312
+ for (const homeDir of cleanupPaths) {
1313
+ for (const stale of STALE_GLOBAL_ENTRIES) {
1314
+ const stalePath = join(homeDir, stale);
1315
+ if (existsSync(stalePath)) {
1316
+ try {
1317
+ await rm(stalePath, { recursive: true, force: true });
1318
+ // eslint-disable-next-line no-console
1319
+ console.warn(`[CLEO] Removed stale global entry: ${stalePath}`);
1320
+ }
1321
+ catch (err) {
1322
+ // eslint-disable-next-line no-console
1323
+ console.warn(`[CLEO] Could not remove stale global entry ${stalePath}: ${err instanceof Error ? err.message : String(err)}`);
1324
+ }
1325
+ }
1326
+ }
1327
+ }
1328
+ return {
1329
+ action: alreadyExists ? 'skipped' : 'created',
1330
+ path: cleoHome,
1331
+ details: alreadyExists
1332
+ ? 'Global home already existed, ensured subdirs'
1333
+ : `Created ~/.cleo/ with ${REQUIRED_GLOBAL_SUBDIRS.length} subdirectories`,
1334
+ };
1335
+ }
1336
+ /**
1337
+ * Ensure the global CLEO injection template is installed.
1338
+ * Delegates to injection.ts for the template content, but owns the
1339
+ * filesystem write to maintain SSoT for scaffolding.
1340
+ *
1341
+ * Idempotent: skips if the template already exists with correct content.
1342
+ *
1343
+ * @returns Scaffold result indicating the action taken
1344
+ *
1345
+ * @remarks
1346
+ * Lazy-imports injection.ts to avoid circular dependencies. Compares file
1347
+ * content byte-for-byte; repairs if the installed version differs from the
1348
+ * bundled template.
1349
+ *
1350
+ * @example
1351
+ * ```typescript
1352
+ * const result = await ensureGlobalTemplates();
1353
+ * if (result.action === 'repaired') console.log('Template updated');
1354
+ * ```
1355
+ */
1356
+ export async function ensureGlobalTemplates() {
1357
+ // Lazy import to avoid circular dependency (injection imports scaffold)
1358
+ const { getInjectionTemplateContent } = await import('./injection.js');
1359
+ const templatesDir = getCleoTemplatesDir();
1360
+ const injectionPath = join(templatesDir, 'CLEO-INJECTION.md');
1361
+ // Ensure directory exists (idempotent via ensureGlobalHome, but defensive)
1362
+ await mkdir(templatesDir, { recursive: true });
1363
+ const templateContent = getInjectionTemplateContent();
1364
+ if (!templateContent) {
1365
+ return {
1366
+ action: 'skipped',
1367
+ path: injectionPath,
1368
+ details: 'Bundled injection template not found; skipped',
1369
+ };
1370
+ }
1371
+ if (existsSync(injectionPath)) {
1372
+ const existing = readFileSync(injectionPath, 'utf-8');
1373
+ if (existing === templateContent) {
1374
+ return { action: 'skipped', path: injectionPath, details: 'Template already current' };
1375
+ }
1376
+ // Content differs — repair
1377
+ await writeFile(injectionPath, templateContent, 'utf-8');
1378
+ return {
1379
+ action: 'repaired',
1380
+ path: injectionPath,
1381
+ details: 'Updated injection template to match bundled version',
1382
+ };
1383
+ }
1384
+ await writeFile(injectionPath, templateContent, 'utf-8');
1385
+ return { action: 'created', path: injectionPath };
1386
+ }
1387
+ /**
1388
+ * Perform a complete global scaffold operation: ensure home and templates
1389
+ * are all present and current. This is the single entry point for global
1390
+ * infrastructure scaffolding.
1391
+ *
1392
+ * Schemas are NOT copied here — they are read at runtime from the npm
1393
+ * package path (getPackageRoot() + '/schemas/'). Use ensureGlobalSchemas()
1394
+ * explicitly from init or upgrade if a copy is needed for a specific workflow.
1395
+ *
1396
+ * Used by:
1397
+ * - CLI startup (via startupHealthCheck in health.ts)
1398
+ * - init (for first-time global setup)
1399
+ * - upgrade (for global repair)
1400
+ *
1401
+ * @returns Combined scaffold results for home and templates
1402
+ *
1403
+ * @remarks
1404
+ * Calls ensureGlobalHome and ensureGlobalTemplates in sequence.
1405
+ * Schemas are NOT copied here -- they are read at runtime from the npm
1406
+ * package path.
1407
+ *
1408
+ * @example
1409
+ * ```typescript
1410
+ * const { home, templates } = await ensureGlobalScaffold();
1411
+ * console.log(home.action, templates.action);
1412
+ * ```
1413
+ */
1414
+ export async function ensureGlobalScaffold() {
1415
+ const home = await ensureGlobalHome();
1416
+ const templates = await ensureGlobalTemplates();
1417
+ const cleoosHub = await ensureCleoOsHub();
1418
+ return { home, templates, cleoosHub };
1419
+ }
1420
+ // ── CleoOS Hub scaffolding (Phase 1) ─────────────────────────────────
1421
+ /**
1422
+ * Recursively copy a template tree from {@link srcDir} into {@link dstDir}.
1423
+ *
1424
+ * Never overwrites existing files: any file that already exists at the
1425
+ * destination is left untouched, preserving user/agent edits. Missing
1426
+ * subdirectories are created on demand. Symbolic links and special files
1427
+ * are skipped silently.
1428
+ *
1429
+ * @param srcDir - Absolute path to the template source directory
1430
+ * @param dstDir - Absolute path to the destination directory
1431
+ * @returns Counts of files copied vs files that already existed and were kept
1432
+ *
1433
+ * @remarks
1434
+ * Returns `{ copied: 0, kept: 0 }` if the source directory does not exist.
1435
+ * Designed as the workhorse for {@link ensureCleoOsHub} and any other
1436
+ * idempotent template scaffolding that ships with the npm package.
1437
+ *
1438
+ * @example
1439
+ * ```typescript
1440
+ * const { copied, kept } = await copyTemplateTree(
1441
+ * '/usr/lib/node_modules/@cleocode/cleo/templates/cleoos-hub/pi-extensions',
1442
+ * '/home/me/.local/share/cleo/pi-extensions',
1443
+ * );
1444
+ * console.log(`copied ${copied}, kept ${kept}`);
1445
+ * ```
1446
+ */
1447
+ async function copyTemplateTree(srcDir, dstDir) {
1448
+ if (!existsSync(srcDir)) {
1449
+ return { copied: 0, kept: 0 };
1450
+ }
1451
+ await mkdir(dstDir, { recursive: true });
1452
+ let copied = 0;
1453
+ let kept = 0;
1454
+ let entries;
1455
+ try {
1456
+ entries = await readdir(srcDir, { withFileTypes: true });
1457
+ }
1458
+ catch (err) {
1459
+ // eslint-disable-next-line no-console
1460
+ console.warn(`[CLEO] Could not read template dir ${srcDir}: ${err instanceof Error ? err.message : String(err)}`);
1461
+ return { copied: 0, kept: 0 };
1462
+ }
1463
+ for (const entry of entries) {
1464
+ const srcPath = join(srcDir, entry.name);
1465
+ const dstPath = join(dstDir, entry.name);
1466
+ if (entry.isDirectory()) {
1467
+ const sub = await copyTemplateTree(srcPath, dstPath);
1468
+ copied += sub.copied;
1469
+ kept += sub.kept;
1470
+ continue;
1471
+ }
1472
+ if (!entry.isFile()) {
1473
+ // Skip symlinks, sockets, devices, etc.
1474
+ continue;
1475
+ }
1476
+ if (existsSync(dstPath)) {
1477
+ kept += 1;
1478
+ continue;
1479
+ }
1480
+ try {
1481
+ await copyFile(srcPath, dstPath);
1482
+ copied += 1;
1483
+ }
1484
+ catch (err) {
1485
+ // eslint-disable-next-line no-console
1486
+ console.warn(`[CLEO] Could not copy template file ${srcPath} -> ${dstPath}: ${err instanceof Error ? err.message : String(err)}`);
1487
+ }
1488
+ }
1489
+ return { copied, kept };
1490
+ }
1491
+ /**
1492
+ * Resolve the absolute root directory of the bundled CleoOS hub templates.
1493
+ *
1494
+ * The templates ship inside the `@cleocode/cleo` package at
1495
+ * `templates/cleoos-hub/`. Because `scaffold.ts` is authored in
1496
+ * `@cleocode/core` but is also esbuilt directly into the `@cleocode/cleo`
1497
+ * CLI bundle, the runtime location of `import.meta.url` (and therefore
1498
+ * {@link getPackageRoot}) varies across deployment shapes. The candidate
1499
+ * list below covers every layout we ship:
1500
+ *
1501
+ * 1. **Bundled cleo (npm install)** — `dist/cli/index.js` lives at
1502
+ * `node_modules/@cleocode/cleo/dist/cli/index.js`, so
1503
+ * `getPackageRoot()` resolves to `node_modules/@cleocode/cleo/dist`
1504
+ * and `../templates/cleoos-hub` reaches the package's templates dir.
1505
+ * 2. **Bundled cleo (dev workspace via build.mjs)** — `dist/cli/index.js`
1506
+ * lives at `packages/cleo/dist/cli/index.js`; same `../templates/...`
1507
+ * candidate as above.
1508
+ * 3. **Unbundled core (workspace tsc)** — scaffold.ts is loaded as
1509
+ * `packages/core/src/scaffold.ts`, so `getPackageRoot()` resolves to
1510
+ * `packages/core/` and `../cleo/templates/cleoos-hub` reaches the
1511
+ * sibling cleo package.
1512
+ * 4. **Unbundled core (npm install of just @cleocode/core)** — the
1513
+ * scope-sibling layout puts both packages under
1514
+ * `node_modules/@cleocode/`, so the same `../cleo/templates/...`
1515
+ * candidate works.
1516
+ * 5. **Legacy fallback** — if the cleo package ever bundles the templates
1517
+ * directly under core, fall through to
1518
+ * `getPackageRoot()/templates/cleoos-hub`.
1519
+ *
1520
+ * @returns Absolute path to the existing template root, or `null` if none found
1521
+ *
1522
+ * @remarks
1523
+ * Returning `null` allows callers to skip the scaffold gracefully instead of
1524
+ * crashing — important for the never-crash scaffold contract.
1525
+ */
1526
+ function resolveCleoOsHubTemplateRoot() {
1527
+ const packageRoot = getPackageRoot();
1528
+ const candidates = [
1529
+ // Bundled cleo: getPackageRoot() = .../@cleocode/cleo/dist
1530
+ join(packageRoot, '..', 'templates', 'cleoos-hub'),
1531
+ // Unbundled core in workspace or scope-sibling install
1532
+ join(packageRoot, '..', 'cleo', 'templates', 'cleoos-hub'),
1533
+ // Workspace from monorepo root resolution (defensive)
1534
+ join(packageRoot, '..', '..', 'packages', 'cleo', 'templates', 'cleoos-hub'),
1535
+ // Legacy in-place fallback
1536
+ join(packageRoot, 'templates', 'cleoos-hub'),
1537
+ ];
1538
+ return candidates.find((p) => existsSync(p)) ?? null;
1539
+ }
1540
+ /**
1541
+ * Ensure the CleoOS Hub subdirectories exist under the global CLEO home,
1542
+ * and seed all bundled hub templates if they are not already present.
1543
+ *
1544
+ * This is the Phase 1 scaffolding entry point. Idempotent: re-running is
1545
+ * safe and will never overwrite a file that already exists — human and
1546
+ * agent edits to the installed copies are always preserved.
1547
+ *
1548
+ * @returns Scaffold result for the CleoOS hub root
1549
+ *
1550
+ * @remarks
1551
+ * The CleoOS hub currently consists of:
1552
+ * - `global-recipes/` (Justfile Hub: justfile + README)
1553
+ * - `pi-extensions/` (Pi extensions: orchestrator, stage-guide, cant-bridge)
1554
+ * - `cant-workflows/` (CANT workflow library — created empty for agents)
1555
+ * - `agents/` (Global CANT agent definitions — created empty)
1556
+ *
1557
+ * Templates are sourced from the `@cleocode/cleo` package's bundled
1558
+ * `templates/cleoos-hub/` tree. The directory is resolved at runtime through
1559
+ * {@link resolveCleoOsHubTemplateRoot} so the same code path works in the
1560
+ * monorepo workspace and in an installed npm package layout.
1561
+ *
1562
+ * @example
1563
+ * ```typescript
1564
+ * const result = await ensureCleoOsHub();
1565
+ * console.log(result.action); // "created" or "skipped"
1566
+ * ```
1567
+ */
1568
+ export async function ensureCleoOsHub() {
1569
+ const recipesDir = getCleoGlobalRecipesDir();
1570
+ const piExtDir = getCleoPiExtensionsDir();
1571
+ const cantWorkflowsDir = getCleoCantWorkflowsDir();
1572
+ const agentsDir = getCleoGlobalAgentsDir();
1573
+ // Always make sure the hub directory skeleton exists, even if templates
1574
+ // can't be located (so downstream tools writing into them don't ENOENT).
1575
+ try {
1576
+ await mkdir(recipesDir, { recursive: true });
1577
+ await mkdir(piExtDir, { recursive: true });
1578
+ await mkdir(cantWorkflowsDir, { recursive: true });
1579
+ await mkdir(agentsDir, { recursive: true });
1580
+ }
1581
+ catch (err) {
1582
+ return {
1583
+ action: 'skipped',
1584
+ path: recipesDir,
1585
+ details: `Failed to create CleoOS hub directories: ${err instanceof Error ? err.message : String(err)}`,
1586
+ };
1587
+ }
1588
+ const templateRoot = resolveCleoOsHubTemplateRoot();
1589
+ if (!templateRoot) {
1590
+ return {
1591
+ action: 'skipped',
1592
+ path: recipesDir,
1593
+ details: 'CleoOS hub template directory not found in any expected location',
1594
+ };
1595
+ }
1596
+ let piResult;
1597
+ let recipesResult;
1598
+ try {
1599
+ piResult = await copyTemplateTree(join(templateRoot, 'pi-extensions'), piExtDir);
1600
+ recipesResult = await copyTemplateTree(join(templateRoot, 'global-recipes'), recipesDir);
1601
+ }
1602
+ catch (err) {
1603
+ return {
1604
+ action: 'skipped',
1605
+ path: recipesDir,
1606
+ details: `Failed to seed CleoOS hub templates: ${err instanceof Error ? err.message : String(err)}`,
1607
+ };
1608
+ }
1609
+ const totalCopied = piResult.copied + recipesResult.copied;
1610
+ return {
1611
+ action: totalCopied > 0 ? 'created' : 'skipped',
1612
+ path: recipesDir,
1613
+ details: `pi-extensions: ${piResult.copied} created/${piResult.kept} kept, global-recipes: ${recipesResult.copied} created/${recipesResult.kept} kept`,
1614
+ };
1615
+ }
1616
+ // ── Global check* functions (read-only diagnostics) ──────────────────
1617
+ /**
1618
+ * Check that the global ~/.cleo/ home and its required subdirectories exist.
1619
+ * Read-only: no side effects.
1620
+ *
1621
+ * @returns Check result with status and missing subdirectory details
1622
+ *
1623
+ * @remarks
1624
+ * Reports "failed" if the home directory itself is absent, "warning" if
1625
+ * subdirectories are missing, "passed" if all infrastructure is present.
1626
+ *
1627
+ * @example
1628
+ * ```typescript
1629
+ * const check = checkGlobalHome();
1630
+ * if (check.status !== 'passed') console.log(check.fix);
1631
+ * ```
1632
+ */
1633
+ export function checkGlobalHome() {
1634
+ const cleoHome = getCleoHome();
1635
+ if (!existsSync(cleoHome)) {
1636
+ return {
1637
+ id: 'global_home',
1638
+ category: 'global',
1639
+ status: 'failed',
1640
+ message: 'Global ~/.cleo/ directory not found',
1641
+ details: { path: cleoHome, exists: false },
1642
+ fix: 'cleo init',
1643
+ };
1644
+ }
1645
+ const missingDirs = REQUIRED_GLOBAL_SUBDIRS.filter((dir) => !existsSync(join(cleoHome, dir)));
1646
+ if (missingDirs.length > 0) {
1647
+ return {
1648
+ id: 'global_home',
1649
+ category: 'global',
1650
+ status: 'warning',
1651
+ message: `Global home exists but missing subdirs: ${missingDirs.join(', ')}`,
1652
+ details: { path: cleoHome, exists: true, missingDirs },
1653
+ fix: 'cleo upgrade --include-global',
1654
+ };
1655
+ }
1656
+ return {
1657
+ id: 'global_home',
1658
+ category: 'global',
1659
+ status: 'passed',
1660
+ message: 'Global ~/.cleo/ home and subdirectories present',
1661
+ details: { path: cleoHome, exists: true, subdirs: REQUIRED_GLOBAL_SUBDIRS.length },
1662
+ fix: null,
1663
+ };
1664
+ }
1665
+ /**
1666
+ * Check that the global injection template is present and current.
1667
+ * Read-only: no side effects.
1668
+ *
1669
+ * @returns Check result with template version and sync status
1670
+ *
1671
+ * @remarks
1672
+ * Also checks for version drift between the XDG-compliant path and the
1673
+ * legacy ~/.cleo/templates/ path. Reports "warning" if versions differ.
1674
+ *
1675
+ * @example
1676
+ * ```typescript
1677
+ * const check = checkGlobalTemplates();
1678
+ * console.log(check.details.version);
1679
+ * ```
1680
+ */
1681
+ export function checkGlobalTemplates() {
1682
+ const templatesDir = getCleoTemplatesDir();
1683
+ const injectionPath = join(templatesDir, 'CLEO-INJECTION.md');
1684
+ if (!existsSync(injectionPath)) {
1685
+ return {
1686
+ id: 'global_templates',
1687
+ category: 'global',
1688
+ status: 'failed',
1689
+ message: 'CLEO-INJECTION.md template not found in global templates',
1690
+ details: { path: injectionPath, exists: false },
1691
+ fix: 'cleo init',
1692
+ };
1693
+ }
1694
+ // Check version sync between XDG and legacy paths
1695
+ const xdgContent = readFileSync(injectionPath, 'utf-8');
1696
+ const xdgVersion = xdgContent.match(/^Version:\s*(.+)$/m)?.[1]?.trim();
1697
+ const home = getHomedir();
1698
+ const legacyPath = join(home, '.cleo', 'templates', 'CLEO-INJECTION.md');
1699
+ if (existsSync(legacyPath)) {
1700
+ const legacyContent = readFileSync(legacyPath, 'utf-8');
1701
+ const legacyVersion = legacyContent.match(/^Version:\s*(.+)$/m)?.[1]?.trim();
1702
+ if (legacyVersion && xdgVersion && legacyVersion !== xdgVersion) {
1703
+ return {
1704
+ id: 'global_templates',
1705
+ category: 'global',
1706
+ status: 'warning',
1707
+ message: `Legacy template version (${legacyVersion}) out of sync with XDG (${xdgVersion})`,
1708
+ details: { path: injectionPath, exists: true, xdgVersion, legacyVersion, legacyPath },
1709
+ fix: 'npm install -g @cleocode/cleo (reinstall syncs both paths)',
1710
+ };
1711
+ }
1712
+ }
1713
+ return {
1714
+ id: 'global_templates',
1715
+ category: 'global',
1716
+ status: 'passed',
1717
+ message: `Global injection template present (v${xdgVersion ?? 'unknown'})`,
1718
+ details: { path: injectionPath, exists: true, version: xdgVersion },
1719
+ fix: null,
1720
+ };
1721
+ }
1722
+ /**
1723
+ * Check that the project log directory exists.
1724
+ * Read-only: no side effects.
1725
+ *
1726
+ * @param projectRoot - Absolute path to the project root directory
1727
+ * @returns Check result indicating whether .cleo/logs/ is present
1728
+ *
1729
+ * @remarks
1730
+ * Reports "warning" if the log directory is absent, "passed" otherwise.
1731
+ *
1732
+ * @example
1733
+ * ```typescript
1734
+ * const check = checkLogDir('/my/project');
1735
+ * console.log(check.status);
1736
+ * ```
1737
+ */
1738
+ export function checkLogDir(projectRoot) {
1739
+ const logDir = join(getCleoDirAbsolute(projectRoot), 'logs');
1740
+ if (!existsSync(logDir)) {
1741
+ return {
1742
+ id: 'log_dir',
1743
+ category: 'scaffold',
1744
+ status: 'warning',
1745
+ message: 'Log directory .cleo/logs/ not found',
1746
+ details: { path: logDir, exists: false },
1747
+ fix: 'cleo upgrade',
1748
+ };
1749
+ }
1750
+ return {
1751
+ id: 'log_dir',
1752
+ category: 'scaffold',
1753
+ status: 'passed',
1754
+ message: 'Log directory .cleo/logs/ present',
1755
+ details: { path: logDir, exists: true },
1756
+ fix: null,
1757
+ };
1758
+ }
1759
+ //# sourceMappingURL=scaffold.js.map