cc-codeconductor 1.4.2 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. package/README.md +52 -14
  2. package/dist/domain/product/entities.d.ts +1 -1
  3. package/dist/index.js +811 -354
  4. package/dist/library.js +47 -3
  5. package/dist/validation/schemas.d.ts +183 -21
  6. package/docs/generated/cli.md +16 -0
  7. package/package.json +1 -1
  8. package/presets/agy/README.md +1 -1
  9. package/presets/agy/hooks.json +1 -1
  10. package/presets/agy/scripts/invoke-hook.cjs +20 -5
  11. package/presets/agy/settings.json +1 -1
  12. package/presets/agy/skills/api-versioning/SKILL.md +394 -0
  13. package/presets/agy/skills/astro/SKILL.md +318 -0
  14. package/presets/agy/skills/auth-token-inspector/SKILL.md +30 -0
  15. package/presets/agy/skills/cc-pagespeed/SKILL.md +2 -3
  16. package/presets/agy/skills/code-review/SKILL.md +207 -0
  17. package/presets/agy/skills/django-orm/SKILL.md +460 -0
  18. package/presets/agy/skills/django-uv/SKILL.md +405 -0
  19. package/presets/agy/skills/drizzle-schema-architect/SKILL.md +50 -0
  20. package/presets/agy/skills/fastapi-pydantic-strict/SKILL.md +43 -0
  21. package/presets/agy/skills/jpa-nplusone-detector/SKILL.md +45 -0
  22. package/presets/agy/skills/jpa-postgres/SKILL.md +623 -0
  23. package/presets/agy/skills/livewire-alpine-bridge/SKILL.md +35 -0
  24. package/presets/agy/skills/nextjs-typescript/SKILL.md +390 -0
  25. package/presets/agy/skills/python/SKILL.md +611 -0
  26. package/presets/agy/skills/seo-analytics-injector/SKILL.md +43 -0
  27. package/presets/agy/skills/spring-auth-auditor/SKILL.md +29 -0
  28. package/presets/agy/skills/spring-boot-feature/SKILL.md +563 -0
  29. package/presets/agy/skills/spring-boot-testing-strategy/SKILL.md +475 -0
  30. package/presets/agy/skills/tailwind-responsive-auditor/SKILL.md +29 -0
  31. package/presets/agy/skills/tdd-mutation-tester/SKILL.md +27 -0
  32. package/presets/agy/workflows/cc-api-contract.md +4 -5
  33. package/presets/agy/workflows/cc-backlog.md +4 -5
  34. package/presets/agy/workflows/cc-clarify.md +4 -5
  35. package/presets/agy/workflows/cc-council.md +4 -5
  36. package/presets/agy/workflows/cc-db-migration.md +4 -5
  37. package/presets/agy/workflows/cc-explore.md +4 -5
  38. package/presets/agy/workflows/cc-feature.md +4 -5
  39. package/presets/agy/workflows/cc-fix.md +4 -5
  40. package/presets/agy/workflows/cc-handoff.md +8 -5
  41. package/presets/agy/workflows/cc-iterative.md +4 -5
  42. package/presets/agy/workflows/cc-odd.md +4 -3
  43. package/presets/agy/workflows/cc-openspec.md +5 -6
  44. package/presets/agy/workflows/cc-pagespeed.md +6 -8
  45. package/presets/agy/workflows/cc-prototype.md +4 -5
  46. package/presets/agy/workflows/cc-refactor.md +4 -5
  47. package/presets/agy/workflows/cc-review.md +35 -5
  48. package/presets/agy/workflows/cc-scorecard.md +4 -5
  49. package/presets/agy/workflows/cc-security.md +5 -6
  50. package/presets/agy/workflows/cc-spec-mutation.md +4 -5
  51. package/presets/agy/workflows/cc-tdd-cycle.md +4 -5
  52. package/presets/agy/workflows/cc-test-plan.md +4 -5
  53. package/presets/agy/workflows/cc-triage.md +4 -5
  54. package/presets/claude/CLAUDE.md +6 -5
  55. package/presets/claude/commands/cc/api-contract.md +4 -5
  56. package/presets/claude/commands/cc/backlog.md +4 -5
  57. package/presets/claude/commands/cc/clarify.md +4 -5
  58. package/presets/claude/commands/cc/council.md +4 -5
  59. package/presets/claude/commands/cc/db-migration.md +4 -5
  60. package/presets/claude/commands/cc/explore.md +4 -5
  61. package/presets/claude/commands/cc/feature.md +4 -5
  62. package/presets/claude/commands/cc/fix.md +4 -5
  63. package/presets/claude/commands/cc/handoff.md +8 -5
  64. package/presets/claude/commands/cc/iterative.md +4 -5
  65. package/presets/claude/commands/cc/odd.md +4 -3
  66. package/presets/claude/commands/cc/openspec.md +23 -7
  67. package/presets/claude/commands/cc/pagespeed.md +4 -5
  68. package/presets/claude/commands/cc/prototype.md +4 -5
  69. package/presets/claude/commands/cc/refactor.md +4 -5
  70. package/presets/claude/commands/cc/review.md +37 -5
  71. package/presets/claude/commands/cc/scorecard.md +4 -5
  72. package/presets/claude/commands/cc/security.md +4 -5
  73. package/presets/claude/commands/cc/spec-mutation.md +4 -5
  74. package/presets/claude/commands/cc/tdd-cycle.md +4 -5
  75. package/presets/claude/commands/cc/test-plan.md +4 -5
  76. package/presets/claude/commands/cc/triage.md +4 -5
  77. package/presets/claude/settings.json +33 -79
  78. package/presets/claude/skills/android/SKILL.md +1 -1
  79. package/presets/claude/skills/api-versioning/SKILL.md +1 -1
  80. package/presets/claude/skills/astro/SKILL.md +318 -0
  81. package/presets/claude/skills/auth-token-inspector/SKILL.md +30 -0
  82. package/presets/claude/skills/code-review/SKILL.md +207 -0
  83. package/presets/claude/skills/django-orm/SKILL.md +1 -1
  84. package/presets/claude/skills/django-testing/SKILL.md +1 -1
  85. package/presets/claude/skills/django-uv/SKILL.md +405 -0
  86. package/presets/claude/skills/drizzle-schema-architect/SKILL.md +50 -0
  87. package/presets/claude/skills/fastapi-pydantic-strict/SKILL.md +43 -0
  88. package/presets/claude/skills/jpa-nplusone-detector/SKILL.md +45 -0
  89. package/presets/claude/skills/jpa-postgres/SKILL.md +1 -1
  90. package/presets/claude/skills/livewire-alpine-bridge/SKILL.md +35 -0
  91. package/presets/claude/skills/nextjs-typescript/SKILL.md +390 -0
  92. package/presets/claude/skills/pagespeed-perf/SKILL.md +1 -1
  93. package/presets/claude/skills/python/SKILL.md +1 -1
  94. package/presets/claude/skills/python-django-stack/SKILL.md +1 -1
  95. package/presets/claude/skills/python-fastapi-stack/SKILL.md +1 -1
  96. package/presets/claude/skills/security/SKILL.md +1 -1
  97. package/presets/claude/skills/seo-analytics-injector/SKILL.md +43 -0
  98. package/presets/claude/skills/spring-auth-auditor/SKILL.md +29 -0
  99. package/presets/claude/skills/spring-boot-feature/SKILL.md +1 -1
  100. package/presets/claude/skills/spring-boot-kotlin/SKILL.md +1 -1
  101. package/presets/claude/skills/spring-boot-testing-strategy/SKILL.md +475 -0
  102. package/presets/claude/skills/sqlalchemy/SKILL.md +1 -1
  103. package/presets/claude/skills/tailwind-responsive-auditor/SKILL.md +29 -0
  104. package/presets/claude/skills/tdd-mutation-tester/SKILL.md +27 -0
  105. package/presets/claude/skills/testing-strategy/SKILL.md +1 -1
  106. package/presets/codex/config.toml +2 -0
  107. package/presets/codex/skills/android/SKILL.md +1 -1
  108. package/presets/codex/skills/api-versioning/SKILL.md +1 -1
  109. package/presets/codex/skills/astro/SKILL.md +318 -0
  110. package/presets/codex/skills/auth-token-inspector/SKILL.md +30 -0
  111. package/presets/codex/skills/cc-api-contract/SKILL.md +4 -5
  112. package/presets/codex/skills/cc-backlog/SKILL.md +4 -5
  113. package/presets/codex/skills/cc-clarify/SKILL.md +4 -5
  114. package/presets/codex/skills/cc-council/SKILL.md +10 -5
  115. package/presets/codex/skills/cc-db-migration/SKILL.md +4 -5
  116. package/presets/codex/skills/cc-explore/SKILL.md +4 -5
  117. package/presets/codex/skills/cc-feature/SKILL.md +4 -5
  118. package/presets/codex/skills/cc-fix/SKILL.md +4 -5
  119. package/presets/codex/skills/cc-handoff/SKILL.md +8 -5
  120. package/presets/codex/skills/cc-iterative/SKILL.md +4 -5
  121. package/presets/codex/skills/cc-odd/SKILL.md +4 -3
  122. package/presets/codex/skills/cc-openspec/SKILL.md +12 -7
  123. package/presets/codex/skills/cc-pagespeed/SKILL.md +6 -8
  124. package/presets/codex/skills/cc-prototype/SKILL.md +4 -5
  125. package/presets/codex/skills/cc-refactor/SKILL.md +4 -5
  126. package/presets/codex/skills/cc-review/SKILL.md +35 -5
  127. package/presets/codex/skills/cc-scorecard/SKILL.md +4 -5
  128. package/presets/codex/skills/cc-security/SKILL.md +5 -6
  129. package/presets/codex/skills/cc-spec-mutation/SKILL.md +4 -5
  130. package/presets/codex/skills/cc-tdd-cycle/SKILL.md +4 -5
  131. package/presets/codex/skills/cc-test-plan/SKILL.md +4 -5
  132. package/presets/codex/skills/cc-triage/SKILL.md +4 -5
  133. package/presets/codex/skills/code-review/SKILL.md +207 -0
  134. package/presets/codex/skills/django-orm/SKILL.md +1 -1
  135. package/presets/codex/skills/django-testing/SKILL.md +1 -1
  136. package/presets/codex/skills/django-uv/SKILL.md +405 -0
  137. package/presets/codex/skills/drizzle-schema-architect/SKILL.md +50 -0
  138. package/presets/codex/skills/fastapi-pydantic-strict/SKILL.md +43 -0
  139. package/presets/codex/skills/jpa-nplusone-detector/SKILL.md +45 -0
  140. package/presets/codex/skills/jpa-postgres/SKILL.md +1 -1
  141. package/presets/codex/skills/livewire-alpine-bridge/SKILL.md +35 -0
  142. package/presets/codex/skills/nextjs-typescript/SKILL.md +390 -0
  143. package/presets/codex/skills/pagespeed-perf/SKILL.md +1 -1
  144. package/presets/codex/skills/python/SKILL.md +1 -1
  145. package/presets/codex/skills/python-django-stack/SKILL.md +1 -1
  146. package/presets/codex/skills/python-fastapi-stack/SKILL.md +1 -1
  147. package/presets/codex/skills/security-ai-llm/SKILL.md +43 -0
  148. package/presets/codex/skills/security-blue-team/SKILL.md +43 -0
  149. package/presets/codex/skills/security-cloud/SKILL.md +43 -0
  150. package/presets/codex/skills/security-crypto/SKILL.md +43 -0
  151. package/presets/codex/skills/security-exploit-dev/SKILL.md +45 -0
  152. package/presets/codex/skills/security-grc/SKILL.md +43 -0
  153. package/presets/codex/skills/security-incident-response/SKILL.md +45 -0
  154. package/presets/codex/skills/security-log-analysis/SKILL.md +43 -0
  155. package/presets/codex/skills/security-malware-analysis/SKILL.md +44 -0
  156. package/presets/codex/skills/security-mobile/SKILL.md +43 -0
  157. package/presets/codex/skills/security-network/SKILL.md +43 -0
  158. package/presets/codex/skills/security-ot-ics/SKILL.md +43 -0
  159. package/presets/codex/skills/security-recon/SKILL.md +45 -0
  160. package/presets/codex/skills/security-red-team/SKILL.md +44 -0
  161. package/presets/codex/skills/security-reverse-engineering/SKILL.md +44 -0
  162. package/presets/codex/skills/security-soc-automation/SKILL.md +43 -0
  163. package/presets/codex/skills/security-threat-hunting/SKILL.md +43 -0
  164. package/presets/codex/skills/security-vuln-assessment/SKILL.md +45 -0
  165. package/presets/codex/skills/security-web/SKILL.md +44 -0
  166. package/presets/codex/skills/seo-analytics-injector/SKILL.md +43 -0
  167. package/presets/codex/skills/spring-auth-auditor/SKILL.md +29 -0
  168. package/presets/codex/skills/spring-boot-feature/SKILL.md +2 -2
  169. package/presets/codex/skills/spring-boot-kotlin/SKILL.md +1 -1
  170. package/presets/codex/skills/spring-boot-testing-strategy/SKILL.md +475 -0
  171. package/presets/codex/skills/sqlalchemy/SKILL.md +1 -1
  172. package/presets/codex/skills/tailwind-responsive-auditor/SKILL.md +29 -0
  173. package/presets/codex/skills/tdd-mutation-tester/SKILL.md +27 -0
  174. package/presets/codex/skills/testing-strategy/SKILL.md +1 -1
  175. package/presets/cursor/commands/cc/api-contract.md +4 -5
  176. package/presets/cursor/commands/cc/backlog.md +4 -5
  177. package/presets/cursor/commands/cc/clarify.md +4 -5
  178. package/presets/cursor/commands/cc/council.md +4 -5
  179. package/presets/cursor/commands/cc/db-migration.md +4 -5
  180. package/presets/cursor/commands/cc/explore.md +4 -5
  181. package/presets/cursor/commands/cc/feature.md +4 -5
  182. package/presets/cursor/commands/cc/fix.md +4 -5
  183. package/presets/cursor/commands/cc/handoff.md +8 -5
  184. package/presets/cursor/commands/cc/iterative.md +4 -5
  185. package/presets/cursor/commands/cc/odd.md +4 -3
  186. package/presets/cursor/commands/cc/openspec.md +6 -7
  187. package/presets/cursor/commands/cc/pagespeed.md +6 -8
  188. package/presets/cursor/commands/cc/prototype.md +4 -5
  189. package/presets/cursor/commands/cc/refactor.md +4 -5
  190. package/presets/cursor/commands/cc/review.md +35 -5
  191. package/presets/cursor/commands/cc/scorecard.md +4 -5
  192. package/presets/cursor/commands/cc/security.md +5 -6
  193. package/presets/cursor/commands/cc/spec-mutation.md +4 -5
  194. package/presets/cursor/commands/cc/tdd-cycle.md +4 -5
  195. package/presets/cursor/commands/cc/test-plan.md +4 -5
  196. package/presets/cursor/commands/cc/triage.md +4 -5
  197. package/presets/cursor/skills/android/SKILL.md +1 -1
  198. package/presets/cursor/skills/api-versioning/SKILL.md +2 -1
  199. package/presets/cursor/skills/astro/SKILL.md +1 -1
  200. package/presets/cursor/skills/auth-token-inspector/SKILL.md +1 -1
  201. package/presets/cursor/skills/code-review/SKILL.md +1 -1
  202. package/presets/cursor/skills/django-orm/SKILL.md +3 -5
  203. package/presets/cursor/skills/django-testing/SKILL.md +1 -1
  204. package/presets/cursor/skills/django-uv/SKILL.md +1 -1
  205. package/presets/cursor/skills/drizzle-schema-architect/SKILL.md +1 -1
  206. package/presets/cursor/skills/fastapi-pydantic-strict/SKILL.md +1 -1
  207. package/presets/cursor/skills/jpa-nplusone-detector/SKILL.md +1 -1
  208. package/presets/cursor/skills/jpa-postgres/SKILL.md +2 -4
  209. package/presets/cursor/skills/livewire-alpine-bridge/SKILL.md +1 -1
  210. package/presets/cursor/skills/nextjs-typescript/SKILL.md +1 -1
  211. package/presets/cursor/skills/pagespeed-perf/SKILL.md +1 -1
  212. package/presets/cursor/skills/python/SKILL.md +6 -7
  213. package/presets/cursor/skills/python-django-stack/SKILL.md +1 -1
  214. package/presets/cursor/skills/python-fastapi-stack/SKILL.md +1 -1
  215. package/presets/cursor/skills/security/SKILL.md +1 -1
  216. package/presets/cursor/skills/seo-analytics-injector/SKILL.md +1 -1
  217. package/presets/cursor/skills/spring-auth-auditor/SKILL.md +1 -1
  218. package/presets/cursor/skills/spring-boot-feature/SKILL.md +2 -4
  219. package/presets/cursor/skills/spring-boot-kotlin/SKILL.md +1 -1
  220. package/presets/cursor/skills/spring-boot-testing-strategy/SKILL.md +1 -1
  221. package/presets/cursor/skills/sqlalchemy/SKILL.md +1 -1
  222. package/presets/cursor/skills/tailwind-responsive-auditor/SKILL.md +1 -1
  223. package/presets/cursor/skills/tdd-mutation-tester/SKILL.md +1 -1
  224. package/presets/gemini/commands/cc/api-contract.toml +4 -5
  225. package/presets/gemini/commands/cc/backlog.toml +4 -5
  226. package/presets/gemini/commands/cc/clarify.toml +4 -5
  227. package/presets/gemini/commands/cc/council.toml +4 -5
  228. package/presets/gemini/commands/cc/db-migration.toml +4 -5
  229. package/presets/gemini/commands/cc/explore.toml +4 -5
  230. package/presets/gemini/commands/cc/feature.toml +4 -5
  231. package/presets/gemini/commands/cc/fix.toml +4 -5
  232. package/presets/gemini/commands/cc/handoff.toml +8 -5
  233. package/presets/gemini/commands/cc/iterative.toml +4 -5
  234. package/presets/gemini/commands/cc/odd.toml +4 -3
  235. package/presets/gemini/commands/cc/openspec.toml +6 -7
  236. package/presets/gemini/commands/cc/pagespeed.toml +6 -8
  237. package/presets/gemini/commands/cc/prototype.toml +4 -5
  238. package/presets/gemini/commands/cc/refactor.toml +4 -5
  239. package/presets/gemini/commands/cc/review.toml +35 -5
  240. package/presets/gemini/commands/cc/scorecard.toml +4 -5
  241. package/presets/gemini/commands/cc/security.toml +5 -6
  242. package/presets/gemini/commands/cc/spec-mutation.toml +4 -5
  243. package/presets/gemini/commands/cc/tdd-cycle.toml +4 -5
  244. package/presets/gemini/commands/cc/test-plan.toml +4 -5
  245. package/presets/gemini/commands/cc/triage.toml +4 -5
  246. package/presets/opencode/README.md +45 -52
  247. package/presets/opencode/commands/cc-api-contract.md +4 -5
  248. package/presets/opencode/commands/cc-backlog.md +4 -5
  249. package/presets/opencode/commands/cc-clarify.md +4 -5
  250. package/presets/opencode/commands/cc-council.md +4 -5
  251. package/presets/opencode/commands/cc-db-migration.md +4 -5
  252. package/presets/opencode/commands/cc-explore.md +4 -5
  253. package/presets/opencode/commands/cc-feature.md +4 -5
  254. package/presets/opencode/commands/cc-fix.md +4 -5
  255. package/presets/opencode/commands/cc-handoff.md +8 -5
  256. package/presets/opencode/commands/cc-iterative.md +4 -5
  257. package/presets/opencode/commands/cc-odd.md +4 -3
  258. package/presets/opencode/commands/cc-openspec.md +6 -7
  259. package/presets/opencode/commands/cc-pagespeed.md +6 -8
  260. package/presets/opencode/commands/cc-prototype.md +4 -5
  261. package/presets/opencode/commands/cc-refactor.md +4 -5
  262. package/presets/opencode/commands/cc-review.md +35 -5
  263. package/presets/opencode/commands/cc-scorecard.md +4 -5
  264. package/presets/opencode/commands/cc-security.md +5 -6
  265. package/presets/opencode/commands/cc-spec-mutation.md +4 -5
  266. package/presets/opencode/commands/cc-tdd-cycle.md +4 -5
  267. package/presets/opencode/commands/cc-test-plan.md +4 -5
  268. package/presets/opencode/commands/cc-triage.md +4 -5
  269. package/presets/opencode/opencode.jsonc +1 -1
  270. package/presets/opencode/skills/android/SKILL.md +1 -1
  271. package/presets/opencode/skills/api-versioning/SKILL.md +2 -1
  272. package/presets/opencode/skills/astro/SKILL.md +1 -1
  273. package/presets/opencode/skills/auth-token-inspector/SKILL.md +1 -1
  274. package/presets/opencode/skills/code-review/SKILL.md +1 -1
  275. package/presets/opencode/skills/django-orm/SKILL.md +3 -3
  276. package/presets/opencode/skills/django-testing/SKILL.md +1 -1
  277. package/presets/opencode/skills/django-uv/SKILL.md +1 -1
  278. package/presets/opencode/skills/drizzle-schema-architect/SKILL.md +1 -1
  279. package/presets/opencode/skills/fastapi-pydantic-strict/SKILL.md +1 -1
  280. package/presets/opencode/skills/jpa-nplusone-detector/SKILL.md +1 -1
  281. package/presets/opencode/skills/jpa-postgres/SKILL.md +2 -1
  282. package/presets/opencode/skills/livewire-alpine-bridge/SKILL.md +1 -1
  283. package/presets/opencode/skills/nextjs-typescript/SKILL.md +1 -1
  284. package/presets/opencode/skills/pagespeed-perf/SKILL.md +1 -1
  285. package/presets/opencode/skills/python/SKILL.md +6 -5
  286. package/presets/opencode/skills/python-django-stack/SKILL.md +1 -1
  287. package/presets/opencode/skills/python-fastapi-stack/SKILL.md +1 -1
  288. package/presets/opencode/skills/security/SKILL.md +1 -1
  289. package/presets/opencode/skills/seo-analytics-injector/SKILL.md +1 -1
  290. package/presets/opencode/skills/spring-auth-auditor/SKILL.md +1 -1
  291. package/presets/opencode/skills/spring-boot-feature/SKILL.md +2 -1
  292. package/presets/opencode/skills/spring-boot-kotlin/SKILL.md +1 -1
  293. package/presets/opencode/skills/spring-boot-testing-strategy/SKILL.md +1 -1
  294. package/presets/opencode/skills/sqlalchemy/SKILL.md +1 -1
  295. package/presets/opencode/skills/tailwind-responsive-auditor/SKILL.md +1 -1
  296. package/presets/opencode/skills/tdd-mutation-tester/SKILL.md +1 -1
  297. package/presets/seo-hotel/skills/astro-seo/SKILL.md +1 -1
  298. package/presets/seo-hotel/skills/geo-readiness/SKILL.md +1 -1
  299. package/presets/seo-hotel/skills/off-page/SKILL.md +1 -1
  300. package/presets/seo-hotel/skills/schema-validator/SKILL.md +1 -1
  301. package/presets/seo-hotel/skills/seo-audit/SKILL.md +1 -1
  302. package/presets/shared/invoke-hook.cjs +20 -5
  303. package/src/presets/manifests/codex.yml +3 -0
  304. package/src/presets/models/roles.yml +54 -56
  305. package/src/presets/shared-skills.yml +58 -22
