@cleocode/core 2026.4.57 → 2026.4.59

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