package/dist/index.js CHANGED
@@ -72,7 +72,7 @@ var package_default;
72
72
  var init_package = __esm(() => {
73
73
  package_default = {
74
74
  name: "cc-codeconductor",
75
- version: "1.4.2",
75
+ version: "1.5.1",
76
76
  description: "A multi-agent orchestration framework for AI-assisted software engineering workflows.",
77
77
  keywords: [
78
78
  "ai",
@@ -11216,9 +11216,11 @@ __export(exports_schemas, {
11216
11216
  ExecutionContextSchema: () => ExecutionContextSchema,
11217
11217
  ExecutionProfileNameSchema: () => ExecutionProfileNameSchema,
11218
11218
  ExecutionProfileSchema: () => ExecutionProfileSchema,
11219
+ FixIntakeOutputSchema: () => FixIntakeOutputSchema,
11219
11220
  GoalGraphSchema: () => GoalGraphSchema,
11220
11221
  GoalTaskSchema: () => GoalTaskSchema,
11221
11222
  GraphRelationSchema: () => GraphRelationSchema,
11223
+ HandoffEnvelopeSchema: () => HandoffEnvelopeSchema,
11222
11224
  HarnessCatalogEntrySchema: () => HarnessCatalogEntrySchema,
11223
11225
  HarnessCatalogSchema: () => HarnessCatalogSchema,
11224
11226
  HarnessComponentIdSchema: () => HarnessComponentIdSchema,
@@ -11439,7 +11441,7 @@ function validateProductEvent(data) {
11439
11441
  function validateSkillsRegistry(data) {
11440
11442
  return SkillsRegistrySchema.parse(data);
11441
11443
  }
11442
- var CouncilAgentSpecSchema, CouncilSpecSchema, ProjectProfileSchema, CompileCheckConfigSchema, LoopConfigSchema, CodeConductorConfigSchema, RunnerTargetSchema, InstallStrategySchema, ManifestEntrySchema, InstallManifestSchema, ToolProviderNamesSchema, PermissionProviderNamesSchema, ModelConfigSchema, SkillFrontmatterSchema, CommandFrontmatterSchema, TargetCapabilitiesSchema, ContractTargetSchema, ContractFormatSchema, AgentContractSchema, CouncilFindingSchema, CouncilVerdictInputSchema, ConsensusConfigSchema, CouncilVerdictSchema, ClaudeAgentFileSchema, OpenCodeAgentFileSchema, SentryStackFrameSchema, SentryWebhookSchema, MemoryPointerSchema, MemoryIndexSchema, GoalTaskSchema, GoalGraphSchema, ConfidenceLevelSchema, ProductNodeTypeSchema, GraphRelationSchema, KnowledgeEntitySchema, DecisionSchema, EvidenceSchema, CanonicalTaskCardStatusSchema, CanonicalTaskCardSchema, ProductGraphNodeSchema, ProductGraphEdgeSchema, ProductGraphSchema, ProductMetaSchema, ProductEventTypeSchema, ProductEventSchema, OperationalStateSchema, StrategicMemorySchema, ImpactReportSchema, BusinessReviewOutputSchema, VerificationReportSchema, BacklogStatusSchema, BacklogTypeSchema, BacklogPrioritySchema, BacklogGlobalSchema, BacklogItemSchema, BacklogDocumentSchema, OpenspecTaskCardPhaseSchema, OpenspecTaskCardStatusSchema, OpenspecTaskCardSchema, OpenspecStateSchema, SpecAnalyzeFindingSchema, SpecAnalyzeReportSchema, WorkflowCommandSchema, CcepOutputFormatSchema, CommandEnvelopeSchema, WorkflowPhaseSchema, WorkflowRiskRuleSchema, WorkflowProfileSchema, ExecutionContextSchema, PlannerOutputSchema, AgentArtifactSchema, AgentOutputSchema, ImplementerTestsSchema, ImplementerOutputSchema, ReviewAxisSchema, ReviewFindingSchema, ReviewerOutputSchema, TechnicalPlanOutputSchema, ScorecardVerdictSchema, ScorecardCriterionIdSchema, ScorecardCriterionSchema, ScorecardRecordSchema, TaskOutcomeSourceSchema, TaskOutcomeStatusSchema, DeliveryRouteSchema, TokenUsageSchema, DeliveryTelemetrySchema, DeliveryLedgerTaskSchema, DeliveryLedgerSchema, DeliveryLedgerRequestSchema, TaskOutcomeSchema, HarnessComponentIdSchema, HarnessComponentLayerSchema, HarnessToggleKindSchema, HarnessCatalogEntrySchema, HarnessCatalogSchema, HarnessOverlaySchema, HarnessExperimentSchema, HarnessSuiteTaskSchema, HarnessSuiteSchema, EvaluationIndexSchema, ExecutionProfileNameSchema, ExecutionProfileSchema, SkillsRegistrySchema;
11444
+ var CouncilAgentSpecSchema, CouncilSpecSchema, ProjectProfileSchema, CompileCheckConfigSchema, LoopConfigSchema, CodeConductorConfigSchema, RunnerTargetSchema, InstallStrategySchema, ManifestEntrySchema, InstallManifestSchema, ToolProviderNamesSchema, PermissionProviderNamesSchema, ModelConfigSchema, SkillFrontmatterSchema, CommandFrontmatterSchema, TargetCapabilitiesSchema, ContractTargetSchema, ContractFormatSchema, AgentContractSchema, CouncilFindingSchema, CouncilVerdictInputSchema, ConsensusConfigSchema, CouncilVerdictSchema, ClaudeAgentFileSchema, OpenCodeAgentFileSchema, SentryStackFrameSchema, SentryWebhookSchema, MemoryPointerSchema, MemoryIndexSchema, GoalTaskSchema, GoalGraphSchema, ConfidenceLevelSchema, ProductNodeTypeSchema, GraphRelationSchema, KnowledgeEntitySchema, DecisionSchema, EvidenceSchema, CanonicalTaskCardStatusSchema, CanonicalTaskCardSchema, ProductGraphNodeSchema, ProductGraphEdgeSchema, ProductGraphSchema, ProductMetaSchema, ProductEventTypeSchema, ProductEventSchema, OperationalStateSchema, StrategicMemorySchema, ImpactReportSchema, BusinessReviewOutputSchema, VerificationReportSchema, BacklogStatusSchema, BacklogTypeSchema, BacklogPrioritySchema, BacklogGlobalSchema, BacklogItemSchema, BacklogDocumentSchema, OpenspecTaskCardPhaseSchema, OpenspecTaskCardStatusSchema, OpenspecTaskCardSchema, OpenspecStateSchema, SpecAnalyzeFindingSchema, SpecAnalyzeReportSchema, WorkflowCommandSchema, CcepOutputFormatSchema, CommandEnvelopeSchema, WorkflowPhaseSchema, WorkflowRiskRuleSchema, WorkflowProfileSchema, ExecutionContextSchema, PlannerOutputSchema, AgentArtifactSchema, AgentOutputSchema, ImplementerTestsSchema, ImplementerOutputSchema, ReviewAxisSchema, ReviewFindingSchema, ReviewerOutputSchema, TechnicalPlanOutputSchema, FixIntakeOutputSchema, ScorecardVerdictSchema, ScorecardCriterionIdSchema, ScorecardCriterionSchema, ScorecardRecordSchema, TaskOutcomeSourceSchema, TaskOutcomeStatusSchema, DeliveryRouteSchema, TokenUsageSchema, DeliveryTelemetrySchema, DeliveryLedgerTaskSchema, DeliveryLedgerSchema, HandoffEnvelopeSchema, RepoRelativePathSchema, DeliveryLedgerRequestSchema, TaskOutcomeSchema, HarnessComponentIdSchema, HarnessComponentLayerSchema, HarnessToggleKindSchema, HarnessCatalogEntrySchema, HarnessCatalogSchema, HarnessOverlaySchema, HarnessExperimentSchema, HarnessSuiteTaskSchema, HarnessSuiteSchema, EvaluationIndexSchema, ExecutionProfileNameSchema, ExecutionProfileSchema, SkillsRegistrySchema;
11443
11445
  var init_schemas = __esm(() => {
11444
11446
  init_zod();
11445
11447
  init_runner_target();
@@ -11517,6 +11519,7 @@ var init_schemas = __esm(() => {
11517
11519
  "append",
11518
11520
  "merge-json",
11519
11521
  "merge-managed",
11522
+ "create-only",
11520
11523
  "skip"
11521
11524
  ]);
11522
11525
  ManifestEntrySchema = exports_external.object({
@@ -11821,7 +11824,8 @@ var init_schemas = __esm(() => {
11821
11824
  "goal.updated",
11822
11825
  "blocker.detected",
11823
11826
  "verification.completed",
11824
- "feedback.processed"
11827
+ "feedback.processed",
11828
+ "context.compiled"
11825
11829
  ]);
11826
11830
  ProductEventSchema = exports_external.object({
11827
11831
  id: exports_external.string(),
@@ -12166,6 +12170,11 @@ var init_schemas = __esm(() => {
12166
12170
  risks: exports_external.array(exports_external.string()).default([]),
12167
12171
  openQuestions: exports_external.array(exports_external.string()).optional()
12168
12172
  });
12173
+ FixIntakeOutputSchema = exports_external.object({
12174
+ actualBehavior: exports_external.string(),
12175
+ expectedBehavior: exports_external.string(),
12176
+ reproductionSteps: exports_external.array(exports_external.string())
12177
+ });
12169
12178
  ScorecardVerdictSchema = exports_external.enum(["PASS", "REVISE", "REJECT"]);
12170
12179
  ScorecardCriterionIdSchema = exports_external.enum([
12171
12180
  "acceptance",
@@ -12237,16 +12246,51 @@ var init_schemas = __esm(() => {
12237
12246
  nextStep: exports_external.string().min(1),
12238
12247
  memoryTopicKey: exports_external.string().min(1),
12239
12248
  workspace: exports_external.record(exports_external.string(), exports_external.string()),
12249
+ technicalPlanPath: exports_external.string().min(1).optional(),
12240
12250
  createdAt: exports_external.string().datetime(),
12241
12251
  updatedAt: exports_external.string().datetime()
12242
12252
  });
12253
+ HandoffEnvelopeSchema = exports_external.object({
12254
+ version: exports_external.literal(1),
12255
+ task: exports_external.object({
12256
+ id: exports_external.string().min(1),
12257
+ objective: exports_external.string().min(1),
12258
+ acceptanceCriteria: exports_external.array(exports_external.string().min(1)),
12259
+ status: CanonicalTaskCardStatusSchema
12260
+ }),
12261
+ decisions: exports_external.object({
12262
+ constraints: exports_external.array(exports_external.string()),
12263
+ unresolved: exports_external.array(exports_external.string())
12264
+ }),
12265
+ scope: exports_external.object({
12266
+ relevantFiles: exports_external.array(exports_external.string()),
12267
+ boundaries: exports_external.array(exports_external.string())
12268
+ }),
12269
+ verification: exports_external.object({
12270
+ evidence: exports_external.array(exports_external.string())
12271
+ }),
12272
+ change: exports_external.object({
12273
+ touchedFiles: exports_external.array(exports_external.string())
12274
+ }),
12275
+ next: exports_external.object({
12276
+ role: exports_external.string().min(1),
12277
+ objective: exports_external.string().min(1)
12278
+ }),
12279
+ sources: exports_external.object({
12280
+ deliveryLedger: exports_external.string().min(1),
12281
+ memoryTopicKey: exports_external.string().min(1),
12282
+ technicalPlanPath: exports_external.string().min(1).optional()
12283
+ })
12284
+ });
12285
+ RepoRelativePathSchema = exports_external.string().min(1).refine((path) => !/^([/\\]|[A-Za-z]:)/.test(path) && !path.split(/[/\\]/).includes(".."), 'Path must be relative to the repository and must not contain ".." segments');
12243
12286
  DeliveryLedgerRequestSchema = exports_external.object({
12244
12287
  id: exports_external.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/),
12245
12288
  authorized: exports_external.boolean(),
12246
12289
  route: DeliveryRouteSchema,
12247
12290
  taskCard: CanonicalTaskCardSchema,
12248
12291
  tasks: exports_external.array(DeliveryLedgerTaskSchema).min(1),
12249
- nextStep: exports_external.string().min(1)
12292
+ nextStep: exports_external.string().min(1),
12293
+ technicalPlanPath: RepoRelativePathSchema.optional()
12250
12294
  });
12251
12295
  TaskOutcomeSchema = exports_external.object({
12252
12296
  id: exports_external.string(),
@@ -12989,18 +13033,19 @@ var init_path_containment = __esm(() => {
12989
13033
  });
12990
13034
 
12991
13035
  // src/core/presets/complementary-detector.ts
12992
- import { execFileSync } from "node:child_process";
13036
+ import { execFile } from "node:child_process";
12993
13037
  import { existsSync, readFileSync } from "node:fs";
12994
13038
  import { join as join3 } from "node:path";
12995
13039
  import { homedir } from "node:os";
12996
- function detectComplementaryTools() {
13040
+ import { promisify } from "node:util";
13041
+ async function detectComplementaryTools() {
12997
13042
  if (cachedStatus) {
12998
13043
  return cachedStatus;
12999
13044
  }
13000
- const isCmdAvailable = (cmd) => {
13045
+ const isCmdAvailable = async (cmd) => {
13001
13046
  try {
13002
13047
  const binary = process.platform === "win32" ? "where" : "which";
13003
- execFileSync(binary, [cmd], { stdio: "ignore", timeout: 1000 });
13048
+ await execFileAsync(binary, [cmd], { timeout: 1000 });
13004
13049
  return true;
13005
13050
  } catch {
13006
13051
  return false;
@@ -13029,18 +13074,28 @@ function detectComplementaryTools() {
13029
13074
  } catch {}
13030
13075
  return false;
13031
13076
  };
13077
+ const [rtk, codeReviewGraph, tokenSavior, tokenSaviorRecall, engram, gentleAi] = await Promise.all([
13078
+ isCmdAvailable("rtk"),
13079
+ isCmdAvailable("code-review-graph"),
13080
+ isCmdAvailable("token-savior"),
13081
+ isCmdAvailable("token-savior-recall"),
13082
+ isCmdAvailable("engram"),
13083
+ isCmdAvailable("gentle-ai")
13084
+ ]);
13032
13085
  cachedStatus = {
13033
- rtk: isCmdAvailable("rtk"),
13034
- codeReviewGraph: isCmdAvailable("code-review-graph"),
13035
- tokenSavior: isCmdAvailable("token-savior") || isCmdAvailable("token-savior-recall"),
13086
+ rtk,
13087
+ codeReviewGraph,
13088
+ tokenSavior: tokenSavior || tokenSaviorRecall,
13036
13089
  caveman: hasCaveman(),
13037
- engram: isCmdAvailable("engram"),
13038
- gentleAi: isCmdAvailable("gentle-ai")
13090
+ engram,
13091
+ gentleAi
13039
13092
  };
13040
13093
  return cachedStatus;
13041
13094
  }
13042
- var cachedStatus = null;
13043
- var init_complementary_detector = () => {};
13095
+ var execFileAsync, cachedStatus = null;
13096
+ var init_complementary_detector = __esm(() => {
13097
+ execFileAsync = promisify(execFile);
13098
+ });
13044
13099
 
13045
13100
  // src/core/presets/file-copier.ts
13046
13101
  import { lstat as lstat2, readdir, readFile as readFile4, stat } from "node:fs/promises";
@@ -13133,9 +13188,9 @@ function extractAgentRole(filePath) {
13133
13188
  ];
13134
13189
  return knownRoles.includes(name) ? name : null;
13135
13190
  }
13136
- function renderTemplate(content, modelConfig, filePath, locale = "en") {
13191
+ async function renderTemplate(content, modelConfig, filePath, locale = "en") {
13137
13192
  const cleanLocale = locale === "es" || locale === "en" ? locale : "en";
13138
- const tools = detectComplementaryTools();
13193
+ const tools = await detectComplementaryTools();
13139
13194
  let complementaryRules = "";
13140
13195
  if (cleanLocale === "es") {
13141
13196
  if (tools.rtk) {
@@ -13258,7 +13313,7 @@ function substituteToolNames(content, modelConfig) {
13258
13313
  const eol = fmMatch[1];
13259
13314
  const frontmatter = fmMatch[2];
13260
13315
  if (target === "opencode" && modelConfig.permissions) {
13261
- const updatedFrontmatter2 = frontmatter.replace(/^tools:\s*(.+)\r?\n?/m, "");
13316
+ const updatedFrontmatter2 = frontmatter.replace(/^tools:\s*(.+)\r?\n?/m, "").replace(/^(?:temperature|effort):[^\r\n]*\r?\n?/gm, "");
13262
13317
  return content.replace(fmMatch[0], `---${eol}${updatedFrontmatter2}${eol}---`);
13263
13318
  }
13264
13319
  if (!modelConfig.tools)
@@ -13276,11 +13331,10 @@ function substituteToolNames(content, modelConfig) {
13276
13331
  });
13277
13332
  return content.replace(fmMatch[0], `---${eol}${updatedFrontmatter}${eol}---`);
13278
13333
  }
13279
- function injectMcpServers(jsonString, filePath) {
13334
+ async function injectMcpServers(jsonString, filePath) {
13280
13335
  const isAgyMcpConfig = filePath.endsWith("mcp_config.json");
13281
- const isClaudeSettings = filePath.endsWith("settings.json");
13282
13336
  const isOpencodeJsonc = filePath.endsWith("opencode.jsonc") || filePath.endsWith("opencode.json");
13283
- if (!isAgyMcpConfig && !isClaudeSettings && !isOpencodeJsonc) {
13337
+ if (!isAgyMcpConfig && !isOpencodeJsonc) {
13284
13338
  return jsonString;
13285
13339
  }
13286
13340
  try {
@@ -13289,13 +13343,13 @@ function injectMcpServers(jsonString, filePath) {
13289
13343
  cleanJson = jsonString.replace(/\/\*[\s\S]*?\*\/|([^\\:]|^)\/\/.*$/gm, "$1");
13290
13344
  }
13291
13345
  const obj = JSON.parse(cleanJson);
13292
- const tools = detectComplementaryTools();
13346
+ const tools = await detectComplementaryTools();
13293
13347
  const mcpServers = {};
13294
13348
  if (tools.tokenSavior) {
13295
13349
  mcpServers["token-savior-recall"] = {
13296
13350
  command: "token-savior",
13297
13351
  env: {
13298
- TOKEN_SAVIOR_CLIENT: isClaudeSettings ? "claude-code" : isOpencodeJsonc ? "opencode" : "agy"
13352
+ TOKEN_SAVIOR_CLIENT: isOpencodeJsonc ? "opencode" : "agy"
13299
13353
  }
13300
13354
  };
13301
13355
  }
@@ -13308,7 +13362,7 @@ function injectMcpServers(jsonString, filePath) {
13308
13362
  if (Object.keys(mcpServers).length === 0) {
13309
13363
  return jsonString;
13310
13364
  }
13311
- if (isClaudeSettings || isAgyMcpConfig) {
13365
+ if (isAgyMcpConfig) {
13312
13366
  obj.mcpServers = mergeDeep(obj.mcpServers || {}, mcpServers);
13313
13367
  } else if (isOpencodeJsonc) {
13314
13368
  const opencodeMcp = {};
@@ -13352,7 +13406,7 @@ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTem
13352
13406
  return { src: srcPath, dest: destPath, action: "error", error: String(error) };
13353
13407
  }
13354
13408
  }
13355
- if (strategy === "overwrite" && !force && await fileExists2(destPath)) {
13409
+ if ((strategy === "overwrite" && !force || strategy === "create-only") && await fileExists2(destPath)) {
13356
13410
  return { src: srcPath, dest: destPath, action: "skipped", dryRun };
13357
13411
  }
13358
13412
  let content;
@@ -13361,8 +13415,8 @@ async function applySingleFile(srcPath, destPath, strategy, force, dryRun, isTem
13361
13415
  } catch (e) {
13362
13416
  return { src: srcPath, dest: destPath, action: "error", error: `Cannot read source: ${e}` };
13363
13417
  }
13364
- let incomingContent = isTemplate && modelConfig ? renderTemplate(content, modelConfig, srcPath, locale) : content;
13365
- incomingContent = injectMcpServers(incomingContent, destPath);
13418
+ let incomingContent = isTemplate && modelConfig ? await renderTemplate(content, modelConfig, srcPath, locale) : content;
13419
+ incomingContent = await injectMcpServers(incomingContent, destPath);
13366
13420
  let finalContent = incomingContent;
13367
13421
  let action = "written";
13368
13422
  if (strategy === "append") {
@@ -13993,6 +14047,11 @@ ${spec.agents.map((a) => `- **${a.role}** (${a.id}): ${a.focus.join(", ")}`).joi
13993
14047
 
13994
14048
  ## Usage
13995
14049
  Use the council agents to get multi-perspective analysis on code changes, architecture decisions, and security reviews.
14050
+ Use Sonnet 5.5 for the routine council pass. Share one concise evidence summary and
14051
+ the relevant diff with the necessary roles. Invoke council-architect with
14052
+ \`model: opus\` only for
14053
+ one unresolved business-logic question; return its short decision to Sonnet for
14054
+ the verdict. Do not run every council role on Opus or repeat full context.
13996
14055
 
13997
14056
  ## Context
13998
14057
  ${spec.outputContract}
@@ -14107,7 +14166,8 @@ function generateCodexFiles(spec) {
14107
14166
  files.push({
14108
14167
  path: ".codex/config.toml",
14109
14168
  content: generateCodexConfig(spec),
14110
- overwrite: false
14169
+ overwrite: false,
14170
+ mergeExisting: (existing) => mergeCodexCouncilConfig(existing, spec)
14111
14171
  });
14112
14172
  for (const agent of spec.agents) {
14113
14173
  files.push({
@@ -14123,17 +14183,32 @@ function generateCodexFiles(spec) {
14123
14183
  });
14124
14184
  return files;
14125
14185
  }
14126
- function generateCodexConfig(spec) {
14127
- const agentTables = spec.agents.map((agent) => {
14128
- const focusAreas = agent.focus.join(", ");
14129
- return `
14186
+ function agentTable(agent) {
14187
+ const focusAreas = agent.focus.join(", ");
14188
+ return `
14130
14189
  [agents.${agent.id}]
14131
14190
  description = "${agent.role} council agent. Focus: ${focusAreas}. Context: ${agent.context}. Model hint: ${agent.modelHint}."
14191
+ config_file = "agents/council_${agent.id}.toml"
14132
14192
  nickname_candidates = ["${agent.role}", "Council ${agent.role}"]`;
14133
- }).join(`
14193
+ }
14194
+ function mergeCodexCouncilConfig(existing, spec) {
14195
+ const missing = spec.agents.filter((agent) => !existing.split(`
14196
+ `).some((line) => line.trim() === `[agents.${agent.id}]`));
14197
+ if (missing.length === 0)
14198
+ return existing;
14199
+ return `${existing.trimEnd()}
14200
+ ${missing.map(agentTable).join(`
14201
+ `)}
14202
+ `;
14203
+ }
14204
+ function generateCodexConfig(spec) {
14205
+ const agentTables = spec.agents.map(agentTable).join(`
14134
14206
  `);
14135
14207
  return `# Codex Council Configuration
14136
14208
 
14209
+ model = "gpt-6.1-sol"
14210
+ model_reasoning_effort = "medium"
14211
+
14137
14212
  [project]
14138
14213
  name = "council"
14139
14214
  version = "${spec.version}"
@@ -14143,7 +14218,9 @@ ${agentTables}
14143
14218
  function generateCodexAgent(agent) {
14144
14219
  const focusAreas = agent.focus.join(", ");
14145
14220
  const checklist = checklistFor(agent.id).join("; ");
14146
- return `name = "${agent.role}"
14221
+ return `model = "gpt-6.1-sol"
14222
+ model_reasoning_effort = "medium"
14223
+ name = "${agent.role}"
14147
14224
  description = "${agent.role} council agent. Focus: ${focusAreas}. Context: ${agent.context}. Model hint: ${agent.modelHint}."
14148
14225
  nickname_candidates = ["${agent.role}", "Council ${agent.role}"]
14149
14226
  developer_instructions = "You are the ${agent.role} council agent. Your focus areas are: ${focusAreas}. Context: ${agent.context}. Apply ${agent.modelHint} reasoning to your analysis. Review checklist: ${checklist}. Categorize findings as CRITICAL (blocks the verdict), WARNING, or SUGGESTION."
@@ -14162,6 +14239,9 @@ ${spec.agents.map((a) => `- **${a.role}** (${a.id}): ${a.focus.join(", ")}`).joi
14162
14239
 
14163
14240
  ## Usage
14164
14241
  Use the council agents to get multi-perspective analysis on code changes, architecture decisions, and security reviews.
14242
+ Keep GPT-6.1 Sol at medium reasoning effort. Share a concise evidence summary
14243
+ and relevant diff with only the necessary council roles. Reuse findings in the
14244
+ final verdict instead of replaying the full transcript or repeating analysis.
14165
14245
  `;
14166
14246
  }
14167
14247
  var init_codex_council_generator = __esm(() => {
@@ -14629,7 +14709,7 @@ async function checkUpdates(basePath, isGlobal, includeConflicts = false) {
14629
14709
  let expectedContent;
14630
14710
  try {
14631
14711
  const srcContent = await readFile7(src, "utf-8");
14632
- expectedContent = isTemplate && modelConfig ? renderTemplate(srcContent, modelConfig, src, locale) : srcContent;
14712
+ expectedContent = isTemplate && modelConfig ? await renderTemplate(srcContent, modelConfig, src, locale) : srcContent;
14633
14713
  } catch {
14634
14714
  continue;
14635
14715
  }
@@ -14714,7 +14794,8 @@ async function checkUpdates(basePath, isGlobal, includeConflicts = false) {
14714
14794
  let destContent;
14715
14795
  try {
14716
14796
  destContent = await readFile7(dest, "utf-8");
14717
- if (destContent.trim() !== f.content.trim()) {
14797
+ const expected = f.mergeExisting ? f.mergeExisting(destContent) : f.content;
14798
+ if (destContent.trim() !== expected.trim()) {
14718
14799
  changedFiles.push(dest);
14719
14800
  }
14720
14801
  } catch {
@@ -15091,7 +15172,7 @@ async function doctorCommand(options) {
15091
15172
  message: "No product graph yet. Run `codeconductor ingest` to build product memory."
15092
15173
  });
15093
15174
  }
15094
- const compTools = detectComplementaryTools();
15175
+ const compTools = await detectComplementaryTools();
15095
15176
  const toolDetails = [
15096
15177
  { name: "rtk", key: "rtk", label: "RTK", desc: "Run `brew install rtk` to compress command outputs" },
15097
15178
  { name: "code-review-graph", key: "codeReviewGraph", label: "code-review-graph", desc: "Run `pipx install code-review-graph` to navigate symbol graph" },
@@ -16899,7 +16980,7 @@ __export(exports_file_writer, {
16899
16980
  writeGeneratedFiles: () => writeGeneratedFiles,
16900
16981
  writeSingleFile: () => writeSingleFile
16901
16982
  });
16902
- import { access as access5 } from "node:fs/promises";
16983
+ import { access as access5, readFile as readFile11 } from "node:fs/promises";
16903
16984
  import { relative as relative4 } from "node:path";
16904
16985
  async function writeGeneratedFiles(files, options) {
16905
16986
  const results = [];
@@ -16926,7 +17007,15 @@ async function writeGeneratedFiles(files, options) {
16926
17007
  });
16927
17008
  continue;
16928
17009
  }
16929
- if (!options.force) {
17010
+ let content = file.content;
17011
+ let merged = false;
17012
+ if (file.mergeExisting) {
17013
+ try {
17014
+ content = file.mergeExisting(await readFile11(file.path, "utf-8"));
17015
+ merged = true;
17016
+ } catch {}
17017
+ }
17018
+ if (!options.force && !merged) {
16930
17019
  try {
16931
17020
  await access5(file.path);
16932
17021
  results.push({
@@ -16946,7 +17035,7 @@ async function writeGeneratedFiles(files, options) {
16946
17035
  continue;
16947
17036
  }
16948
17037
  try {
16949
- await writeContainedFile(options.projectRoot, relative4(options.projectRoot, file.path), file.content, { force: true });
17038
+ await writeContainedFile(options.projectRoot, relative4(options.projectRoot, file.path), content, { force: true });
16950
17039
  results.push({
16951
17040
  path: file.path,
16952
17041
  success: true
@@ -16986,7 +17075,7 @@ __export(exports_install_command, {
16986
17075
  installPresetCommand: () => installPresetCommand,
16987
17076
  verifyRenderedFiles: () => verifyRenderedFiles
16988
17077
  });
16989
- import { readFile as readFile11 } from "node:fs/promises";
17078
+ import { readFile as readFile12 } from "node:fs/promises";
16990
17079
  import { homedir as homedir7 } from "node:os";
16991
17080
  import { basename as basename5, dirname as dirname6, resolve as resolve12, sep as sep3 } from "node:path";
16992
17081
  async function installCommand(options) {
@@ -17119,7 +17208,7 @@ async function verifyRenderedFiles(manifest, baseDir, results) {
17119
17208
  continue;
17120
17209
  let content;
17121
17210
  try {
17122
- content = await readFile11(r.dest, "utf-8");
17211
+ content = await readFile12(r.dest, "utf-8");
17123
17212
  } catch {
17124
17213
  continue;
17125
17214
  }
@@ -18373,13 +18462,13 @@ var init_binary_downloader = __esm(() => {
18373
18462
  });
18374
18463
 
18375
18464
  // src/core/lsp/lsp-installer.ts
18376
- import { execFile } from "node:child_process";
18465
+ import { execFile as execFile2 } from "node:child_process";
18377
18466
  import { access as access6, mkdir as mkdir6, mkdtemp, rename as rename2, rm, writeFile as writeFile5 } from "node:fs/promises";
18378
18467
  import { homedir as homedir8, tmpdir } from "node:os";
18379
18468
  import { join as join9 } from "node:path";
18380
- import { promisify } from "node:util";
18469
+ import { promisify as promisify2 } from "node:util";
18381
18470
  async function assertTarArchiveSafe(archivePath, destRoot) {
18382
- const { stdout } = await execFileAsync("tar", ["-tzf", archivePath], {
18471
+ const { stdout } = await execFileAsync2("tar", ["-tzf", archivePath], {
18383
18472
  timeout: 60000,
18384
18473
  maxBuffer: 10 * 1024 * 1024
18385
18474
  });
@@ -18391,7 +18480,7 @@ async function assertTarArchiveSafe(archivePath, destRoot) {
18391
18480
  }
18392
18481
  async function extractHardenedTar(archivePath, destRoot) {
18393
18482
  await assertTarArchiveSafe(archivePath, destRoot);
18394
- await execFileAsync("tar", ["-xzf", archivePath, "-C", destRoot, ...TAR_EXTRACT_HARDENING_FLAGS], { timeout: 120000 });
18483
+ await execFileAsync2("tar", ["-xzf", archivePath, "-C", destRoot, ...TAR_EXTRACT_HARDENING_FLAGS], { timeout: 120000 });
18395
18484
  }
18396
18485
 
18397
18486
  class LspInstaller {
@@ -18401,7 +18490,7 @@ class LspInstaller {
18401
18490
  }
18402
18491
  async checkInstalled(def) {
18403
18492
  try {
18404
- const { stdout } = await execFileAsync("which", [def.binaryName], { timeout: 5000 });
18493
+ const { stdout } = await execFileAsync2("which", [def.binaryName], { timeout: 5000 });
18405
18494
  const path = stdout.trim();
18406
18495
  if (path) {
18407
18496
  const version = await this.getVersion(def);
@@ -18410,7 +18499,7 @@ class LspInstaller {
18410
18499
  } catch {}
18411
18500
  if (def.packageManager === "npm" && def.npmDetect) {
18412
18501
  try {
18413
- const { stdout } = await execFileAsync("npm", ["list", "-g", def.npmDetect, "--depth=0"], { timeout: 1e4 });
18502
+ const { stdout } = await execFileAsync2("npm", ["list", "-g", def.npmDetect, "--depth=0"], { timeout: 1e4 });
18414
18503
  if (stdout.includes(def.npmDetect)) {
18415
18504
  const version = this.parseVersionFromNpmList(stdout, def.npmDetect);
18416
18505
  return { installed: true, version };
@@ -18419,7 +18508,7 @@ class LspInstaller {
18419
18508
  }
18420
18509
  if (def.packageManager === "pip" && def.pipDetect) {
18421
18510
  try {
18422
- const { stdout } = await execFileAsync("pip", ["show", def.pipDetect], { timeout: 1e4 });
18511
+ const { stdout } = await execFileAsync2("pip", ["show", def.pipDetect], { timeout: 1e4 });
18423
18512
  if (stdout.includes("Version:")) {
18424
18513
  const version = this.parseVersionFromPipShow(stdout);
18425
18514
  return { installed: true, version };
@@ -18483,7 +18572,7 @@ class LspInstaller {
18483
18572
  }
18484
18573
  async getVersion(def) {
18485
18574
  try {
18486
- const { stdout } = await execFileAsync(def.binaryName, [def.versionFlag], { timeout: 5000 });
18575
+ const { stdout } = await execFileAsync2(def.binaryName, [def.versionFlag], { timeout: 5000 });
18487
18576
  return stdout.trim().split(`
18488
18577
  `)[0];
18489
18578
  } catch {
@@ -18501,7 +18590,7 @@ class LspInstaller {
18501
18590
  async installNpm(def) {
18502
18591
  assertPinnedPackage(def);
18503
18592
  try {
18504
- await execFileAsync("npm", ["install", "-g", def.package], { timeout: 120000 });
18593
+ await execFileAsync2("npm", ["install", "-g", def.package], { timeout: 120000 });
18505
18594
  } catch (error) {
18506
18595
  throw new Error(`Failed to install ${def.serverName}: ${error instanceof Error ? error.message : String(error)}`);
18507
18596
  }
@@ -18509,7 +18598,7 @@ class LspInstaller {
18509
18598
  async installPip(def) {
18510
18599
  assertPinnedPackage(def);
18511
18600
  try {
18512
- await execFileAsync("pip", ["install", "--user", def.package], { timeout: 120000 });
18601
+ await execFileAsync2("pip", ["install", "--user", def.package], { timeout: 120000 });
18513
18602
  } catch (error) {
18514
18603
  throw new Error(`Failed to install ${def.serverName}: ${error instanceof Error ? error.message : String(error)}`);
18515
18604
  }
@@ -18551,7 +18640,7 @@ class LspInstaller {
18551
18640
  await access6(candidate);
18552
18641
  await rename2(candidate, destPath);
18553
18642
  } catch {
18554
- const { stdout: found } = await execFileAsync("find", [workDir, "-type", "f", "-name", def.binaryName], { timeout: 30000 });
18643
+ const { stdout: found } = await execFileAsync2("find", [workDir, "-type", "f", "-name", def.binaryName], { timeout: 30000 });
18555
18644
  const first = found.split(`
18556
18645
  `).map((l) => l.trim()).find((l) => l.length > 0);
18557
18646
  if (!first) {
@@ -18561,7 +18650,7 @@ class LspInstaller {
18561
18650
  await rename2(first, destPath);
18562
18651
  }
18563
18652
  if (process.platform !== "win32") {
18564
- await execFileAsync("chmod", ["+x", destPath], { timeout: 5000 });
18653
+ await execFileAsync2("chmod", ["+x", destPath], { timeout: 5000 });
18565
18654
  }
18566
18655
  } finally {
18567
18656
  await rm(workDir, { recursive: true, force: true });
@@ -18571,11 +18660,11 @@ class LspInstaller {
18571
18660
  function createLspInstaller() {
18572
18661
  return new LspInstaller;
18573
18662
  }
18574
- var execFileAsync, TAR_EXTRACT_HARDENING_FLAGS;
18663
+ var execFileAsync2, TAR_EXTRACT_HARDENING_FLAGS;
18575
18664
  var init_lsp_installer = __esm(() => {
18576
18665
  init_binary_integrity();
18577
18666
  init_binary_downloader();
18578
- execFileAsync = promisify(execFile);
18667
+ execFileAsync2 = promisify2(execFile2);
18579
18668
  TAR_EXTRACT_HARDENING_FLAGS = [
18580
18669
  "--no-same-owner",
18581
18670
  "--no-same-permissions"
@@ -18824,7 +18913,7 @@ var init_install_lsp_command = __esm(() => {
18824
18913
  });
18825
18914
 
18826
18915
  // src/core/install/update-transaction.ts
18827
- import { mkdir as mkdir7, readFile as readFile12, rm as rm2, unlink, writeFile as writeFile6 } from "node:fs/promises";
18916
+ import { mkdir as mkdir7, readFile as readFile13, rm as rm2, unlink, writeFile as writeFile6 } from "node:fs/promises";
18828
18917
  import { dirname as dirname7, join as join10, relative as relative5, resolve as resolve16 } from "node:path";
18829
18918
 
18830
18919
  class UpdateTransaction {
@@ -18841,7 +18930,7 @@ class UpdateTransaction {
18841
18930
  const snapshots = [];
18842
18931
  for (const destination of unique) {
18843
18932
  try {
18844
- const content = await readFile12(destination);
18933
+ const content = await readFile13(destination);
18845
18934
  snapshots.push({ path: destination, existed: true, content });
18846
18935
  const backupPath = join10(backupDir, relative5(basePath, destination));
18847
18936
  await mkdir7(dirname7(backupPath), { recursive: true });
@@ -18883,7 +18972,7 @@ __export(exports_update_command, {
18883
18972
  });
18884
18973
  import { homedir as homedir10 } from "node:os";
18885
18974
  import { resolve as resolve17, dirname as dirname8 } from "node:path";
18886
- import { mkdir as mkdir8, readFile as readFile13, writeFile as writeFile7, stat as stat3 } from "node:fs/promises";
18975
+ import { mkdir as mkdir8, readFile as readFile14, writeFile as writeFile7, stat as stat3 } from "node:fs/promises";
18887
18976
  async function updateCommand(options) {
18888
18977
  const { dryRun, force, global: isGlobal, output, projectRoot, check = false } = options;
18889
18978
  const basePath = isGlobal ? homedir10() : projectRoot;
@@ -18992,7 +19081,7 @@ async function updateCommand(options) {
18992
19081
  const localCouncil = resolve17(basePath, ".codeconductor", "presets", "council.yml");
18993
19082
  const bundledCouncil = resolve17(SRC_PRESETS_DIR, "council", "council.yml");
18994
19083
  try {
18995
- const content = await readFile13(bundledCouncil, "utf-8");
19084
+ const content = await readFile14(bundledCouncil, "utf-8");
18996
19085
  await mkdir8(dirname8(localCouncil), { recursive: true });
18997
19086
  await writeFile7(localCouncil, content, "utf-8");
18998
19087
  updated.push(localCouncil);
@@ -19003,7 +19092,7 @@ async function updateCommand(options) {
19003
19092
  if (updateResults.policy) {
19004
19093
  const localPolicy = resolve17(basePath, ".codeconductor", "presets", "policy.yml");
19005
19094
  try {
19006
- const content = await readFile13(POLICY_PATH, "utf-8");
19095
+ const content = await readFile14(POLICY_PATH, "utf-8");
19007
19096
  await mkdir8(dirname8(localPolicy), { recursive: true });
19008
19097
  await writeFile7(localPolicy, content, "utf-8");
19009
19098
  updated.push(localPolicy);
@@ -19161,7 +19250,7 @@ __export(exports_migrate_command, {
19161
19250
  migrateCommand: () => migrateCommand,
19162
19251
  migratePermissionRules: () => migratePermissionRules
19163
19252
  });
19164
- import { readFile as readFile14, readdir as readdir3, rm as rm3, writeFile as writeFile8 } from "node:fs/promises";
19253
+ import { readFile as readFile15, readdir as readdir3, rm as rm3, writeFile as writeFile8 } from "node:fs/promises";
19165
19254
  import { homedir as homedir11 } from "node:os";
19166
19255
  import { join as join11, resolve as resolve18 } from "node:path";
19167
19256
  function migratePermissionRules(settings) {
@@ -19244,7 +19333,7 @@ async function migrateCommand(options) {
19244
19333
  const settingsPath = resolveSettingsPath(options);
19245
19334
  let raw = null;
19246
19335
  try {
19247
- raw = await readFile14(settingsPath, "utf-8");
19336
+ raw = await readFile15(settingsPath, "utf-8");
19248
19337
  } catch (error) {
19249
19338
  if (error.code !== "ENOENT") {
19250
19339
  return {
@@ -19657,7 +19746,7 @@ var exports_debt_harvest_command = {};
19657
19746
  __export(exports_debt_harvest_command, {
19658
19747
  debtHarvestCommand: () => debtHarvestCommand
19659
19748
  });
19660
- import { readdir as readdir5, readFile as readFile15, writeFile as writeFile9, mkdir as mkdir9 } from "node:fs/promises";
19749
+ import { readdir as readdir5, readFile as readFile16, writeFile as writeFile9, mkdir as mkdir9 } from "node:fs/promises";
19661
19750
  import { join as join13, relative as relative6, extname } from "node:path";
19662
19751
  async function walkDir(dir, extensions) {
19663
19752
  const results = [];
@@ -19735,7 +19824,7 @@ async function debtHarvestCommand(options) {
19735
19824
  const files = await walkDir(targetDir, SOURCE_EXTENSIONS);
19736
19825
  const allEntries = [];
19737
19826
  for (const file of files) {
19738
- const content = await readFile15(file, "utf-8");
19827
+ const content = await readFile16(file, "utf-8");
19739
19828
  const entries = extractDefers(content, relative6(projectRoot, file));
19740
19829
  allEntries.push(...entries);
19741
19830
  }
@@ -20213,11 +20302,11 @@ __export(exports_graph_store, {
20213
20302
  saveGraph: () => saveGraph,
20214
20303
  upsertNode: () => upsertNode
20215
20304
  });
20216
- import { mkdir as mkdir10, readFile as readFile16, writeFile as writeFile10 } from "node:fs/promises";
20305
+ import { mkdir as mkdir10, readFile as readFile17, writeFile as writeFile10 } from "node:fs/promises";
20217
20306
  import { dirname as dirname9 } from "node:path";
20218
20307
  async function loadGraph(projectRoot) {
20219
20308
  try {
20220
- const raw = await readFile16(productGraphPath(projectRoot), "utf-8");
20309
+ const raw = await readFile17(productGraphPath(projectRoot), "utf-8");
20221
20310
  return ok(ProductGraphSchema.parse(JSON.parse(raw)));
20222
20311
  } catch (e) {
20223
20312
  if (e.code === "ENOENT") {
@@ -20399,7 +20488,7 @@ __export(exports_goal_state, {
20399
20488
  markTaskBlocked: () => markTaskBlocked,
20400
20489
  writeGoal: () => writeGoal
20401
20490
  });
20402
- import { mkdir as mkdir11, readFile as readFile17, writeFile as writeFile11 } from "node:fs/promises";
20491
+ import { mkdir as mkdir11, readFile as readFile18, writeFile as writeFile11 } from "node:fs/promises";
20403
20492
  import { resolve as resolve19 } from "node:path";
20404
20493
  function validateGoalGraph2(graph) {
20405
20494
  const ids = new Set(graph.tasks.map((t) => t.id));
@@ -20452,7 +20541,7 @@ async function writeGoal(projectRoot, graph) {
20452
20541
  async function loadGoal(projectRoot) {
20453
20542
  try {
20454
20543
  const filePath = resolve19(projectRoot, GOAL_FILE);
20455
- const content = await readFile17(filePath, "utf-8");
20544
+ const content = await readFile18(filePath, "utf-8");
20456
20545
  const data = $parse(content);
20457
20546
  const validated = GoalGraphSchema.parse(data);
20458
20547
  return validateGoalGraph2(validated);
@@ -20614,7 +20703,7 @@ var init_goal_command = __esm(() => {
20614
20703
  });
20615
20704
 
20616
20705
  // src/core/openspec/backlog-parser.ts
20617
- import { readFile as readFile18 } from "node:fs/promises";
20706
+ import { readFile as readFile19 } from "node:fs/promises";
20618
20707
  import { resolve as resolve20 } from "node:path";
20619
20708
  function parseBool(value) {
20620
20709
  const v = value.trim().toLowerCase();
@@ -20790,7 +20879,7 @@ function parseBacklogMarkdown(content) {
20790
20879
  async function loadBacklog(projectRoot) {
20791
20880
  try {
20792
20881
  const filePath = resolve20(projectRoot, BACKLOG_FILE);
20793
- const content = await readFile18(filePath, "utf-8");
20882
+ const content = await readFile19(filePath, "utf-8");
20794
20883
  return parseBacklogMarkdown(content);
20795
20884
  } catch (e) {
20796
20885
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -20839,14 +20928,14 @@ function slugify(text) {
20839
20928
  }
20840
20929
 
20841
20930
  // src/core/knowledge/source-parsers.ts
20842
- import { readFile as readFile19 } from "node:fs/promises";
20843
- import { join as join15 } from "node:path";
20931
+ import { readFile as readFile20 } from "node:fs/promises";
20932
+ import { basename as basename6, join as join15, relative as relative7, sep as sep5 } from "node:path";
20844
20933
  async function parseAdrFile(filePath) {
20845
20934
  try {
20846
- const content = await readFile19(filePath, "utf-8");
20847
- const basename6 = filePath.split("/").pop() ?? "";
20848
- const idMatch = basename6.match(/adr-(\d+)/i);
20849
- const id = idMatch ? `adr-${idMatch[1]}` : slugify(basename6);
20935
+ const content = await readFile20(filePath, "utf-8");
20936
+ const base = basename6(filePath);
20937
+ const idMatch = base.match(/adr-(\d+)/i);
20938
+ const id = idMatch ? `adr-${idMatch[1]}` : slugify(base);
20850
20939
  const titleMatch = content.match(/^#\s+ADR-\d+:\s*(.+)$/m);
20851
20940
  const statusMatch = content.match(/\*\*Status:\*\*\s*(\w+)/i);
20852
20941
  const contextSection = extractSection(content, "Context");
@@ -20856,7 +20945,7 @@ async function parseAdrFile(filePath) {
20856
20945
  `).filter((l) => l.trim().startsWith("-") || l.trim().startsWith("*")).map((l) => l.replace(/^[\s*-]+/, "").trim()).filter(Boolean);
20857
20946
  return {
20858
20947
  id,
20859
- title: titleMatch?.[1]?.trim() ?? basename6,
20948
+ title: titleMatch?.[1]?.trim() ?? base,
20860
20949
  status: statusMatch?.[1]?.toLowerCase() ?? "unknown",
20861
20950
  context: contextSection.trim(),
20862
20951
  decision: decisionSection.trim(),
@@ -20892,7 +20981,7 @@ function adrToEntities(adr, source) {
20892
20981
  }
20893
20982
  async function parseReadmeEntities(projectRoot, productName) {
20894
20983
  try {
20895
- const content = await readFile19(join15(projectRoot, "README.md"), "utf-8");
20984
+ const content = await readFile20(join15(projectRoot, "README.md"), "utf-8");
20896
20985
  const firstPara = content.split(`
20897
20986
 
20898
20987
  `).find((p) => p.trim() && !p.startsWith("#") && !p.startsWith(">")) ?? "";
@@ -20913,7 +21002,7 @@ async function parseReadmeEntities(projectRoot, productName) {
20913
21002
  }
20914
21003
  async function parseGraphifyEntities(projectRoot) {
20915
21004
  try {
20916
- const raw = await readFile19(join15(projectRoot, "graphify-out", "graph.json"), "utf-8");
21005
+ const raw = await readFile20(join15(projectRoot, "graphify-out", "graph.json"), "utf-8");
20917
21006
  const graph = JSON.parse(raw);
20918
21007
  const entities = [];
20919
21008
  for (const node2 of graph.nodes ?? []) {
@@ -20956,7 +21045,7 @@ async function scanSrcComponents(projectRoot) {
20956
21045
  source: "src/",
20957
21046
  confidence: "medium",
20958
21047
  relations: [],
20959
- data: { path: full.replace(projectRoot + "/", "") }
21048
+ data: { path: relative7(projectRoot, full).split(sep5).join("/") }
20960
21049
  });
20961
21050
  await walk(full, prefix ? `${prefix}-${domainSlug}` : domainSlug);
20962
21051
  }
@@ -20969,7 +21058,7 @@ async function scanSrcComponents(projectRoot) {
20969
21058
  var init_source_parsers = () => {};
20970
21059
 
20971
21060
  // src/core/memory/episodic-store.ts
20972
- import { appendFile, mkdir as mkdir12, readFile as readFile20 } from "node:fs/promises";
21061
+ import { appendFile, mkdir as mkdir12, readFile as readFile21 } from "node:fs/promises";
20973
21062
  import { resolve as resolve21 } from "node:path";
20974
21063
  async function appendEvent(projectRoot, event) {
20975
21064
  try {
@@ -20988,7 +21077,7 @@ async function appendEvent(projectRoot, event) {
20988
21077
  }
20989
21078
  async function listEvents(projectRoot, since, type) {
20990
21079
  try {
20991
- const content = await readFile20(eventsPath(projectRoot), "utf-8");
21080
+ const content = await readFile21(eventsPath(projectRoot), "utf-8");
20992
21081
  const events = [];
20993
21082
  for (const line of content.split(`
20994
21083
  `)) {
@@ -21016,8 +21105,8 @@ var init_episodic_store = __esm(() => {
21016
21105
 
21017
21106
  // src/core/knowledge/ingest-pipeline.ts
21018
21107
  import { createHash as createHash3 } from "node:crypto";
21019
- import { readFile as readFile21 } from "node:fs/promises";
21020
- import { join as join16 } from "node:path";
21108
+ import { readFile as readFile22 } from "node:fs/promises";
21109
+ import { join as join16, relative as relative8, sep as sep6 } from "node:path";
21021
21110
  import { mkdir as mkdir13, writeFile as writeFile12 } from "node:fs/promises";
21022
21111
  async function mapLimit(items, limit, fn) {
21023
21112
  if (items.length === 0)
@@ -21038,7 +21127,7 @@ async function mapLimit(items, limit, fn) {
21038
21127
  }
21039
21128
  async function hashFile(path) {
21040
21129
  try {
21041
- const content = await readFile21(path, "utf-8");
21130
+ const content = await readFile22(path, "utf-8");
21042
21131
  return createHash3("sha256").update(content).digest("hex").slice(0, 16);
21043
21132
  } catch {
21044
21133
  return null;
@@ -21065,8 +21154,8 @@ async function collectDeferRisks(projectRoot) {
21065
21154
  await walk(join16(projectRoot, "src"));
21066
21155
  const perFile = await mapLimit(files, WALK_READ_CONCURRENCY, async (full) => {
21067
21156
  const entities = [];
21068
- const content = await readFile21(full, "utf-8");
21069
- const rel = full.replace(projectRoot + "/", "");
21157
+ const content = await readFile22(full, "utf-8");
21158
+ const rel = relative8(projectRoot, full).split(sep6).join("/");
21070
21159
  const deferRe = new RegExp(DEFER_REGEX_SOURCE, "gm");
21071
21160
  let match;
21072
21161
  while ((match = deferRe.exec(content)) !== null) {
@@ -21314,11 +21403,11 @@ var init_ingest_command = __esm(() => {
21314
21403
  });
21315
21404
 
21316
21405
  // src/core/memory/operational-state.ts
21317
- import { mkdir as mkdir14, readFile as readFile22, writeFile as writeFile13 } from "node:fs/promises";
21406
+ import { mkdir as mkdir14, readFile as readFile23, writeFile as writeFile13 } from "node:fs/promises";
21318
21407
  import { resolve as resolve22 } from "node:path";
21319
21408
  async function loadOperationalState(projectRoot) {
21320
21409
  try {
21321
- const raw = await readFile22(operationalStatePath(projectRoot), "utf-8");
21410
+ const raw = await readFile23(operationalStatePath(projectRoot), "utf-8");
21322
21411
  return ok(OperationalStateSchema.parse(JSON.parse(raw)));
21323
21412
  } catch (e) {
21324
21413
  if (e.code === "ENOENT") {
@@ -21375,11 +21464,11 @@ var init_operational_state = __esm(() => {
21375
21464
  });
21376
21465
 
21377
21466
  // src/core/memory/strategic-memory.ts
21378
- import { mkdir as mkdir15, readFile as readFile23, writeFile as writeFile14 } from "node:fs/promises";
21467
+ import { mkdir as mkdir15, readFile as readFile24, writeFile as writeFile14 } from "node:fs/promises";
21379
21468
  import { resolve as resolve23 } from "node:path";
21380
21469
  async function loadStrategicMemory(projectRoot) {
21381
21470
  try {
21382
- const raw = await readFile23(strategicPath(projectRoot), "utf-8");
21471
+ const raw = await readFile24(strategicPath(projectRoot), "utf-8");
21383
21472
  return ok(StrategicMemorySchema.parse(JSON.parse(raw)));
21384
21473
  } catch (e) {
21385
21474
  if (e.code === "ENOENT") {
@@ -21413,7 +21502,7 @@ var init_strategic_memory = __esm(() => {
21413
21502
  });
21414
21503
 
21415
21504
  // src/core/memory/memory-index.ts
21416
- import { mkdir as mkdir16, readFile as readFile24, writeFile as writeFile15 } from "node:fs/promises";
21505
+ import { mkdir as mkdir16, readFile as readFile25, writeFile as writeFile15 } from "node:fs/promises";
21417
21506
  import { resolve as resolve24 } from "node:path";
21418
21507
  function parseMarkdown(content) {
21419
21508
  const startIdx = content.indexOf(SENTINEL_START);
@@ -21434,7 +21523,7 @@ function parseMarkdown(content) {
21434
21523
  async function loadMemoryIndex(projectRoot) {
21435
21524
  try {
21436
21525
  const filePath = resolve24(projectRoot, MEMORY_FILE);
21437
- const content = await readFile24(filePath, "utf-8");
21526
+ const content = await readFile25(filePath, "utf-8");
21438
21527
  const parsed = parseMarkdown(content);
21439
21528
  if (!parsed) {
21440
21529
  return err(new Error("Failed to parse memory index from memory.md"));
@@ -21451,7 +21540,7 @@ var init_memory_index = __esm(() => {
21451
21540
  });
21452
21541
 
21453
21542
  // src/core/evaluation/outcome-store.ts
21454
- import { appendFile as appendFile2, mkdir as mkdir17, readFile as readFile25, readdir as readdir6, writeFile as writeFile16 } from "node:fs/promises";
21543
+ import { appendFile as appendFile2, mkdir as mkdir17, readFile as readFile26, readdir as readdir6, writeFile as writeFile16 } from "node:fs/promises";
21455
21544
  import { resolve as resolve25 } from "node:path";
21456
21545
  async function ensureEvaluationDir(projectRoot) {
21457
21546
  const dir = resolve25(projectRoot, EVAL_DIR);
@@ -21468,7 +21557,7 @@ async function appendOutcome(projectRoot, outcome) {
21468
21557
  const indexPath = resolve25(dir, INDEX_FILE);
21469
21558
  let index = { version: 1, lastOutcomeId: validated.id };
21470
21559
  try {
21471
- const raw = await readFile25(indexPath, "utf-8");
21560
+ const raw = await readFile26(indexPath, "utf-8");
21472
21561
  index = EvaluationIndexSchema.parse(JSON.parse(raw));
21473
21562
  index.lastOutcomeId = validated.id;
21474
21563
  } catch {}
@@ -21481,7 +21570,7 @@ async function appendOutcome(projectRoot, outcome) {
21481
21570
  async function listOutcomes(projectRoot, filter = {}) {
21482
21571
  try {
21483
21572
  const path = resolve25(projectRoot, EVAL_DIR, OUTCOMES_FILE);
21484
- const content = await readFile25(path, "utf-8");
21573
+ const content = await readFile26(path, "utf-8");
21485
21574
  const lines = content.split(`
21486
21575
  `).filter((l) => l.trim().length > 0);
21487
21576
  const outcomes = [];
@@ -21541,7 +21630,7 @@ async function listScorecards(projectRoot, filter = {}) {
21541
21630
  for (const file of files) {
21542
21631
  if (!file.endsWith(".json"))
21543
21632
  continue;
21544
- const parsed = ScorecardRecordSchema.parse(JSON.parse(await readFile25(resolve25(dir, file), "utf-8")));
21633
+ const parsed = ScorecardRecordSchema.parse(JSON.parse(await readFile26(resolve25(dir, file), "utf-8")));
21545
21634
  if (filter.backlogId && parsed.backlogId !== filter.backlogId)
21546
21635
  continue;
21547
21636
  if (filter.verdict && parsed.verdict !== filter.verdict)
@@ -21564,7 +21653,7 @@ async function hasPassingScorecard(projectRoot, backlogId) {
21564
21653
  async function loadScorecard(projectRoot, id) {
21565
21654
  try {
21566
21655
  const filePath = resolve25(projectRoot, EVAL_DIR, SCORECARDS_DIR, `${id}.json`);
21567
- const content = await readFile25(filePath, "utf-8");
21656
+ const content = await readFile26(filePath, "utf-8");
21568
21657
  return ok(ScorecardRecordSchema.parse(JSON.parse(content)));
21569
21658
  } catch (e) {
21570
21659
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -21656,11 +21745,11 @@ var init_outcome_store = __esm(() => {
21656
21745
  });
21657
21746
 
21658
21747
  // src/core/feedback/feedback-ingestor.ts
21659
- import { readFile as readFile26 } from "node:fs/promises";
21748
+ import { readFile as readFile27 } from "node:fs/promises";
21660
21749
  import { join as join17 } from "node:path";
21661
21750
  async function parseChangelogLearnings(projectRoot) {
21662
21751
  try {
21663
- const content = await readFile26(join17(projectRoot, "CHANGELOG.md"), "utf-8");
21752
+ const content = await readFile27(join17(projectRoot, "CHANGELOG.md"), "utf-8");
21664
21753
  const unreleased = content.match(/## \[Unreleased\]([\s\S]*?)(?=## \[|\n## )/i);
21665
21754
  if (!unreleased)
21666
21755
  return [];
@@ -22264,22 +22353,22 @@ __export(exports_rdd_receipt, {
22264
22353
  verifyReceipt: () => verifyReceipt
22265
22354
  });
22266
22355
  import { createHash as createHash4 } from "node:crypto";
22267
- import { readdir as readdir7, readFile as readFile27 } from "node:fs/promises";
22268
- import { join as join18, relative as relative7, resolve as resolve26, sep as sep5 } from "node:path";
22356
+ import { readdir as readdir7, readFile as readFile28 } from "node:fs/promises";
22357
+ import { join as join18, relative as relative9, resolve as resolve26, sep as sep7 } from "node:path";
22269
22358
  function sha2562(content) {
22270
22359
  return createHash4("sha256").update(content).digest("hex");
22271
22360
  }
22272
22361
  function canonicalPath(projectRoot, input) {
22273
22362
  const root = resolve26(projectRoot);
22274
22363
  const absolute = resolve26(root, input);
22275
- if (absolute !== root && !absolute.startsWith(`${root}${sep5}`)) {
22364
+ if (absolute !== root && !absolute.startsWith(`${root}${sep7}`)) {
22276
22365
  throw new Error(`RDD receipt path "${input}" is outside the project root`);
22277
22366
  }
22278
- const normalized = relative7(root, absolute);
22367
+ const normalized = relative9(root, absolute);
22279
22368
  if (!normalized || normalized === ".") {
22280
22369
  throw new Error("RDD receipts require file paths, not the project root");
22281
22370
  }
22282
- return { absolute, relative: normalized.split(sep5).join("/") };
22371
+ return { absolute, relative: normalized.split(sep7).join("/") };
22283
22372
  }
22284
22373
  async function fingerprint(projectRoot, paths) {
22285
22374
  const deduplicated = new Map;
@@ -22289,7 +22378,7 @@ async function fingerprint(projectRoot, paths) {
22289
22378
  }
22290
22379
  const result = [];
22291
22380
  for (const [path, absolute] of [...deduplicated.entries()].sort(([left], [right]) => left.localeCompare(right))) {
22292
- result.push({ path, hash: sha2562(await readFile27(absolute)) });
22381
+ result.push({ path, hash: sha2562(await readFile28(absolute)) });
22293
22382
  }
22294
22383
  return result;
22295
22384
  }
@@ -22334,7 +22423,7 @@ async function collectReceiptPaths(projectRoot) {
22334
22423
  continue;
22335
22424
  }
22336
22425
  if (entry.isFile())
22337
- paths.push(relative7(root, join18(current, entry.name)).split(sep5).join("/"));
22426
+ paths.push(relative9(root, join18(current, entry.name)).split(sep7).join("/"));
22338
22427
  }
22339
22428
  }
22340
22429
  await visit2(root);
@@ -22349,7 +22438,7 @@ async function verifyReceipt(projectRoot, receipt) {
22349
22438
  for (const path of pathsToCheck) {
22350
22439
  try {
22351
22440
  const canonical = canonicalPath(projectRoot, path);
22352
- const hash = sha2562(await readFile27(canonical.absolute));
22441
+ const hash = sha2562(await readFile28(canonical.absolute));
22353
22442
  const currentEntry = { path: canonical.relative, hash };
22354
22443
  current.push(currentEntry);
22355
22444
  currentByPath.set(currentEntry.path, currentEntry);
@@ -22386,7 +22475,7 @@ var init_rdd_receipt = __esm(() => {
22386
22475
 
22387
22476
  // src/core/verification/verification-runner.ts
22388
22477
  import { existsSync as existsSync3 } from "node:fs";
22389
- import { readFile as readFile28 } from "node:fs/promises";
22478
+ import { readFile as readFile29 } from "node:fs/promises";
22390
22479
  import { dirname as dirname10, join as join19, resolve as resolve27 } from "node:path";
22391
22480
  import { mkdir as mkdir18, readdir as readdir8, writeFile as writeFile18 } from "node:fs/promises";
22392
22481
  function isPassingEvidence(ev) {
@@ -22449,7 +22538,7 @@ async function collectTaskEvidence(projectRoot, taskId) {
22449
22538
  if (!file.endsWith(".json"))
22450
22539
  continue;
22451
22540
  try {
22452
- const raw = await readFile28(join19(evDir, file), "utf-8");
22541
+ const raw = await readFile29(join19(evDir, file), "utf-8");
22453
22542
  const ev = EvidenceSchema.parse(JSON.parse(raw));
22454
22543
  if (ev.relatedTask === taskId)
22455
22544
  records.push(ev);
@@ -22636,7 +22725,7 @@ async function loadTddSuiteEvidence(projectRoot, taskId, evidenceId) {
22636
22725
  return path;
22637
22726
  let raw;
22638
22727
  try {
22639
- raw = await readFile28(path.data, "utf-8");
22728
+ raw = await readFile29(path.data, "utf-8");
22640
22729
  } catch (e) {
22641
22730
  return err(e instanceof Error ? e : new Error(String(e)));
22642
22731
  }
@@ -23037,8 +23126,8 @@ var init_loop_state = __esm(() => {
23037
23126
  });
23038
23127
 
23039
23128
  // src/core/loop/git-stats.ts
23040
- import { execFile as execFile2 } from "node:child_process";
23041
- import { promisify as promisify2 } from "node:util";
23129
+ import { execFile as execFile3 } from "node:child_process";
23130
+ import { promisify as promisify3 } from "node:util";
23042
23131
  function countPorcelain(output) {
23043
23132
  return output.split(`
23044
23133
  `).filter((line) => line.trim().length > 0).length;
@@ -23062,12 +23151,12 @@ function countNumstat(output) {
23062
23151
  async function fetchGitChangeStats(cwd) {
23063
23152
  try {
23064
23153
  const [status, diff] = await Promise.all([
23065
- execFileAsync2("git", ["status", "--porcelain"], {
23154
+ execFileAsync3("git", ["status", "--porcelain"], {
23066
23155
  cwd,
23067
23156
  encoding: "utf-8",
23068
23157
  timeout: 15000
23069
23158
  }),
23070
- execFileAsync2("git", ["diff", "--numstat"], {
23159
+ execFileAsync3("git", ["diff", "--numstat"], {
23071
23160
  cwd,
23072
23161
  encoding: "utf-8",
23073
23162
  timeout: 15000
@@ -23091,9 +23180,9 @@ async function readGitChangeStats(cwd, options) {
23091
23180
  cache = { cwd, at: now, stats };
23092
23181
  return stats;
23093
23182
  }
23094
- var execFileAsync2, GIT_STATS_CACHE_TTL_MS = 3000, cache;
23183
+ var execFileAsync3, GIT_STATS_CACHE_TTL_MS = 3000, cache;
23095
23184
  var init_git_stats = __esm(() => {
23096
- execFileAsync2 = promisify2(execFile2);
23185
+ execFileAsync3 = promisify3(execFile3);
23097
23186
  });
23098
23187
 
23099
23188
  // src/core/loop/loop-engine.ts
@@ -23826,11 +23915,11 @@ __export(exports_rdd_command, {
23826
23915
  rddCommand: () => rddCommand,
23827
23916
  rddStatus: () => rddStatus
23828
23917
  });
23829
- import { execFile as execFile3 } from "node:child_process";
23918
+ import { execFile as execFile4 } from "node:child_process";
23830
23919
  import { existsSync as existsSync4 } from "node:fs";
23831
- import { mkdir as mkdir19, readFile as readFile29, readdir as readdir9, rename as rename3, writeFile as writeFile19, chmod } from "node:fs/promises";
23920
+ import { mkdir as mkdir19, readFile as readFile30, readdir as readdir9, rename as rename3, writeFile as writeFile19, chmod } from "node:fs/promises";
23832
23921
  import { dirname as dirname11, resolve as resolve28 } from "node:path";
23833
- import { promisify as promisify3 } from "node:util";
23922
+ import { promisify as promisify4 } from "node:util";
23834
23923
  function receiptPath(projectRoot, id) {
23835
23924
  const directory = resolve28(evidenceDir(projectRoot));
23836
23925
  const path = resolve28(directory, `${id.replace(/[^A-Za-z0-9_-]/g, "_")}.json`);
@@ -23839,7 +23928,7 @@ function receiptPath(projectRoot, id) {
23839
23928
  return path;
23840
23929
  }
23841
23930
  async function loadReceipt(projectRoot, id) {
23842
- const raw = await readFile29(receiptPath(projectRoot, id), "utf-8");
23931
+ const raw = await readFile30(receiptPath(projectRoot, id), "utf-8");
23843
23932
  const evidence = EvidenceSchema.parse(JSON.parse(raw));
23844
23933
  if (evidence.type !== "rdd")
23845
23934
  throw new Error(`Evidence "${id}" is not an RDD receipt`);
@@ -23877,7 +23966,7 @@ async function rddStatus(projectRoot, taskId) {
23877
23966
  return receipts.sort((left, right) => right.timestamp.localeCompare(left.timestamp));
23878
23967
  }
23879
23968
  async function installHooks(projectRoot) {
23880
- const { stdout } = await execFileAsync3("git", ["rev-parse", "--git-path", "hooks"], {
23969
+ const { stdout } = await execFileAsync4("git", ["rev-parse", "--git-path", "hooks"], {
23881
23970
  cwd: projectRoot,
23882
23971
  encoding: "utf8"
23883
23972
  });
@@ -23890,7 +23979,7 @@ async function installHooks(projectRoot) {
23890
23979
  const backup = resolve28(hooksDir, `${hook}.rdd-original`);
23891
23980
  const marker = "# codeconductor-rdd-hook";
23892
23981
  if (existsSync4(path)) {
23893
- const existing = await readFile29(path, "utf8");
23982
+ const existing = await readFile30(path, "utf8");
23894
23983
  if (existing.includes(marker)) {
23895
23984
  skipped.push(hook);
23896
23985
  continue;
@@ -23969,12 +24058,12 @@ async function rddCommand(options) {
23969
24058
  return { code: 1, data: { success: false, errors: [error instanceof Error ? error.message : String(error)] } };
23970
24059
  }
23971
24060
  }
23972
- var execFileAsync3;
24061
+ var execFileAsync4;
23973
24062
  var init_rdd_command = __esm(() => {
23974
24063
  init_schemas();
23975
24064
  init_paths();
23976
24065
  init_rdd_receipt();
23977
- execFileAsync3 = promisify3(execFile3);
24066
+ execFileAsync4 = promisify4(execFile4);
23978
24067
  });
23979
24068
 
23980
24069
  // src/core/ccep/confirmation-gate.ts
@@ -24055,6 +24144,101 @@ function buildOpenspecPhaseContext(context, phase) {
24055
24144
  };
24056
24145
  }
24057
24146
 
24147
+ // src/core/ccep/output-validator.ts
24148
+ function resolveOutputSchemaName(schemaName, role) {
24149
+ if (schemaName !== "agent-output" && SCHEMA_REGISTRY[schemaName]) {
24150
+ return schemaName;
24151
+ }
24152
+ if (role === "implementer") {
24153
+ return "implementer-output";
24154
+ }
24155
+ if (role === "reviewer") {
24156
+ return "review-report";
24157
+ }
24158
+ if (role === "task-coach" || role === "architect") {
24159
+ return schemaName === "technical-plan" ? "technical-plan" : "planner-output";
24160
+ }
24161
+ return schemaName;
24162
+ }
24163
+ function validateResolvedOutputSchema(schemaName, data) {
24164
+ const schema = SCHEMA_REGISTRY[schemaName];
24165
+ if (!schema) {
24166
+ return {
24167
+ valid: false,
24168
+ schema: schemaName,
24169
+ errors: [`Unknown output schema: ${schemaName}`]
24170
+ };
24171
+ }
24172
+ const parsed = schema.safeParse(data);
24173
+ if (parsed.success) {
24174
+ return { valid: true, schema: schemaName, data: parsed.data };
24175
+ }
24176
+ return {
24177
+ valid: false,
24178
+ schema: schemaName,
24179
+ errors: parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
24180
+ };
24181
+ }
24182
+ function parseJsonInput(raw) {
24183
+ return JSON.parse(raw);
24184
+ }
24185
+ function validateOutputForRole(role, outputSchema, data) {
24186
+ const resolved = resolveOutputSchemaName(outputSchema, role);
24187
+ if (resolved === "implementer-output") {
24188
+ try {
24189
+ return { valid: true, schema: "implementer-output", data: validateImplementerOutput(data) };
24190
+ } catch (err3) {
24191
+ return {
24192
+ valid: false,
24193
+ schema: "implementer-output",
24194
+ errors: [String(err3)]
24195
+ };
24196
+ }
24197
+ }
24198
+ if (resolved === "review-report") {
24199
+ try {
24200
+ return { valid: true, schema: "review-report", data: validateReviewerOutput(data) };
24201
+ } catch (err3) {
24202
+ return {
24203
+ valid: false,
24204
+ schema: "review-report",
24205
+ errors: [String(err3)]
24206
+ };
24207
+ }
24208
+ }
24209
+ if (resolved === "planner-output") {
24210
+ try {
24211
+ return { valid: true, schema: "planner-output", data: validatePlannerOutput(data) };
24212
+ } catch (err3) {
24213
+ return {
24214
+ valid: false,
24215
+ schema: "planner-output",
24216
+ errors: [String(err3)]
24217
+ };
24218
+ }
24219
+ }
24220
+ return validateResolvedOutputSchema(resolved, data);
24221
+ }
24222
+ var SCHEMA_REGISTRY;
24223
+ var init_output_validator = __esm(() => {
24224
+ init_schemas();
24225
+ SCHEMA_REGISTRY = {
24226
+ "planner-output": PlannerOutputSchema,
24227
+ "implementer-output": ImplementerOutputSchema,
24228
+ "review-report": ReviewerOutputSchema,
24229
+ "technical-plan": TechnicalPlanOutputSchema,
24230
+ "fix-intake-output": FixIntakeOutputSchema,
24231
+ "council-verdict": CouncilVerdictSchema,
24232
+ "agent-output": AgentOutputSchema,
24233
+ taskcard: CanonicalTaskCardSchema,
24234
+ "test-plan": AgentOutputSchema,
24235
+ "complexity-audit": AgentOutputSchema,
24236
+ "api-contract": AgentOutputSchema,
24237
+ "pagespeed-report": AgentOutputSchema,
24238
+ "scorecard-record": ScorecardRecordSchema
24239
+ };
24240
+ });
24241
+
24058
24242
  // src/core/ccep/prompt-compiler.ts
24059
24243
  function buildSystemLayer() {
24060
24244
  return [
@@ -24067,24 +24251,22 @@ function buildSystemLayer() {
24067
24251
  ].join(`
24068
24252
  `);
24069
24253
  }
24070
- function buildAgentLayer(role, phase) {
24254
+ function buildAgentLayer(role, phase, outputSchema) {
24071
24255
  const label = ROLE_LABELS[role] ?? role;
24256
+ let instruction;
24072
24257
  if (role === "task-coach" && phase === "intake") {
24073
- return `You are the Planner / ${label} of CodeConductor.
24258
+ instruction = `You are the Planner / ${label} of CodeConductor.
24074
24259
  Convert product intent into a structured plan without writing code.`;
24075
- }
24076
- if (role === "implementer") {
24077
- return `You are the ${label}.
24078
- Execute only your assigned phase: ${phase}.
24079
- Write the minimal diff. Return implementer-output JSON only.`;
24080
- }
24081
- if (role === "reviewer") {
24082
- return `You are the ${label}.
24260
+ } else if (role === "implementer") {
24261
+ instruction = `You are the ${label}.
24083
24262
  Execute only your assigned phase: ${phase}.
24084
- Return review-report JSON only.`;
24085
- }
24086
- return `You are the ${label}.
24263
+ Write the minimal diff.`;
24264
+ } else {
24265
+ instruction = `You are the ${label}.
24087
24266
  Execute only your assigned phase: ${phase}.`;
24267
+ }
24268
+ return `${instruction}
24269
+ Return ${outputSchema} JSON only.`;
24088
24270
  }
24089
24271
  function buildTaskLayer(context, phase) {
24090
24272
  if (context.envelope.command === "openspec") {
@@ -24094,19 +24276,28 @@ function buildTaskLayer(context, phase) {
24094
24276
  command: context.envelope.command,
24095
24277
  intent: context.intent,
24096
24278
  phase,
24097
- userRequest: context.envelope.userRequest,
24098
24279
  project: context.project.name
24099
24280
  }, null, 2);
24100
24281
  }
24282
+ function scopeKnowledgeForRole(knowledge, role) {
24283
+ const keys = KNOWLEDGE_KEYS_BY_ROLE[role];
24284
+ if (!keys)
24285
+ return knowledge;
24286
+ if (Object.keys(knowledge).some((key) => !PRODUCT_GRAPH_KNOWLEDGE_KEYS.has(key))) {
24287
+ return knowledge;
24288
+ }
24289
+ return Object.fromEntries(keys.filter((key) => knowledge[key] !== undefined).map((key) => [key, knowledge[key]]));
24290
+ }
24101
24291
  function compilePrompt(options) {
24102
24292
  const { role, phase, context, promptVersion } = options;
24103
24293
  const phaseDef = context.profile.phases.find((p) => p.id === phase);
24104
- const schemaName = phaseDef?.outputSchema ?? context.outputSchema ?? "agent-output";
24294
+ const configuredSchemaName = phaseDef?.outputSchema ?? context.outputSchema ?? "agent-output";
24295
+ const schemaName = resolveOutputSchemaName(configuredSchemaName, role);
24105
24296
  const schemaBody = OUTPUT_SCHEMAS[schemaName] ?? OUTPUT_SCHEMAS["agent-output"];
24106
- const knowledge = context.envelope.command === "openspec" ? buildOpenspecPhaseContext(context, phase).knowledge : context.knowledge;
24297
+ const knowledge = context.envelope.command === "openspec" ? buildOpenspecPhaseContext(context, phase).knowledge : scopeKnowledgeForRole(context.knowledge, role);
24107
24298
  const layers = [
24108
24299
  { name: "system", content: buildSystemLayer() },
24109
- { name: "agent", content: buildAgentLayer(role, phase) },
24300
+ { name: "agent", content: buildAgentLayer(role, phase, schemaName) },
24110
24301
  {
24111
24302
  name: "policies",
24112
24303
  content: JSON.stringify({ ...context.policies, promptVersion }, null, 2)
@@ -24124,10 +24315,11 @@ ${schemaBody}`
24124
24315
  ${layer.content}`).join(`
24125
24316
 
24126
24317
  `);
24127
- return { layers, prompt };
24318
+ return { layers, prompt, outputSchema: schemaName };
24128
24319
  }
24129
- var ROLE_LABELS, OUTPUT_SCHEMAS;
24320
+ var ROLE_LABELS, OUTPUT_SCHEMAS, KNOWLEDGE_KEYS_BY_ROLE, PRODUCT_GRAPH_KNOWLEDGE_KEYS;
24130
24321
  var init_prompt_compiler = __esm(() => {
24322
+ init_output_validator();
24131
24323
  ROLE_LABELS = {
24132
24324
  "task-coach": "Task Coach",
24133
24325
  architect: "Architect",
@@ -24182,108 +24374,50 @@ var init_prompt_compiler = __esm(() => {
24182
24374
  "filesChanged": [{ "path": "", "summary": "" }],
24183
24375
  "tests": { "runner": "", "result": "passed" | "failed" }
24184
24376
  }`,
24185
- "agent-output": '{ "status": "success", "artifacts": [], "next_actions": [] }'
24186
- };
24187
- });
24188
-
24189
- // src/core/ccep/output-validator.ts
24190
- function resolveOutputSchemaName(schemaName, role) {
24191
- if (schemaName !== "agent-output" && SCHEMA_REGISTRY[schemaName]) {
24192
- return schemaName;
24193
- }
24194
- if (role === "implementer") {
24195
- return "implementer-output";
24196
- }
24197
- if (role === "reviewer") {
24198
- return "review-report";
24199
- }
24200
- if (role === "task-coach" || role === "architect") {
24201
- return schemaName === "technical-plan" ? "technical-plan" : "planner-output";
24202
- }
24203
- return schemaName;
24204
- }
24205
- function validateAgentOutputBySchema(schemaName, data, role) {
24206
- const resolved = resolveOutputSchemaName(schemaName, role);
24207
- const schema = SCHEMA_REGISTRY[resolved];
24208
- if (!schema) {
24209
- return {
24210
- valid: false,
24211
- schema: resolved,
24212
- errors: [`Unknown output schema: ${resolved}`]
24213
- };
24214
- }
24215
- const parsed = schema.safeParse(data);
24216
- if (parsed.success) {
24217
- return { valid: true, schema: resolved, data: parsed.data };
24218
- }
24219
- return {
24220
- valid: false,
24221
- schema: resolved,
24222
- errors: parsed.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
24223
- };
24224
- }
24225
- function parseJsonInput(raw) {
24226
- return JSON.parse(raw);
24227
- }
24228
- function validateOutputForRole(role, outputSchema, data) {
24229
- if (role === "implementer") {
24230
- try {
24231
- return { valid: true, schema: "implementer-output", data: validateImplementerOutput(data) };
24232
- } catch (err3) {
24233
- return {
24234
- valid: false,
24235
- schema: "implementer-output",
24236
- errors: [String(err3)]
24237
- };
24238
- }
24239
- }
24240
- if (role === "reviewer") {
24241
- try {
24242
- return { valid: true, schema: "review-report", data: validateReviewerOutput(data) };
24243
- } catch (err3) {
24244
- return {
24245
- valid: false,
24246
- schema: "review-report",
24247
- errors: [String(err3)]
24248
- };
24249
- }
24250
- }
24251
- if (outputSchema === "planner-output" || role === "task-coach") {
24252
- try {
24253
- return { valid: true, schema: "planner-output", data: validatePlannerOutput(data) };
24254
- } catch (err3) {
24255
- return {
24256
- valid: false,
24257
- schema: "planner-output",
24258
- errors: [String(err3)]
24259
- };
24260
- }
24261
- }
24262
- return validateAgentOutputBySchema(outputSchema, data, role);
24263
- }
24264
- var SCHEMA_REGISTRY;
24265
- var init_output_validator = __esm(() => {
24266
- init_schemas();
24267
- SCHEMA_REGISTRY = {
24268
- "planner-output": PlannerOutputSchema,
24269
- "implementer-output": ImplementerOutputSchema,
24270
- "review-report": ReviewerOutputSchema,
24271
- "technical-plan": TechnicalPlanOutputSchema,
24272
- "fix-intake-output": AgentOutputSchema,
24273
- "council-verdict": CouncilVerdictSchema,
24274
- "agent-output": AgentOutputSchema,
24275
- taskcard: CanonicalTaskCardSchema,
24276
- "test-plan": AgentOutputSchema,
24277
- "complexity-audit": AgentOutputSchema,
24278
- "api-contract": AgentOutputSchema,
24279
- "pagespeed-report": AgentOutputSchema,
24280
- "scorecard-record": ScorecardRecordSchema
24281
- };
24377
+ "agent-output": `{
24378
+ "status": "success" | "failure" | "blocked" | "needs_clarification",
24379
+ "confidence": 0.0,
24380
+ "warnings": [],
24381
+ "artifacts": [],
24382
+ "next_actions": []
24383
+ }`,
24384
+ "scorecard-record": `{
24385
+ "id": "",
24386
+ "taskId": "",
24387
+ "agent": "",
24388
+ "contractVersion": "",
24389
+ "criteria": [],
24390
+ "weightedScore": 0.0,
24391
+ "verdict": "PASS" | "REVISE" | "REJECT",
24392
+ "findings": [],
24393
+ "createdAt": ""
24394
+ }`
24395
+ };
24396
+ KNOWLEDGE_KEYS_BY_ROLE = {
24397
+ "task-coach": ["productName", "domains", "requirements", "risks"],
24398
+ architect: ["productName", "domains", "decisions", "requirements", "risks"],
24399
+ tester: ["productName", "requirements", "risks"],
24400
+ implementer: ["productName", "decisions", "requirements"],
24401
+ reviewer: ["productName", "decisions", "requirements", "risks"],
24402
+ docs: ["productName", "decisions", "requirements"],
24403
+ "contract-builder": ["productName", "domains", "decisions", "requirements"],
24404
+ "complexity-auditor": ["productName", "decisions", "requirements", "risks"],
24405
+ orchestrator: ["productName", "domains", "decisions", "requirements", "risks"],
24406
+ "repo-explorer": ["productName", "domains", "requirements"]
24407
+ };
24408
+ PRODUCT_GRAPH_KNOWLEDGE_KEYS = new Set([
24409
+ "productName",
24410
+ "domains",
24411
+ "decisions",
24412
+ "risks",
24413
+ "requirements",
24414
+ "nodeCount"
24415
+ ]);
24282
24416
  });
24283
24417
 
24284
24418
  // src/core/ccep/command-parser.ts
24285
24419
  import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
24286
- import { join as join20 } from "node:path";
24420
+ import { basename as basename7, join as join20 } from "node:path";
24287
24421
  function readProjectId(projectRoot) {
24288
24422
  const packageJsonPath = join20(projectRoot, "package.json");
24289
24423
  if (existsSync5(packageJsonPath)) {
@@ -24294,7 +24428,7 @@ function readProjectId(projectRoot) {
24294
24428
  }
24295
24429
  } catch {}
24296
24430
  }
24297
- return projectRoot.split("/").pop() ?? "unknown";
24431
+ return basename7(projectRoot) || "unknown";
24298
24432
  }
24299
24433
  function parseCommand(command, userRequest, projectRoot) {
24300
24434
  const validatedCommand = WorkflowCommandSchema.parse(command);
@@ -24390,7 +24524,7 @@ var init_workflow_profile_loader = __esm(() => {
24390
24524
  // src/core/evaluation/harness-catalog.ts
24391
24525
  import { createHash as createHash5 } from "node:crypto";
24392
24526
  import { existsSync as existsSync6, readFileSync as readFileSync4 } from "node:fs";
24393
- import { copyFile as copyFile2, mkdir as mkdir20, readFile as readFile30, readdir as readdir10, rm as rm4, writeFile as writeFile20 } from "node:fs/promises";
24527
+ import { copyFile as copyFile2, mkdir as mkdir20, readFile as readFile31, readdir as readdir10, rm as rm4, writeFile as writeFile20 } from "node:fs/promises";
24394
24528
  import { dirname as dirname12, join as join21, resolve as resolve29 } from "node:path";
24395
24529
  function catalogIds(catalog = DEFAULT_HARNESS_CATALOG) {
24396
24530
  return catalog.components.map((c) => c.id);
@@ -24446,7 +24580,7 @@ function harnessFingerprint(disabled, contractVersion, ccepProfile = "feature",
24446
24580
  async function loadHarnessCatalog(projectRoot) {
24447
24581
  const overridePath = resolve29(projectRoot, EVAL_DIR2, CATALOG_FILE);
24448
24582
  try {
24449
- const raw = await readFile30(overridePath, "utf-8");
24583
+ const raw = await readFile31(overridePath, "utf-8");
24450
24584
  return HarnessCatalogSchema.parse($parse(raw));
24451
24585
  } catch {
24452
24586
  return DEFAULT_HARNESS_CATALOG;
@@ -24457,7 +24591,7 @@ function activeOverlayPath(projectRoot) {
24457
24591
  }
24458
24592
  async function readActiveOverlay(projectRoot) {
24459
24593
  try {
24460
- const raw = await readFile30(activeOverlayPath(projectRoot), "utf-8");
24594
+ const raw = await readFile31(activeOverlayPath(projectRoot), "utf-8");
24461
24595
  return HarnessOverlaySchema.parse($parse(raw));
24462
24596
  } catch {
24463
24597
  return null;
@@ -24714,7 +24848,7 @@ var init_harness_catalog = __esm(() => {
24714
24848
 
24715
24849
  // src/core/openspec/openspec-state.ts
24716
24850
  import { createHash as createHash6 } from "node:crypto";
24717
- import { mkdir as mkdir21, readFile as readFile31, rename as rename4, writeFile as writeFile21 } from "node:fs/promises";
24851
+ import { mkdir as mkdir21, readFile as readFile32, rename as rename4, writeFile as writeFile21 } from "node:fs/promises";
24718
24852
  import { resolve as resolve30 } from "node:path";
24719
24853
  function canTransition(from, to) {
24720
24854
  return ALLOWED_TRANSITIONS[from]?.includes(to) ?? false;
@@ -24742,7 +24876,7 @@ async function writeFileAtomic(path, content) {
24742
24876
  async function loadOpenspecState(projectRoot) {
24743
24877
  try {
24744
24878
  const filePath = resolve30(projectRoot, STATE_FILE);
24745
- const content = await readFile31(filePath, "utf-8");
24879
+ const content = await readFile32(filePath, "utf-8");
24746
24880
  const data = JSON.parse(content);
24747
24881
  return ok(OpenspecStateSchema.parse(data));
24748
24882
  } catch (e) {
@@ -24867,7 +25001,7 @@ var init_openspec_state = __esm(() => {
24867
25001
 
24868
25002
  // src/core/ccep/context-resolver.ts
24869
25003
  import { existsSync as existsSync7 } from "node:fs";
24870
- import { readFile as readFile32 } from "node:fs/promises";
25004
+ import { readFile as readFile33 } from "node:fs/promises";
24871
25005
  import { join as join22 } from "node:path";
24872
25006
  async function loadProductKnowledge(projectRoot) {
24873
25007
  if (isProductGraphDisabled(projectRoot)) {
@@ -24878,7 +25012,7 @@ async function loadProductKnowledge(projectRoot) {
24878
25012
  return {};
24879
25013
  }
24880
25014
  try {
24881
- const raw = await readFile32(graphPath, "utf-8");
25015
+ const raw = await readFile33(graphPath, "utf-8");
24882
25016
  const graph = ProductGraphSchema.parse(JSON.parse(raw));
24883
25017
  return {
24884
25018
  productName: graph.productName,
@@ -25448,6 +25582,11 @@ async function ccepCommand(options) {
25448
25582
  input,
25449
25583
  contextPath,
25450
25584
  promptVersion = "v1.0.0",
25585
+ view = "full",
25586
+ recordTelemetry = false,
25587
+ executionId,
25588
+ taskId,
25589
+ contextStrategy,
25451
25590
  config,
25452
25591
  rest
25453
25592
  } = options;
@@ -25498,6 +25637,28 @@ async function ccepCommand(options) {
25498
25637
  };
25499
25638
  }
25500
25639
  case "compile": {
25640
+ if (!["prompt", "layers", "full"].includes(view)) {
25641
+ return {
25642
+ code: 1,
25643
+ data: {
25644
+ success: false,
25645
+ command: "ccep compile",
25646
+ errors: [`Unknown compile view: ${view}`]
25647
+ }
25648
+ };
25649
+ }
25650
+ if (contextStrategy !== undefined && !CONTEXT_STRATEGIES.includes(contextStrategy)) {
25651
+ return {
25652
+ code: 1,
25653
+ data: {
25654
+ success: false,
25655
+ command: "ccep compile",
25656
+ errors: [
25657
+ `Invalid context strategy: ${contextStrategy}. Expected one of: ${CONTEXT_STRATEGIES.join(", ")}`
25658
+ ]
25659
+ }
25660
+ };
25661
+ }
25501
25662
  const parsed = parseWorkflowCommand(command);
25502
25663
  if (!parsed.ok) {
25503
25664
  return {
@@ -25548,12 +25709,69 @@ async function ccepCommand(options) {
25548
25709
  outputSchema: resolvedPhase.outputSchema
25549
25710
  };
25550
25711
  }
25712
+ const compileStartedAt = Date.now();
25551
25713
  const compiled = compilePrompt({
25552
25714
  role: agentRole,
25553
25715
  phase: phaseId,
25554
25716
  context,
25555
25717
  promptVersion
25556
25718
  });
25719
+ if (recordTelemetry) {
25720
+ const event = await appendEvent(projectRoot, {
25721
+ type: "context.compiled",
25722
+ timestamp: new Date().toISOString(),
25723
+ payload: {
25724
+ workflow: parsed.command,
25725
+ phase: phaseId,
25726
+ agent: agentRole,
25727
+ executionId: executionId ?? "unknown",
25728
+ taskId: taskId ?? "unknown",
25729
+ contextStrategy: contextStrategy ?? "unknown",
25730
+ promptVersion,
25731
+ view,
25732
+ source: "compiler",
25733
+ success: true,
25734
+ provider: "unknown",
25735
+ model: "unknown",
25736
+ effort: "unknown",
25737
+ sessionId: "unknown",
25738
+ parentSessionId: "unknown",
25739
+ requestedModel: "unknown",
25740
+ effectiveModel: "unknown",
25741
+ inputTokens: "unknown",
25742
+ outputTokens: "unknown",
25743
+ reasoningTokens: "unknown",
25744
+ cacheReadTokens: "unknown",
25745
+ cacheWriteTokens: "unknown",
25746
+ cachedTokens: "unknown",
25747
+ contextTokensEstimate: "unknown",
25748
+ filesRead: "unknown",
25749
+ uniqueFilesRead: "unknown",
25750
+ repeatedFileReads: "unknown",
25751
+ toolCalls: "unknown",
25752
+ retries: "unknown",
25753
+ compactions: "unknown",
25754
+ modelSwitches: "unknown",
25755
+ promptBytes: Buffer.byteLength(compiled.prompt, "utf-8"),
25756
+ layerBytes: Object.fromEntries(compiled.layers.map((layer) => [
25757
+ layer.name,
25758
+ Buffer.byteLength(layer.content, "utf-8")
25759
+ ])),
25760
+ durationMs: Date.now() - compileStartedAt
25761
+ }
25762
+ });
25763
+ if (!event.success) {
25764
+ return {
25765
+ code: 1,
25766
+ data: {
25767
+ success: false,
25768
+ command: "ccep compile",
25769
+ errors: [`Could not record compile telemetry: ${event.error.message}`]
25770
+ }
25771
+ };
25772
+ }
25773
+ }
25774
+ const representation = view === "prompt" ? { prompt: compiled.prompt } : view === "layers" ? { layers: compiled.layers } : { layers: compiled.layers, prompt: compiled.prompt };
25557
25775
  return {
25558
25776
  code: 0,
25559
25777
  data: {
@@ -25561,10 +25779,9 @@ async function ccepCommand(options) {
25561
25779
  command: "ccep compile",
25562
25780
  phase: phaseId,
25563
25781
  role: agentRole,
25564
- outputSchema: resolvedPhase.outputSchema,
25782
+ outputSchema: compiled.outputSchema,
25565
25783
  promptVersion,
25566
- layers: compiled.layers,
25567
- prompt: compiled.prompt
25784
+ ...representation
25568
25785
  }
25569
25786
  };
25570
25787
  }
@@ -25818,9 +26035,11 @@ async function ccepCommand(options) {
25818
26035
  };
25819
26036
  }
25820
26037
  }
26038
+ var CONTEXT_STRATEGIES;
25821
26039
  var init_ccep_command = __esm(() => {
25822
26040
  init_path_containment();
25823
26041
  init_prompt_compiler();
26042
+ init_episodic_store();
25824
26043
  init_output_validator();
25825
26044
  init_command_parser();
25826
26045
  init_context_resolver();
@@ -25828,6 +26047,7 @@ var init_ccep_command = __esm(() => {
25828
26047
  init_council_consensus();
25829
26048
  init_task_card_validator();
25830
26049
  init_schemas();
26050
+ CONTEXT_STRATEGIES = ["sticky", "isolated", "artifact", "compact", "fork", "full"];
25831
26051
  });
25832
26052
 
25833
26053
  // src/core/openspec/backlog-validator.ts
@@ -25969,8 +26189,8 @@ var init_backlog_validator = __esm(() => {
25969
26189
  });
25970
26190
 
25971
26191
  // src/core/openspec/backlog-scanner.ts
25972
- import { execFileSync as execFileSync2 } from "node:child_process";
25973
- import { readFile as readFile33 } from "node:fs/promises";
26192
+ import { execFileSync } from "node:child_process";
26193
+ import { readFile as readFile34 } from "node:fs/promises";
25974
26194
  import { resolve as resolve31 } from "node:path";
25975
26195
  function itemSnapshot(items) {
25976
26196
  const snap = {};
@@ -25982,11 +26202,11 @@ function itemSnapshot(items) {
25982
26202
  async function scanBacklog(projectRoot, previousSnapshot = {}) {
25983
26203
  try {
25984
26204
  const filePath = resolve31(projectRoot, BACKLOG_FILENAME);
25985
- const content = await readFile33(filePath, "utf-8");
26205
+ const content = await readFile34(filePath, "utf-8");
25986
26206
  const hash = hashContent(content);
25987
26207
  let fileChanged = true;
25988
26208
  try {
25989
- const diff = execFileSync2("git", ["diff", "--name-only", "--", BACKLOG_FILENAME], {
26209
+ const diff = execFileSync("git", ["diff", "--name-only", "--", BACKLOG_FILENAME], {
25990
26210
  cwd: projectRoot,
25991
26211
  encoding: "utf-8",
25992
26212
  stdio: ["ignore", "pipe", "ignore"]
@@ -26220,7 +26440,7 @@ var init_backlog_planner = __esm(() => {
26220
26440
 
26221
26441
  // src/core/openspec/openspec-generator.ts
26222
26442
  import { mkdir as mkdir22, rename as rename5, writeFile as writeFile22 } from "node:fs/promises";
26223
- import { basename as basename6, resolve as resolve32 } from "node:path";
26443
+ import { basename as basename8, resolve as resolve32 } from "node:path";
26224
26444
  function pad3(n) {
26225
26445
  return String(n).padStart(3, "0");
26226
26446
  }
@@ -26367,7 +26587,7 @@ capabilities:
26367
26587
  }
26368
26588
  async function archiveChangeFolder(projectRoot, changePath) {
26369
26589
  const src = resolve32(projectRoot, changePath);
26370
- const slug = basename6(changePath);
26590
+ const slug = basename8(changePath);
26371
26591
  const destDir = resolve32(projectRoot, "openspec", "changes", "archive");
26372
26592
  await mkdir22(destDir, { recursive: true });
26373
26593
  const dest = resolve32(destDir, slug);
@@ -26392,8 +26612,8 @@ var init_openspec_generator = __esm(() => {
26392
26612
  });
26393
26613
 
26394
26614
  // src/core/openspec/spec-quality.ts
26395
- import { readdir as readdir11, readFile as readFile34 } from "node:fs/promises";
26396
- import { join as join23, relative as relative8, resolve as resolve33 } from "node:path";
26615
+ import { readdir as readdir11, readFile as readFile35 } from "node:fs/promises";
26616
+ import { join as join23, relative as relative10, resolve as resolve33 } from "node:path";
26397
26617
  function uniqueIds(matches) {
26398
26618
  return [...new Set(matches ?? [])];
26399
26619
  }
@@ -26549,17 +26769,17 @@ async function assessChangeFolder(projectRoot, changePath) {
26549
26769
  const extra = ["proposal.md", "design.md"].map((name) => join23(changeRoot, name));
26550
26770
  const reports = [];
26551
26771
  for (const file of specFiles) {
26552
- const content = await readFile34(file, "utf-8");
26553
- reports.push(assessSpecMarkdown(content, relative8(root, file).replace(/\\/g, "/")));
26772
+ const content = await readFile35(file, "utf-8");
26773
+ reports.push(assessSpecMarkdown(content, relative10(root, file).replace(/\\/g, "/")));
26554
26774
  }
26555
26775
  for (const file of extra) {
26556
26776
  let content;
26557
26777
  try {
26558
- content = await readFile34(file, "utf-8");
26778
+ content = await readFile35(file, "utf-8");
26559
26779
  } catch {
26560
26780
  continue;
26561
26781
  }
26562
- const rel = relative8(root, file).replace(/\\/g, "/");
26782
+ const rel = relative10(root, file).replace(/\\/g, "/");
26563
26783
  if (PLACEHOLDER.test(content)) {
26564
26784
  reports.push({
26565
26785
  valid: false,
@@ -26610,7 +26830,7 @@ var init_spec_quality = __esm(() => {
26610
26830
  });
26611
26831
 
26612
26832
  // src/core/openspec/spec-analyzer.ts
26613
- import { readFile as readFile35, readdir as readdir12 } from "node:fs/promises";
26833
+ import { readFile as readFile36, readdir as readdir12 } from "node:fs/promises";
26614
26834
  import { join as join24, resolve as resolve34 } from "node:path";
26615
26835
  function unique(ids) {
26616
26836
  return [...new Set(ids)];
@@ -26648,7 +26868,7 @@ async function readDeltaSpecs(specDir) {
26648
26868
  if (entry.isDirectory()) {
26649
26869
  contents.push(await readDeltaSpecs(path));
26650
26870
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
26651
- contents.push(await readFile35(path, "utf-8"));
26871
+ contents.push(await readFile36(path, "utf-8"));
26652
26872
  }
26653
26873
  }
26654
26874
  return contents.filter(Boolean).join(`
@@ -26730,13 +26950,13 @@ async function analyzeChangeFolder(projectRoot, changePath, options) {
26730
26950
  const specMarkdown = await readDeltaSpecs(join24(changeRoot, "specs"));
26731
26951
  let tasksMarkdown = "";
26732
26952
  try {
26733
- tasksMarkdown = await readFile35(join24(changeRoot, "tasks.md"), "utf-8");
26953
+ tasksMarkdown = await readFile36(join24(changeRoot, "tasks.md"), "utf-8");
26734
26954
  } catch {
26735
26955
  tasksMarkdown = "";
26736
26956
  }
26737
26957
  let designMarkdown;
26738
26958
  try {
26739
- designMarkdown = await readFile35(join24(changeRoot, "design.md"), "utf-8");
26959
+ designMarkdown = await readFile36(join24(changeRoot, "design.md"), "utf-8");
26740
26960
  } catch {
26741
26961
  designMarkdown = undefined;
26742
26962
  }
@@ -26755,8 +26975,8 @@ var init_spec_analyzer = __esm(() => {
26755
26975
  });
26756
26976
 
26757
26977
  // src/core/openspec/spec-sync.ts
26758
- import { mkdir as mkdir23, readFile as readFile36, readdir as readdir13 } from "node:fs/promises";
26759
- import { join as join25, relative as relative9, resolve as resolve35 } from "node:path";
26978
+ import { mkdir as mkdir23, readFile as readFile37, readdir as readdir13 } from "node:fs/promises";
26979
+ import { join as join25, relative as relative11, resolve as resolve35 } from "node:path";
26760
26980
  async function markdownFiles(dir) {
26761
26981
  let entries;
26762
26982
  try {
@@ -26854,23 +27074,23 @@ async function syncChangeSpecs(projectRoot, changePath) {
26854
27074
  }
26855
27075
  const writes = [];
26856
27076
  for (const file of files) {
26857
- const delta = await readFile36(file, "utf-8");
27077
+ const delta = await readFile37(file, "utf-8");
26858
27078
  const requirements = deltaRequirements(delta);
26859
27079
  if (requirements.length === 0) {
26860
27080
  return {
26861
27081
  success: false,
26862
27082
  syncedPaths: [],
26863
- errors: [`${relative9(root, file)}: no delta requirements found`]
27083
+ errors: [`${relative11(root, file)}: no delta requirements found`]
26864
27084
  };
26865
27085
  }
26866
- const relativeSpec = relative9(changeSpecs, file);
27086
+ const relativeSpec = relative11(changeSpecs, file);
26867
27087
  const target = join25(root, "openspec", "specs", relativeSpec);
26868
27088
  let existing = `# ${relativeSpec.replace(/\\/g, "/")}
26869
27089
  `;
26870
27090
  try {
26871
- existing = await readFile36(target, "utf-8");
27091
+ existing = await readFile37(target, "utf-8");
26872
27092
  } catch {}
26873
- const merged = upsertRequirements(existing, requirements, relative9(root, file));
27093
+ const merged = upsertRequirements(existing, requirements, relative11(root, file));
26874
27094
  if (!merged.ok)
26875
27095
  return { success: false, syncedPaths: [], errors: [merged.error] };
26876
27096
  writes.push({ path: target, content: merged.content });
@@ -26881,7 +27101,7 @@ async function syncChangeSpecs(projectRoot, changePath) {
26881
27101
  }
26882
27102
  return {
26883
27103
  success: true,
26884
- syncedPaths: writes.map((write) => relative9(root, write.path).replace(/\\/g, "/")),
27104
+ syncedPaths: writes.map((write) => relative11(root, write.path).replace(/\\/g, "/")),
26885
27105
  errors: []
26886
27106
  };
26887
27107
  }
@@ -26897,7 +27117,7 @@ var exports_openspec_command = {};
26897
27117
  __export(exports_openspec_command, {
26898
27118
  openspecCommand: () => openspecCommand
26899
27119
  });
26900
- import { readFile as readFile37 } from "node:fs/promises";
27120
+ import { readFile as readFile38 } from "node:fs/promises";
26901
27121
  import { resolve as resolve36 } from "node:path";
26902
27122
  async function openspecCommand(options) {
26903
27123
  const { subcommand, itemId, projectRoot, output, reason } = options;
@@ -26954,7 +27174,7 @@ async function persistState(projectRoot, state) {
26954
27174
  return { ok: true };
26955
27175
  }
26956
27176
  async function readBacklogMarkdown(projectRoot) {
26957
- return readFile37(resolve36(projectRoot, BACKLOG_FILENAME), "utf-8");
27177
+ return readFile38(resolve36(projectRoot, BACKLOG_FILENAME), "utf-8");
26958
27178
  }
26959
27179
  async function transitionItem(projectRoot, item, to, progress) {
26960
27180
  const content = await readBacklogMarkdown(projectRoot);
@@ -27152,7 +27372,7 @@ async function handleAnalyze(projectRoot, itemId) {
27152
27372
  const policyParts = [];
27153
27373
  for (const name of ["AGENTS.md", "BACKLOG.md"]) {
27154
27374
  try {
27155
- policyParts.push(await readFile37(resolve36(projectRoot, name), "utf-8"));
27375
+ policyParts.push(await readFile38(resolve36(projectRoot, name), "utf-8"));
27156
27376
  } catch {}
27157
27377
  }
27158
27378
  const cards = itemCards(stateResult.data, targetId);
@@ -27584,10 +27804,45 @@ function resumeDecision(changedPaths) {
27584
27804
  }
27585
27805
  var init_context_assembly = () => {};
27586
27806
 
27807
+ // src/core/ccep/handoff-envelope.ts
27808
+ function buildHandoffEnvelope(ledger, changedPaths) {
27809
+ return HandoffEnvelopeSchema.parse({
27810
+ version: 1,
27811
+ task: {
27812
+ id: ledger.taskCard.id,
27813
+ objective: ledger.taskCard.objective,
27814
+ acceptanceCriteria: ledger.taskCard.acceptanceCriteria,
27815
+ status: ledger.taskCard.status
27816
+ },
27817
+ decisions: {
27818
+ constraints: ledger.taskCard.constraints,
27819
+ unresolved: ledger.tasks.filter((task) => task.status === "blocked").map((task) => task.title)
27820
+ },
27821
+ scope: {
27822
+ relevantFiles: ledger.taskCard.targetFiles,
27823
+ boundaries: ledger.taskCard.boundaries
27824
+ },
27825
+ verification: { evidence: ledger.evidence },
27826
+ change: { touchedFiles: [...new Set(changedPaths)].sort() },
27827
+ next: {
27828
+ role: ledger.taskCard.agentType,
27829
+ objective: ledger.nextStep
27830
+ },
27831
+ sources: {
27832
+ deliveryLedger: ledger.id,
27833
+ memoryTopicKey: ledger.memoryTopicKey,
27834
+ technicalPlanPath: ledger.technicalPlanPath
27835
+ }
27836
+ });
27837
+ }
27838
+ var init_handoff_envelope = __esm(() => {
27839
+ init_schemas();
27840
+ });
27841
+
27587
27842
  // src/core/delivery/delivery-ledger.ts
27588
27843
  import { createHash as createHash7 } from "node:crypto";
27589
- import { mkdir as mkdir24, readFile as readFile38, writeFile as writeFile23 } from "node:fs/promises";
27590
- import { resolve as resolve37, sep as sep6 } from "node:path";
27844
+ import { mkdir as mkdir24, readFile as readFile39, writeFile as writeFile23 } from "node:fs/promises";
27845
+ import { resolve as resolve37, sep as sep8 } from "node:path";
27591
27846
  function ledgerPath(projectRoot, id) {
27592
27847
  if (!/^[a-z0-9][a-z0-9-]{0,63}$/.test(id))
27593
27848
  throw new Error("Invalid delivery ledger id");
@@ -27598,12 +27853,12 @@ async function fingerprintWorkspace(projectRoot, paths) {
27598
27853
  const fingerprints = {};
27599
27854
  for (const path of paths) {
27600
27855
  const candidate = resolve37(root, path);
27601
- if (candidate !== root && !candidate.startsWith(`${root}${sep6}`)) {
27856
+ if (candidate !== root && !candidate.startsWith(`${root}${sep8}`)) {
27602
27857
  fingerprints[path] = "outside-root";
27603
27858
  continue;
27604
27859
  }
27605
27860
  try {
27606
- fingerprints[path] = createHash7("sha256").update(await readFile38(candidate)).digest("hex");
27861
+ fingerprints[path] = createHash7("sha256").update(await readFile39(candidate)).digest("hex");
27607
27862
  } catch {
27608
27863
  fingerprints[path] = "missing";
27609
27864
  }
@@ -27626,7 +27881,7 @@ function parseLedger(content) {
27626
27881
  return DeliveryLedgerSchema.parse($parse(match[1]));
27627
27882
  }
27628
27883
  async function loadDeliveryLedger(projectRoot, id) {
27629
- return parseLedger(await readFile38(ledgerPath(projectRoot, id), "utf-8"));
27884
+ return parseLedger(await readFile39(ledgerPath(projectRoot, id), "utf-8"));
27630
27885
  }
27631
27886
  async function createDeliveryLedger(projectRoot, input) {
27632
27887
  const request = DeliveryLedgerRequestSchema.parse(input);
@@ -27647,6 +27902,7 @@ async function createDeliveryLedger(projectRoot, input) {
27647
27902
  nextStep: request.nextStep,
27648
27903
  memoryTopicKey: `delivery:${request.id}`,
27649
27904
  workspace: await fingerprintWorkspace(projectRoot, request.taskCard.targetFiles),
27905
+ technicalPlanPath: request.technicalPlanPath,
27650
27906
  createdAt: now,
27651
27907
  updatedAt: now
27652
27908
  });
@@ -27678,6 +27934,28 @@ var exports_odd_command = {};
27678
27934
  __export(exports_odd_command, {
27679
27935
  oddCommand: () => oddCommand
27680
27936
  });
27937
+ async function reconcileForResume(projectRoot, id) {
27938
+ const result = await reconcileDeliveryLedger(projectRoot, id);
27939
+ const memory = await loadMemoryIndex(projectRoot);
27940
+ const { rddCommand: rddCommand2 } = await Promise.resolve().then(() => (init_rdd_command(), exports_rdd_command));
27941
+ const rdd = await rddCommand2({
27942
+ subcommand: "status",
27943
+ projectRoot,
27944
+ taskId: result.ledger.taskCard.id
27945
+ });
27946
+ const receipts = rdd.data.receipts ?? [];
27947
+ const staleReceipt = receipts.some((receipt) => receipt.valid === false);
27948
+ const changedPaths = staleReceipt ? [...result.changedPaths, "RDD receipt"].sort() : result.changedPaths;
27949
+ return {
27950
+ ...result,
27951
+ changedPaths,
27952
+ resume: resumeDecision(changedPaths),
27953
+ status: changedPaths.length === 0 ? "ready" : "conflict",
27954
+ rdd,
27955
+ memory: memory.success ? "available" : "unavailable",
27956
+ memoryPointers: memory.success ? memory.data.pointers.filter((pointer) => pointer.topic_key === result.ledger.memoryTopicKey) : []
27957
+ };
27958
+ }
27681
27959
  async function oddCommand(options) {
27682
27960
  if (options.subcommand === "create") {
27683
27961
  let input = options.input;
@@ -27711,28 +27989,25 @@ async function oddCommand(options) {
27711
27989
  return { code: 0, data: { success: true, ledger: await loadDeliveryLedger(options.projectRoot, options.id) } };
27712
27990
  }
27713
27991
  if (options.subcommand === "reconcile") {
27714
- const result = await reconcileDeliveryLedger(options.projectRoot, options.id);
27715
- const memory = await loadMemoryIndex(options.projectRoot);
27716
- const { rddCommand: rddCommand2 } = await Promise.resolve().then(() => (init_rdd_command(), exports_rdd_command));
27717
- const rdd = await rddCommand2({
27718
- subcommand: "status",
27719
- projectRoot: options.projectRoot,
27720
- taskId: result.ledger.taskCard.id
27721
- });
27722
- const receipts = rdd.data.receipts ?? [];
27723
- const staleReceipt = receipts.some((receipt) => receipt.valid === false);
27724
- const changedPaths = staleReceipt ? [...result.changedPaths, "RDD receipt"].sort() : result.changedPaths;
27992
+ const result = await reconcileForResume(options.projectRoot, options.id);
27725
27993
  return {
27726
27994
  code: 0,
27727
27995
  data: {
27728
27996
  success: true,
27729
- status: changedPaths.length === 0 ? "ready" : "conflict",
27730
- resume: resumeDecision(changedPaths),
27731
- rdd,
27732
- memory: memory.success ? "available" : "unavailable",
27733
- memoryPointers: memory.success ? memory.data.pointers.filter((pointer) => pointer.topic_key === result.ledger.memoryTopicKey) : [],
27734
- ...result,
27735
- changedPaths
27997
+ ...result
27998
+ }
27999
+ };
28000
+ }
28001
+ if (options.subcommand === "handoff") {
28002
+ const result = await reconcileForResume(options.projectRoot, options.id);
28003
+ return {
28004
+ code: 0,
28005
+ data: {
28006
+ success: true,
28007
+ status: result.status,
28008
+ changedPaths: result.changedPaths,
28009
+ resume: result.resume,
28010
+ handoff: buildHandoffEnvelope(result.ledger, result.changedPaths)
27736
28011
  }
27737
28012
  };
27738
28013
  }
@@ -27744,6 +28019,7 @@ async function oddCommand(options) {
27744
28019
  var init_odd_command = __esm(() => {
27745
28020
  init_memory_index();
27746
28021
  init_context_assembly();
28022
+ init_handoff_envelope();
27747
28023
  init_task_card_validator();
27748
28024
  init_workflow_profile_loader();
27749
28025
  init_delivery_ledger();
@@ -27755,7 +28031,11 @@ function normalizeCommand(raw) {
27755
28031
  return raw.replace(/\s+/g, " ").trim();
27756
28032
  }
27757
28033
  function stripGitBinary(command) {
27758
- return command.replace(/(?:^|[\\/])git(?:\.exe)?\b/i, "git");
28034
+ const match = command.match(/^(?:&\s*)?("[^"]+"|'[^']+'|[^\s]+)([\s\S]*)$/);
28035
+ if (!match)
28036
+ return command;
28037
+ const binary = match[1].replace(/^["']|["']$/g, "").replace(/\\/g, "/").split("/").pop();
28038
+ return /^git(?:\.exe)?$/i.test(binary ?? "") ? `git${match[2]}` : command;
27759
28039
  }
27760
28040
  function evaluatePath(filePath) {
27761
28041
  if (SENSITIVE_PATH.test(filePath.replace(/\//g, "\\")) || SENSITIVE_PATH.test(filePath)) {
@@ -27764,9 +28044,44 @@ function evaluatePath(filePath) {
27764
28044
  return allow();
27765
28045
  }
27766
28046
  function evaluateCommand(rawCommand) {
27767
- const command = stripGitBinary(normalizeCommand(rawCommand));
28047
+ if (/(?:curl|wget)\b.+\|\s*(sh|bash|zsh|cmd)\b/i.test(normalizeCommand(rawCommand)))
28048
+ return deny(MSG_POLICY);
28049
+ const tokens = rawCommand.match(/"[^"]*"|'[^']*'|[^"';&|\r\n]+|[;&|\r\n]+/g) ?? [];
28050
+ const commands = [];
28051
+ let current = "";
28052
+ for (const token of tokens) {
28053
+ if (/^[;&|\r\n]+$/.test(token) && !(token === "&" && !current.trim())) {
28054
+ commands.push(current);
28055
+ current = "";
28056
+ } else {
28057
+ current += token;
28058
+ }
28059
+ }
28060
+ commands.push(current);
28061
+ let verdict = allow();
28062
+ for (const part of commands) {
28063
+ const result = evaluateSingleCommand(part);
28064
+ if (result.action === "deny")
28065
+ return result;
28066
+ if (result.action === "ask")
28067
+ verdict = result;
28068
+ }
28069
+ return verdict;
28070
+ }
28071
+ function evaluateSingleCommand(rawCommand) {
28072
+ let command = stripGitBinary(normalizeCommand(rawCommand));
27768
28073
  if (!command)
27769
28074
  return allow();
28075
+ if (/^git\s/i.test(command)) {
28076
+ const words = command.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
28077
+ let index = 1;
28078
+ while (words[index]?.startsWith("-")) {
28079
+ const option = words[index++];
28080
+ if (/^(?:-C|-c|--git-dir|--work-tree|--namespace)$/.test(option))
28081
+ index++;
28082
+ }
28083
+ command = ["git", ...words.slice(index)].join(" ");
28084
+ }
27770
28085
  if (SENSITIVE_PATH.test(command) && READ_LIKE.test(command)) {
27771
28086
  return deny(MSG_SECRET);
27772
28087
  }
@@ -27789,9 +28104,12 @@ function evaluateCommand(rawCommand) {
27789
28104
  }
27790
28105
  if (/^rm\s+-rf\s+(\*|[\\/])/i.test(command))
27791
28106
  return deny(MSG_POLICY);
27792
- if (/^sudo\s+/i.test(command))
28107
+ const windowsTarget = /(?:^|\s)["']?(?:[a-z]:[\\/]|\\|\*)/i.test(command);
28108
+ if (windowsTarget && /^Remove-Item\b/i.test(command) && /\s-(?:Recurse|r)\b/i.test(command) && /\s-(?:Force|f)\b/i.test(command))
27793
28109
  return deny(MSG_POLICY);
27794
- if (/(?:curl|wget)\b.+\|\s*(sh|bash|zsh|cmd)\b/i.test(command))
28110
+ if (windowsTarget && /^(?:rd|rmdir|del|erase)\b/i.test(command) && /\s\/s\b/i.test(command))
28111
+ return deny(MSG_POLICY);
28112
+ if (/^sudo\s+/i.test(command))
27795
28113
  return deny(MSG_POLICY);
27796
28114
  if (/^chmod\s+777\b/i.test(command))
27797
28115
  return deny(MSG_POLICY);
@@ -27818,13 +28136,16 @@ function evaluatePreTool(input) {
27818
28136
  function formatHookOutput(verdict, format) {
27819
28137
  if (format === "agy") {
27820
28138
  if (verdict.action === "deny") {
27821
- return JSON.stringify({ decision: "deny", error: verdict.message });
28139
+ return JSON.stringify({ decision: "deny", reason: verdict.message });
27822
28140
  }
27823
28141
  if (verdict.action === "ask") {
27824
28142
  return JSON.stringify({ decision: "ask" });
27825
28143
  }
27826
28144
  return JSON.stringify({ decision: "allow" });
27827
28145
  }
28146
+ if (verdict.action === "ask") {
28147
+ return JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "ask" } });
28148
+ }
27828
28149
  return "";
27829
28150
  }
27830
28151
  function claudeExitCode(verdict) {
@@ -27835,11 +28156,24 @@ function parseAgyPayload(raw) {
27835
28156
  return {};
27836
28157
  try {
27837
28158
  const parsed = JSON.parse(raw);
27838
- const args = parsed.arguments ?? {};
28159
+ const args = parsed.toolCall?.args ?? parsed.arguments ?? {};
28160
+ return {
28161
+ toolName: typeof (parsed.toolCall?.name ?? parsed.toolName) === "string" ? parsed.toolCall?.name ?? parsed.toolName : undefined,
28162
+ command: typeof args.CommandLine === "string" ? args.CommandLine : undefined,
28163
+ filePath: typeof args.TargetFile === "string" ? args.TargetFile : typeof args.AbsolutePath === "string" ? args.AbsolutePath : undefined
28164
+ };
28165
+ } catch {
28166
+ return {};
28167
+ }
28168
+ }
28169
+ function parseClaudePayload(raw) {
28170
+ try {
28171
+ const parsed = JSON.parse(raw);
28172
+ const input = parsed.tool_input ?? {};
27839
28173
  return {
27840
- toolName: parsed.toolName,
27841
- command: args.CommandLine,
27842
- filePath: args.TargetFile ?? args.AbsolutePath
28174
+ toolName: typeof parsed.tool_name === "string" ? parsed.tool_name : undefined,
28175
+ command: typeof input.command === "string" ? input.command : undefined,
28176
+ filePath: typeof input.file_path === "string" ? input.file_path : undefined
27843
28177
  };
27844
28178
  } catch {
27845
28179
  return {};
@@ -27890,7 +28224,7 @@ function isDestructiveRestore(command) {
27890
28224
  }
27891
28225
  var SENSITIVE_PATH, READ_LIKE, MSG_SECRET = "Bloqueado: intento de leer archivos sensibles", MSG_AUTHORITY = "Bloqueado: El agente no tiene autoridad sobre este comando.", MSG_POLICY = "Bloqueado: Command violated security policy";
27892
28226
  var init_hook_runner = __esm(() => {
27893
- SENSITIVE_PATH = /(?:\.env\b|secrets[/\\]|id_rsa|\.pem\b|\.key\b|(?:^|[/\\])\.ssh(?:[/\\]|$)|(?:^|[/\\])\.aws(?:[/\\]|$)|(?:^|[/\\])\.kube(?:[/\\]|$))/i;
28227
+ SENSITIVE_PATH = /(?:\.env(?!\.example(?:$|[/\\]))\b|secrets[/\\]|id_rsa|\.pem\b|\.key\b|(?:^|[/\\])\.ssh(?:[/\\]|$)|(?:^|[/\\])\.aws(?:[/\\]|$)|(?:^|[/\\])\.kube(?:[/\\]|$))/i;
27894
28228
  READ_LIKE = /(?:^|[\s;&|])(cat|less|more|head|tail|type|Get-Content|cp|copy|mv|move|scp)\b/i;
27895
28229
  });
27896
28230
 
@@ -27901,14 +28235,17 @@ __export(exports_hook_command, {
27901
28235
  readStdinText: () => readStdinText
27902
28236
  });
27903
28237
  import { spawnSync } from "node:child_process";
27904
- import { existsSync as existsSync8 } from "node:fs";
27905
- import { resolve as resolve38 } from "node:path";
28238
+ import { existsSync as existsSync8, readFileSync as readFileSync5 } from "node:fs";
28239
+ import { createRequire as createRequire2 } from "node:module";
28240
+ import { dirname as dirname13, resolve as resolve38 } from "node:path";
27906
28241
  function detectFormat(explicit, stdinText) {
27907
28242
  if (explicit)
27908
28243
  return explicit;
27909
- const trimmed = stdinText.trim();
27910
- if (trimmed.startsWith("{"))
27911
- return "agy";
28244
+ try {
28245
+ const payload = JSON.parse(stdinText);
28246
+ if (payload && (("toolCall" in payload) || ("toolName" in payload) || ("workspacePaths" in payload)))
28247
+ return "agy";
28248
+ } catch {}
27912
28249
  return "claude";
27913
28250
  }
27914
28251
  async function readStdinText() {
@@ -27920,11 +28257,11 @@ async function readStdinText() {
27920
28257
  }
27921
28258
  return Buffer.concat(chunks).toString("utf8");
27922
28259
  }
27923
- function resolvePreToolInput(options, stdinText) {
27924
- const fromStdin = parseAgyPayload(stdinText);
28260
+ function resolvePreToolInput(options, stdinText, format) {
28261
+ const fromStdin = format === "agy" ? parseAgyPayload(stdinText) : parseClaudePayload(stdinText);
27925
28262
  return {
27926
- command: options.command ?? process.env.CLAUDE_TOOL_INPUT_COMMAND ?? fromStdin.command,
27927
- filePath: options.filePath ?? process.env.CLAUDE_TOOL_INPUT_FILE_PATH ?? fromStdin.filePath,
28263
+ command: options.command ?? (process.env.CLAUDE_TOOL_INPUT_COMMAND || undefined) ?? fromStdin.command,
28264
+ filePath: options.filePath ?? (process.env.CLAUDE_TOOL_INPUT_FILE_PATH || undefined) ?? fromStdin.filePath,
27928
28265
  toolName: fromStdin.toolName
27929
28266
  };
27930
28267
  }
@@ -27951,15 +28288,15 @@ async function hookCommand(options) {
27951
28288
  return { code: 0, data: { success: true, command: "hook session-start" } };
27952
28289
  }
27953
28290
  if (event === "post-tool") {
27954
- const filePath = options.filePath ?? process.env.CLAUDE_TOOL_INPUT_FILE_PATH ?? parseAgyPayload(stdinText).filePath;
27955
- formatWrittenFile(filePath);
28291
+ const filePath = resolvePreToolInput(options, stdinText, format).filePath;
28292
+ formatWrittenFile(filePath, options.projectRoot);
27956
28293
  if (format === "agy") {
27957
28294
  process.stdout.write(`{}
27958
28295
  `);
27959
28296
  }
27960
28297
  return { code: 0, data: { success: true, command: "hook post-tool" } };
27961
28298
  }
27962
- const input = resolvePreToolInput(options, stdinText);
28299
+ const input = resolvePreToolInput(options, stdinText, format);
27963
28300
  const verdict = evaluatePreTool(input);
27964
28301
  if (format === "agy") {
27965
28302
  process.stdout.write(`${formatHookOutput(verdict, "agy")}
@@ -27973,23 +28310,40 @@ async function hookCommand(options) {
27973
28310
  process.stderr.write(`${verdict.message}
27974
28311
  `);
27975
28312
  }
28313
+ const output = formatHookOutput(verdict, "claude");
28314
+ if (output)
28315
+ process.stdout.write(`${output}
28316
+ `);
27976
28317
  return {
27977
28318
  code: claudeExitCode(verdict),
27978
28319
  data: { success: verdict.action !== "deny", command: "hook pre-tool", verdict }
27979
28320
  };
27980
28321
  }
27981
- function formatWrittenFile(filePath) {
27982
- if (!filePath || !existsSync8(filePath))
28322
+ function formatWrittenFile(filePath, projectRoot) {
28323
+ if (!filePath)
28324
+ return;
28325
+ filePath = resolve38(projectRoot, filePath);
28326
+ if (!existsSync8(filePath))
27983
28327
  return;
27984
28328
  const prettier = /\.(ts|tsx|js|jsx|mjs|cjs|json|jsonc|md|mdx|css|scss|html|astro|ya?ml)$/i;
27985
28329
  const eslint = /\.(ts|tsx|js|jsx|mjs|cjs)$/i;
27986
28330
  const python = /\.py$/i;
27987
- const opts = { stdio: "ignore", windowsHide: true };
28331
+ const opts = { cwd: projectRoot, stdio: "ignore", windowsHide: true, timeout: 5000 };
28332
+ const require2 = createRequire2(resolve38(projectRoot, "package.json"));
28333
+ function runNodeTool(name, args) {
28334
+ try {
28335
+ const manifestPath = require2.resolve(`${name}/package.json`);
28336
+ const manifest = JSON.parse(readFileSync5(manifestPath, "utf8"));
28337
+ const bin = typeof manifest.bin === "string" ? manifest.bin : manifest.bin?.[name];
28338
+ if (typeof bin === "string")
28339
+ spawnSync(process.execPath, [resolve38(dirname13(manifestPath), bin), ...args], opts);
28340
+ } catch {}
28341
+ }
27988
28342
  if (prettier.test(filePath)) {
27989
- spawnSync("npx", ["--no-install", "prettier", "--write", filePath], opts);
28343
+ runNodeTool("prettier", ["--write", filePath]);
27990
28344
  }
27991
28345
  if (eslint.test(filePath)) {
27992
- spawnSync("npx", ["--no-install", "eslint", "--fix", filePath], opts);
28346
+ runNodeTool("eslint", ["--fix", filePath]);
27993
28347
  }
27994
28348
  if (python.test(filePath)) {
27995
28349
  spawnSync("ruff", ["format", filePath], opts);
@@ -28396,7 +28750,7 @@ function ccGainToScorecardImpact(result) {
28396
28750
  }
28397
28751
 
28398
28752
  // src/core/evaluation/scorecard-signals.ts
28399
- import { execFileSync as execFileSync3 } from "node:child_process";
28753
+ import { execFileSync as execFileSync2 } from "node:child_process";
28400
28754
  function parseScopeFiles(scope) {
28401
28755
  return scope.split(/[,;]/).map((entry) => entry.trim().replace(/[.;:]$/, "").replace(/^`|`$/g, "")).filter(Boolean);
28402
28756
  }
@@ -28426,7 +28780,7 @@ function collectScorecardSignals(projectRoot, scopeFiles, extra) {
28426
28780
  }
28427
28781
  let diff = "";
28428
28782
  try {
28429
- diff = execFileSync3("git", ["diff", "HEAD"], {
28783
+ diff = execFileSync2("git", ["diff", "HEAD"], {
28430
28784
  cwd: projectRoot,
28431
28785
  encoding: "utf-8",
28432
28786
  stdio: ["ignore", "pipe", "ignore"]
@@ -28521,8 +28875,8 @@ var init_scorecard_signals = __esm(() => {
28521
28875
 
28522
28876
  // src/core/evaluation/harness-experiment.ts
28523
28877
  import { existsSync as existsSync9 } from "node:fs";
28524
- import { cp, mkdir as mkdir25, readFile as readFile39, readdir as readdir14, writeFile as writeFile24 } from "node:fs/promises";
28525
- import { dirname as dirname13, join as join26, resolve as resolve39 } from "node:path";
28878
+ import { cp, mkdir as mkdir25, readFile as readFile40, readdir as readdir14, writeFile as writeFile24 } from "node:fs/promises";
28879
+ import { dirname as dirname14, join as join26, resolve as resolve39 } from "node:path";
28526
28880
  function experimentDir(projectRoot, experimentId) {
28527
28881
  return resolve39(projectRoot, EVAL_DIR2, EXPERIMENTS_DIR, experimentId);
28528
28882
  }
@@ -28538,7 +28892,7 @@ async function loadHarnessSuite(projectRoot, suiteId, suitePath) {
28538
28892
  if (!existsSync9(path))
28539
28893
  continue;
28540
28894
  try {
28541
- const suite = HarnessSuiteSchema.parse($parse(await readFile39(path, "utf-8")));
28895
+ const suite = HarnessSuiteSchema.parse($parse(await readFile40(path, "utf-8")));
28542
28896
  return ok({ ...suite, path });
28543
28897
  } catch (e) {
28544
28898
  return err(e instanceof Error ? e : new Error(String(e)));
@@ -28548,7 +28902,7 @@ async function loadHarnessSuite(projectRoot, suiteId, suitePath) {
28548
28902
  }
28549
28903
  async function loadExperiment(projectRoot, experimentId) {
28550
28904
  try {
28551
- const raw = await readFile39(experimentYamlPath(projectRoot, experimentId), "utf-8");
28905
+ const raw = await readFile40(experimentYamlPath(projectRoot, experimentId), "utf-8");
28552
28906
  return ok(HarnessExperimentSchema.parse($parse(raw)));
28553
28907
  } catch (e) {
28554
28908
  if (e.code === "ENOENT") {
@@ -28637,7 +28991,7 @@ function generateExperimentId(suiteId, now = new Date) {
28637
28991
  return `abl-${date}-${suiteId}-${rand}`;
28638
28992
  }
28639
28993
  async function copyFixture(suitePath, fixtureDir, dest) {
28640
- const source = fixtureDir ? resolve39(dirname13(suitePath), fixtureDir) : join26(dirname13(suitePath), "fixture");
28994
+ const source = fixtureDir ? resolve39(dirname14(suitePath), fixtureDir) : join26(dirname14(suitePath), "fixture");
28641
28995
  await mkdir25(dest, { recursive: true });
28642
28996
  if (existsSync9(source)) {
28643
28997
  await cp(source, dest, { recursive: true });
@@ -28823,11 +29177,11 @@ var init_suite_run = __esm(() => {
28823
29177
  });
28824
29178
 
28825
29179
  // src/core/evaluation/execution-profile.ts
28826
- import { readFile as readFile40 } from "node:fs/promises";
29180
+ import { readFile as readFile41 } from "node:fs/promises";
28827
29181
  import { resolve as resolve41 } from "node:path";
28828
29182
  async function loadExecutionProfile(projectRoot, defaultTarget = "opencode") {
28829
29183
  try {
28830
- const content = await readFile40(resolve41(projectRoot, PROFILE_FILE), "utf-8");
29184
+ const content = await readFile41(resolve41(projectRoot, PROFILE_FILE), "utf-8");
28831
29185
  const data = $parse(content);
28832
29186
  return ExecutionProfileSchema.parse(data);
28833
29187
  } catch {
@@ -28907,7 +29261,7 @@ var init_execution_profile = __esm(() => {
28907
29261
  });
28908
29262
 
28909
29263
  // src/core/evaluation/prompt-diff.ts
28910
- import { readFile as readFile41, readdir as readdir15 } from "node:fs/promises";
29264
+ import { readFile as readFile42, readdir as readdir15 } from "node:fs/promises";
28911
29265
  import { join as join27, resolve as resolve42 } from "node:path";
28912
29266
  function normalizeVersion(v) {
28913
29267
  return v.startsWith("v") ? v : `v${v}`;
@@ -28967,7 +29321,7 @@ async function diffPromptVersions(fromVersion, toVersion, options = {}) {
28967
29321
  const f = join27(root, fromV);
28968
29322
  const t = join27(root, toV);
28969
29323
  try {
28970
- await readFile41(join27(f, "orchestrator.md"), "utf-8");
29324
+ await readFile42(join27(f, "orchestrator.md"), "utf-8");
28971
29325
  fromDir = f;
28972
29326
  toDir = t;
28973
29327
  break;
@@ -28978,7 +29332,7 @@ async function diffPromptVersions(fromVersion, toVersion, options = {}) {
28978
29332
  toDir = join27(ROOT_PRESETS_DIR, "opencode", "prompts", toV);
28979
29333
  } else {
28980
29334
  try {
28981
- await readFile41(join27(toDir, "orchestrator.md"), "utf-8");
29335
+ await readFile42(join27(toDir, "orchestrator.md"), "utf-8");
28982
29336
  } catch {
28983
29337
  toDir = join27(ROOT_PRESETS_DIR, "opencode", "prompts", toV);
28984
29338
  }
@@ -28998,12 +29352,12 @@ async function diffPromptVersions(fromVersion, toVersion, options = {}) {
28998
29352
  let oldText = "";
28999
29353
  let newText = "";
29000
29354
  try {
29001
- oldText = await readFile41(join27(fromDir, name), "utf-8");
29355
+ oldText = await readFile42(join27(fromDir, name), "utf-8");
29002
29356
  } catch {
29003
29357
  oldText = "";
29004
29358
  }
29005
29359
  try {
29006
- newText = await readFile41(join27(toDir, name), "utf-8");
29360
+ newText = await readFile42(join27(toDir, name), "utf-8");
29007
29361
  } catch {
29008
29362
  newText = "";
29009
29363
  }
@@ -29042,13 +29396,13 @@ var init_prompt_diff = __esm(() => {
29042
29396
  });
29043
29397
 
29044
29398
  // src/core/evaluation/regression-checklist.ts
29045
- import { execFileSync as execFileSync4 } from "node:child_process";
29046
- import { mkdir as mkdir26, readFile as readFile42, writeFile as writeFile25 } from "node:fs/promises";
29047
- import { dirname as dirname14, resolve as resolve43 } from "node:path";
29399
+ import { execFileSync as execFileSync3 } from "node:child_process";
29400
+ import { mkdir as mkdir26, readFile as readFile43, writeFile as writeFile25 } from "node:fs/promises";
29401
+ import { dirname as dirname15, resolve as resolve43 } from "node:path";
29048
29402
  async function loadRegressionChecklist() {
29049
29403
  try {
29050
29404
  const path = resolve43(ROOT_PRESETS_DIR, "templates", "regression-checklist.yml");
29051
- const content = await readFile42(path, "utf-8");
29405
+ const content = await readFile43(path, "utf-8");
29052
29406
  const data = $parse(content);
29053
29407
  return data.checks ?? [];
29054
29408
  } catch {
@@ -29062,7 +29416,7 @@ function runCheckCommand(cmd, cwd) {
29062
29416
  if (!file)
29063
29417
  return { passed: false, message: "Empty command" };
29064
29418
  try {
29065
- execFileSync4(file, args, {
29419
+ execFileSync3(file, args, {
29066
29420
  cwd,
29067
29421
  encoding: "utf-8",
29068
29422
  stdio: ["ignore", "pipe", "pipe"],
@@ -29104,7 +29458,7 @@ async function runRegressionChecklist(projectRoot, options = {}) {
29104
29458
  timestamp: new Date().toISOString()
29105
29459
  };
29106
29460
  const outPath = resolve43(projectRoot, ".codeconductor/evaluation/regression-last.json");
29107
- await mkdir26(dirname14(outPath), { recursive: true });
29461
+ await mkdir26(dirname15(outPath), { recursive: true });
29108
29462
  await writeFile25(outPath, JSON.stringify(report, null, 2), "utf-8");
29109
29463
  return ok(report);
29110
29464
  } catch (e) {
@@ -29378,7 +29732,7 @@ var exports_scorecard_command = {};
29378
29732
  __export(exports_scorecard_command, {
29379
29733
  scorecardCommand: () => scorecardCommand
29380
29734
  });
29381
- import { readFile as readFile43 } from "node:fs/promises";
29735
+ import { readFile as readFile44 } from "node:fs/promises";
29382
29736
  import { resolve as resolve46 } from "node:path";
29383
29737
  function contractVersion() {
29384
29738
  return package_default.version;
@@ -29669,7 +30023,7 @@ async function handleCompareModels(projectRoot, options) {
29669
30023
  const markdown = generateModelComparisonMarkdown(outcomes, options.modelsFilter);
29670
30024
  const templatePath = resolve46(projectRoot, ".codeconductor/evaluation/model-comparison-last.md");
29671
30025
  try {
29672
- await readFile43(templatePath);
30026
+ await readFile44(templatePath);
29673
30027
  } catch {}
29674
30028
  const { writeFile: writeFile28, mkdir: mkdir28 } = await import("node:fs/promises");
29675
30029
  await mkdir28(resolve46(projectRoot, ".codeconductor/evaluation"), { recursive: true });
@@ -31516,6 +31870,7 @@ var COMMANDS = [
31516
31870
  { name: "setup", group: "getting-started", summary: "Configure and install CodeConductor.", usage: ["cc-codeconductor setup [--target <target>] [--locale en|es] [--yes] [--dry-run]"] },
31517
31871
  { name: "detect", group: "getting-started", summary: "Inspect the project stack and recommended presets.", usage: ["cc-codeconductor detect"] },
31518
31872
  { name: "init", group: "getting-started", summary: "Initialize low-level CodeConductor configuration.", usage: ["cc-codeconductor init [--locale en|es]"] },
31873
+ { name: "usage", group: "getting-started", summary: "Show installation examples for presets, council, and LSP.", usage: ["cc-codeconductor usage"] },
31519
31874
  { name: "install", group: "getting-started", summary: "Install harness components.", usage: ["cc-codeconductor install preset --target <target>"], options: ["--target opencode|claude|codex|gemini|cursor|agy|pi|all"], subcommands: [
31520
31875
  { name: "preset", group: "getting-started", summary: "Install agents, prompts, skills, and commands.", usage: ["cc-codeconductor install preset --target <target> [--locale en|es]"] },
31521
31876
  { name: "council", group: "getting-started", summary: "Install generated council files.", usage: ["cc-codeconductor install council --target <target>"] },
@@ -31535,6 +31890,7 @@ var COMMANDS = [
31535
31890
  { name: "ingest", group: "workflow", summary: "Ingest repository knowledge into the product graph.", usage: ["cc-codeconductor ingest"] },
31536
31891
  { name: "product", group: "workflow", summary: "Explore the product graph and memory.", usage: ["cc-codeconductor product <subcommand>"] },
31537
31892
  { name: "ccep", group: "workflow", summary: "Run CCEP contract workflows.", usage: ["cc-codeconductor ccep <subcommand>"] },
31893
+ { name: "odd", group: "workflow", summary: "Create, resume, and hand off tracked delivery work.", usage: ["cc-codeconductor odd <create|read|reconcile|handoff>"] },
31538
31894
  { name: "openspec", group: "workflow", summary: "Run the OpenSpec delivery loop.", usage: ["cc-codeconductor openspec <subcommand>"] },
31539
31895
  { name: "scorecard", group: "workflow", summary: "Record and aggregate outcomes.", usage: ["cc-codeconductor scorecard <subcommand>"] },
31540
31896
  { name: "orchestrate", group: "workflow", summary: "Run goal execution orchestration.", usage: ["cc-codeconductor orchestrate <subcommand>"] },
@@ -31629,11 +31985,102 @@ _arguments '1:command:(${names})' '--target[target]:target:(${targets})'`;
31629
31985
  return null;
31630
31986
  }
31631
31987
 
31988
+ // src/cli/usage-guide.ts
31989
+ init_cli_bin();
31990
+ var RECOMMENDED_FLOW = [
31991
+ "cc-codeconductor setup --target claude",
31992
+ "cc-codeconductor setup --target claude --locale es --yes",
31993
+ "cc-codeconductor setup --target claude --dry-run"
31994
+ ];
31995
+ var PRIMITIVES = [
31996
+ "cc-codeconductor detect",
31997
+ "cc-codeconductor init --locale es",
31998
+ "cc-codeconductor install preset --target claude",
31999
+ "cc-codeconductor install council --target claude",
32000
+ "cc-codeconductor install lsp --target claude"
32001
+ ];
32002
+ var PRESET_EXAMPLES = [
32003
+ "cc-codeconductor install preset --target claude",
32004
+ "cc-codeconductor install preset --target claude --global",
32005
+ "cc-codeconductor install preset --target claude --force",
32006
+ "cc-codeconductor install preset --target claude --dry-run",
32007
+ "cc-codeconductor install preset --target all",
32008
+ "cc-codeconductor install preset --target claude --locale es",
32009
+ "cc-codeconductor install preset --target claude --global --force"
32010
+ ];
32011
+ var COUNCIL_EXAMPLES = [
32012
+ "cc-codeconductor install council --target claude",
32013
+ "cc-codeconductor install council --target claude --global",
32014
+ "cc-codeconductor install council --target claude --force",
32015
+ "cc-codeconductor install council --target claude --dry-run",
32016
+ "cc-codeconductor install council --target all",
32017
+ "cc-codeconductor install council --target claude --global --force"
32018
+ ];
32019
+ var LSP_EXAMPLES = [
32020
+ "cc-codeconductor install lsp --target claude --lang typescript,python",
32021
+ "cc-codeconductor install lsp --target all --lang typescript,python",
32022
+ "cc-codeconductor install lsp --target claude --global",
32023
+ "cc-codeconductor install lsp --target claude --dry-run"
32024
+ ];
32025
+ var AFTER_INSTALL_EXAMPLES = [
32026
+ ["cc-codeconductor doctor", "validates the installation"],
32027
+ ["cc-codeconductor status", "shows what is installed"],
32028
+ ["cc-codeconductor update --dry-run", "previews reconciliation of managed files after upgrading"],
32029
+ ["cc-codeconductor update --force", "applies reconciliation of managed files after upgrading"],
32030
+ ["cc-codeconductor migrate --dry-run", "previews repair of orphaned artifacts a reinstall cannot fix"]
32031
+ ];
32032
+ function renderExamples(lines, bin) {
32033
+ return lines.map((line) => ` ${formatUsage(line, bin)}`);
32034
+ }
32035
+ function renderAnnotatedExamples(entries, bin) {
32036
+ return entries.map(([line, description]) => ` ${formatUsage(line, bin)} — ${description}`);
32037
+ }
32038
+ function renderUsageGuide(bin = resolveCliBin()) {
32039
+ return [
32040
+ "CodeConductor — installation usage guide",
32041
+ "",
32042
+ "Recommended flow (one command, installs and configures everything):",
32043
+ ...renderExamples(RECOMMENDED_FLOW, bin),
32044
+ "",
32045
+ "Fine-grained primitives (for scripting or partial installs):",
32046
+ ...renderExamples(PRIMITIVES, bin),
32047
+ "",
32048
+ "Shared flags:",
32049
+ " --target opencode|claude|codex|gemini|cursor|agy|pi|all (default: opencode)",
32050
+ " --global Install to the home directory (~/.claude, ~/.opencode, etc.) instead of the project",
32051
+ " --force Allow overwriting existing files",
32052
+ " --dry-run Show what would happen without writing files",
32053
+ " --locale Instruction language for agent files: en (default) | es — install preset only",
32054
+ " --lang Comma-separated languages to install LSP servers for — install lsp only",
32055
+ " --output Output mode: human (default) | json",
32056
+ "",
32057
+ "install preset — agents, prompts, skills, and commands:",
32058
+ ...renderExamples(PRESET_EXAMPLES, bin),
32059
+ "",
32060
+ "install council — generated council spec files:",
32061
+ ...renderExamples(COUNCIL_EXAMPLES, bin),
32062
+ "",
32063
+ "install lsp — language servers:",
32064
+ ...renderExamples(LSP_EXAMPLES, bin),
32065
+ " Dedicated integration config is generated for: opencode, claude, codex, gemini, cursor, agy.",
32066
+ " pi installs the language servers but does not get a dedicated integration config file.",
32067
+ "",
32068
+ "After installing, verify and keep in sync:",
32069
+ ...renderAnnotatedExamples(AFTER_INSTALL_EXAMPLES, bin),
32070
+ "",
32071
+ "More detail (GitHub only, not bundled in the npm package):",
32072
+ " docs/usage-cli.md Detailed CLI reference, incl. per-target generated files",
32073
+ " docs/getting-started/ Installation, first project, and update walkthroughs"
32074
+ ].join(`
32075
+ `);
32076
+ }
32077
+
31632
32078
  // src/cli/router.ts
31633
32079
  var OutputModeSchema = exports_external.enum(["json", "human"]);
31634
32080
  var SeoFormatSchema = exports_external.enum(["cli", "json", "markdown"]);
31635
32081
  var SeoFailOnSchema = exports_external.enum(["error", "warning", "never"]);
31636
32082
  var NonNegativeIntegerSchema = exports_external.coerce.number().int().nonnegative();
32083
+ var BOOLEAN_OPTIONS = new Set(["record-telemetry"]);
31637
32084
  function parseArgs(args) {
31638
32085
  const commandAcceptsOutputPath = args[0] === "seo";
31639
32086
  const flags = {
@@ -31704,6 +32151,9 @@ function parseArgs(args) {
31704
32151
  } else if (value !== undefined) {
31705
32152
  options[key] = value;
31706
32153
  consumed.add(i);
32154
+ } else if (BOOLEAN_OPTIONS.has(key)) {
32155
+ options[key] = true;
32156
+ consumed.add(i);
31707
32157
  } else if (remaining[i + 1] && !remaining[i + 1].startsWith("-")) {
31708
32158
  options[key] = remaining[++i];
31709
32159
  consumed.add(i - 1);
@@ -31909,6 +32359,8 @@ async function routeCommand(args, projectRoot) {
31909
32359
  case "docs":
31910
32360
  case "man":
31911
32361
  return { code: 0, data: { success: true, command, output: renderDocs(subcommand) } };
32362
+ case "usage":
32363
+ return { code: 0, data: { success: true, command: "usage", output: renderUsageGuide() } };
31912
32364
  case "completion": {
31913
32365
  const output = renderCompletion(subcommand ?? "");
31914
32366
  return output ? { code: 0, data: { success: true, command: "completion", output } } : cliContractError("completion", ["Expected shell: bash, zsh, fish, or powershell."]);
@@ -32059,6 +32511,11 @@ async function routeCommand(args, projectRoot) {
32059
32511
  input: options.input,
32060
32512
  contextPath: options.context || options.contextPath,
32061
32513
  promptVersion: options["prompt-version"] || options.promptVersion,
32514
+ view: options.view,
32515
+ recordTelemetry: options["record-telemetry"] === true || options["record-telemetry"] === "true",
32516
+ executionId: options["execution-id"],
32517
+ taskId: options["task-id"],
32518
+ contextStrategy: options["context-strategy"],
32062
32519
  config: options.config,
32063
32520
  rest: validateRest
32064
32521
  });
@@ -32099,7 +32556,7 @@ async function routeCommand(args, projectRoot) {
32099
32556
  });
32100
32557
  }
32101
32558
  case "odd": {
32102
- const validSubs = ["create", "read", "reconcile"];
32559
+ const validSubs = ["create", "read", "reconcile", "handoff"];
32103
32560
  if (!subcommand || !validSubs.includes(subcommand)) {
32104
32561
  return unknownSubcommand(command, subcommand ?? "", validSubs);
32105
32562
  }