agileflow 4.0.0-alpha.2 → 4.0.0-alpha.21

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 (372) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/content/plugins/accessibility/plugin.yaml +14 -0
  3. package/content/plugins/accessibility/skills/agileflow-accessibility/SKILL.md +392 -0
  4. package/content/plugins/accessibility/skills/agileflow-accessibility/references/aria-patterns.md +528 -0
  5. package/content/plugins/accessibility/skills/agileflow-accessibility/references/testing-checklist.md +457 -0
  6. package/content/plugins/accessibility/skills/agileflow-accessibility/references/wcag-guide.md +683 -0
  7. package/content/plugins/accessibility/skills/agileflow-accessibility/workflows/audit-page.md +310 -0
  8. package/content/plugins/accessibility/skills/agileflow-accessibility/workflows/implement-accessible-component.md +479 -0
  9. package/content/plugins/ads/agents/ads-audit-budget.md +185 -0
  10. package/content/plugins/ads/agents/ads-audit-compliance.md +171 -0
  11. package/content/plugins/ads/agents/ads-audit-creative.md +168 -0
  12. package/content/plugins/ads/agents/ads-audit-google.md +227 -0
  13. package/content/plugins/ads/agents/ads-audit-meta.md +184 -0
  14. package/content/plugins/ads/agents/ads-audit-tracking.md +205 -0
  15. package/content/plugins/ads/agents/ads-consensus.md +410 -0
  16. package/content/plugins/ads/agents/ads-generate.md +152 -0
  17. package/content/plugins/ads/agents/ads-performance-tracker.md +212 -0
  18. package/content/plugins/ads/plugin.yaml +23 -4
  19. package/content/plugins/ads/skills/agileflow-ads/SKILL.md +218 -0
  20. package/content/plugins/ads/skills/agileflow-ads/references/ad-copy-formula-guide.md +131 -0
  21. package/content/plugins/ads/skills/agileflow-ads/references/audience-targeting-guide.md +137 -0
  22. package/content/plugins/ads/skills/agileflow-ads/references/bid-strategy-guide.md +115 -0
  23. package/content/plugins/ads/skills/agileflow-ads/references/platform-benchmarks.md +100 -0
  24. package/content/plugins/ads/skills/agileflow-ads/workflows/audit.md +118 -0
  25. package/content/plugins/ads/skills/agileflow-ads/workflows/generate.md +84 -0
  26. package/content/plugins/audit/agents/a11y-analyzer-aria.md +173 -0
  27. package/content/plugins/audit/agents/a11y-analyzer-forms.md +173 -0
  28. package/content/plugins/audit/agents/a11y-analyzer-keyboard.md +183 -0
  29. package/content/plugins/audit/agents/a11y-analyzer-semantic.md +169 -0
  30. package/content/plugins/audit/agents/a11y-analyzer-visual.md +172 -0
  31. package/content/plugins/audit/agents/a11y-consensus.md +249 -0
  32. package/content/plugins/audit/agents/accessibility.md +558 -0
  33. package/content/plugins/audit/agents/api-quality-analyzer-conventions.md +156 -0
  34. package/content/plugins/audit/agents/api-quality-analyzer-docs.md +184 -0
  35. package/content/plugins/audit/agents/api-quality-analyzer-errors.md +191 -0
  36. package/content/plugins/audit/agents/api-quality-analyzer-pagination.md +179 -0
  37. package/content/plugins/audit/agents/api-quality-analyzer-versioning.md +150 -0
  38. package/content/plugins/audit/agents/api-quality-consensus.md +217 -0
  39. package/content/plugins/audit/agents/api-validator.md +191 -0
  40. package/content/plugins/audit/agents/arch-analyzer-circular.md +156 -0
  41. package/content/plugins/audit/agents/arch-analyzer-complexity.md +193 -0
  42. package/content/plugins/audit/agents/arch-analyzer-coupling.md +152 -0
  43. package/content/plugins/audit/agents/arch-analyzer-layering.md +160 -0
  44. package/content/plugins/audit/agents/arch-analyzer-patterns.md +210 -0
  45. package/content/plugins/audit/agents/arch-consensus.md +228 -0
  46. package/content/plugins/audit/agents/browser-qa.md +342 -0
  47. package/content/plugins/audit/agents/code-reviewer.md +298 -0
  48. package/content/plugins/audit/agents/completeness-analyzer-api.md +199 -0
  49. package/content/plugins/audit/agents/completeness-analyzer-conditional.md +211 -0
  50. package/content/plugins/audit/agents/completeness-analyzer-handlers.md +166 -0
  51. package/content/plugins/audit/agents/completeness-analyzer-imports.md +165 -0
  52. package/content/plugins/audit/agents/completeness-analyzer-routes.md +190 -0
  53. package/content/plugins/audit/agents/completeness-analyzer-state.md +196 -0
  54. package/content/plugins/audit/agents/completeness-analyzer-stubs.md +206 -0
  55. package/content/plugins/audit/agents/completeness-consensus.md +295 -0
  56. package/content/plugins/audit/agents/error-analyzer.md +213 -0
  57. package/content/plugins/audit/agents/flow-analyzer-authorization.md +182 -0
  58. package/content/plugins/audit/agents/flow-analyzer-discovery.md +174 -0
  59. package/content/plugins/audit/agents/flow-analyzer-errors.md +186 -0
  60. package/content/plugins/audit/agents/flow-analyzer-feedback.md +185 -0
  61. package/content/plugins/audit/agents/flow-analyzer-navigation.md +177 -0
  62. package/content/plugins/audit/agents/flow-analyzer-persistence.md +193 -0
  63. package/content/plugins/audit/agents/flow-analyzer-wiring.md +169 -0
  64. package/content/plugins/audit/agents/flow-consensus.md +237 -0
  65. package/content/plugins/audit/agents/legal-analyzer-a11y.md +114 -0
  66. package/content/plugins/audit/agents/legal-analyzer-ai.md +121 -0
  67. package/content/plugins/audit/agents/legal-analyzer-consumer.md +114 -0
  68. package/content/plugins/audit/agents/legal-analyzer-content.md +117 -0
  69. package/content/plugins/audit/agents/legal-analyzer-international.md +119 -0
  70. package/content/plugins/audit/agents/legal-analyzer-licensing.md +119 -0
  71. package/content/plugins/audit/agents/legal-analyzer-privacy.md +112 -0
  72. package/content/plugins/audit/agents/legal-analyzer-security.md +116 -0
  73. package/content/plugins/audit/agents/legal-analyzer-terms.md +115 -0
  74. package/content/plugins/audit/agents/legal-consensus.md +250 -0
  75. package/content/plugins/audit/agents/logic-analyzer-edge.md +179 -0
  76. package/content/plugins/audit/agents/logic-analyzer-flow.md +264 -0
  77. package/content/plugins/audit/agents/logic-analyzer-invariant.md +215 -0
  78. package/content/plugins/audit/agents/logic-analyzer-race.md +280 -0
  79. package/content/plugins/audit/agents/logic-analyzer-type.md +227 -0
  80. package/content/plugins/audit/agents/logic-consensus.md +259 -0
  81. package/content/plugins/audit/agents/perf-analyzer-assets.md +182 -0
  82. package/content/plugins/audit/agents/perf-analyzer-bundle.md +173 -0
  83. package/content/plugins/audit/agents/perf-analyzer-caching.md +170 -0
  84. package/content/plugins/audit/agents/perf-analyzer-compute.md +173 -0
  85. package/content/plugins/audit/agents/perf-analyzer-memory.md +193 -0
  86. package/content/plugins/audit/agents/perf-analyzer-network.md +165 -0
  87. package/content/plugins/audit/agents/perf-analyzer-queries.md +162 -0
  88. package/content/plugins/audit/agents/perf-analyzer-rendering.md +168 -0
  89. package/content/plugins/audit/agents/perf-consensus.md +287 -0
  90. package/content/plugins/audit/agents/qa.md +820 -0
  91. package/content/plugins/audit/agents/quality-analyzer-comments.md +159 -0
  92. package/content/plugins/audit/agents/quality-analyzer-duplication.md +184 -0
  93. package/content/plugins/audit/agents/quality-analyzer-naming.md +160 -0
  94. package/content/plugins/audit/agents/quality-consensus.md +241 -0
  95. package/content/plugins/audit/agents/schema-validator.md +473 -0
  96. package/content/plugins/audit/agents/security-analyzer-api.md +210 -0
  97. package/content/plugins/audit/agents/security-analyzer-auth.md +169 -0
  98. package/content/plugins/audit/agents/security-analyzer-authz.md +180 -0
  99. package/content/plugins/audit/agents/security-analyzer-deps.md +153 -0
  100. package/content/plugins/audit/agents/security-analyzer-infra.md +184 -0
  101. package/content/plugins/audit/agents/security-analyzer-injection.md +155 -0
  102. package/content/plugins/audit/agents/security-analyzer-input.md +201 -0
  103. package/content/plugins/audit/agents/security-analyzer-secrets.md +183 -0
  104. package/content/plugins/audit/agents/security-consensus.md +283 -0
  105. package/content/plugins/audit/agents/test-analyzer-assertions.md +188 -0
  106. package/content/plugins/audit/agents/test-analyzer-coverage.md +189 -0
  107. package/content/plugins/audit/agents/test-analyzer-fragility.md +193 -0
  108. package/content/plugins/audit/agents/test-analyzer-integration.md +161 -0
  109. package/content/plugins/audit/agents/test-analyzer-maintenance.md +180 -0
  110. package/content/plugins/audit/agents/test-analyzer-mocking.md +188 -0
  111. package/content/plugins/audit/agents/test-analyzer-patterns.md +196 -0
  112. package/content/plugins/audit/agents/test-analyzer-structure.md +184 -0
  113. package/content/plugins/audit/agents/test-consensus.md +301 -0
  114. package/content/plugins/audit/agents/testing.md +561 -0
  115. package/content/plugins/audit/agents/ui-validator.md +344 -0
  116. package/content/plugins/audit/plugin.yaml +186 -5
  117. package/content/plugins/audit/skills/agileflow-audit/SKILL.md +113 -0
  118. package/content/plugins/audit/skills/agileflow-audit/references/audit-depth-guide.md +151 -0
  119. package/content/plugins/audit/skills/agileflow-audit/references/dependency-risk-guide.md +139 -0
  120. package/content/plugins/audit/skills/agileflow-audit/references/owasp-top10.md +120 -0
  121. package/content/plugins/audit/skills/agileflow-audit/references/performance-budget-guide.md +143 -0
  122. package/content/plugins/audit/skills/agileflow-audit/references/wcag-criteria.md +117 -0
  123. package/content/plugins/audit/skills/agileflow-audit/workflows/run-audit.md +52 -0
  124. package/content/plugins/audit/skills/agileflow-audit/workflows/tdd.md +66 -0
  125. package/content/plugins/core/agents/adr-writer.md +521 -0
  126. package/content/plugins/core/agents/epic-planner.md +520 -0
  127. package/content/plugins/core/agents/mentor.md +709 -0
  128. package/content/plugins/core/agents/orchestrator.md +776 -0
  129. package/content/plugins/core/agents/team-coordinator.md +334 -0
  130. package/content/plugins/core/agents/team-lead.md +181 -0
  131. package/content/plugins/core/agents/workspace-orchestrator.md +146 -0
  132. package/content/plugins/core/hooks/context-loader.js +31 -4
  133. package/content/plugins/core/hooks/damage-control-bash.js +10 -2
  134. package/content/plugins/core/hooks/damage-control-edit.js +4 -1
  135. package/content/plugins/core/hooks/damage-control-patterns.yaml +1 -1
  136. package/content/plugins/core/hooks/damage-control-write.js +4 -1
  137. package/content/plugins/core/hooks/{pre-compact-state.js → post-compact-state.js} +25 -8
  138. package/content/plugins/core/hooks/preferences-injector.js +352 -0
  139. package/content/plugins/core/plugin.yaml +24 -28
  140. package/content/plugins/core/skills/agileflow-adr/SKILL.md +34 -8
  141. package/content/plugins/core/skills/agileflow-adr/references/madr-format-guide.md +86 -0
  142. package/content/plugins/core/skills/agileflow-adr/workflows/write-adr.md +57 -0
  143. package/content/plugins/core/skills/agileflow-babysit-mentor/SKILL.md +94 -27
  144. package/content/plugins/core/skills/agileflow-babysit-mentor/references/mentor-decision-guide.md +81 -0
  145. package/content/plugins/core/skills/agileflow-babysit-mentor/workflows/mentor-session.md +79 -0
  146. package/content/plugins/core/skills/agileflow-epic-planner/SKILL.md +37 -7
  147. package/content/plugins/core/skills/agileflow-epic-planner/references/epic-sizing-guide.md +81 -0
  148. package/content/plugins/core/skills/agileflow-epic-planner/workflows/plan-epic.md +55 -0
  149. package/content/plugins/core/skills/agileflow-status-updater/SKILL.md +36 -20
  150. package/content/plugins/core/skills/agileflow-status-updater/references/status-transitions.md +89 -0
  151. package/content/plugins/core/skills/agileflow-status-updater/workflows/update-status.md +56 -0
  152. package/content/plugins/core/skills/agileflow-story-writer/SKILL.md +39 -114
  153. package/content/plugins/core/skills/agileflow-story-writer/references/estimation-reference.md +36 -0
  154. package/content/plugins/core/skills/agileflow-story-writer/references/story-template.md +92 -0
  155. package/content/plugins/core/skills/agileflow-story-writer/workflows/write-story.md +138 -0
  156. package/content/plugins/council/agents/council-advocate.md +223 -0
  157. package/content/plugins/council/agents/council-analyst.md +278 -0
  158. package/content/plugins/council/agents/council-compounder.md +204 -0
  159. package/content/plugins/council/agents/council-contrarian.md +217 -0
  160. package/content/plugins/council/agents/council-moonshot.md +217 -0
  161. package/content/plugins/council/agents/council-optimist.md +185 -0
  162. package/content/plugins/council/agents/council-revenue.md +200 -0
  163. package/content/plugins/council/agents/council-technical.md +218 -0
  164. package/content/plugins/council/agents/multi-expert.md +334 -0
  165. package/content/plugins/council/plugin.yaml +23 -4
  166. package/content/plugins/council/skills/agileflow-council/SKILL.md +102 -0
  167. package/content/plugins/council/skills/agileflow-council/references/decision-log-template.md +109 -0
  168. package/content/plugins/council/skills/agileflow-council/references/perspective-guide.md +104 -0
  169. package/content/plugins/council/skills/agileflow-council/references/when-to-convene-guide.md +112 -0
  170. package/content/plugins/council/skills/agileflow-council/workflows/convene.md +73 -0
  171. package/content/plugins/council/skills/agileflow-council/workflows/multi-expert.md +75 -0
  172. package/content/plugins/database/plugin.yaml +14 -0
  173. package/content/plugins/database/skills/agileflow-database/SKILL.md +284 -0
  174. package/content/plugins/database/skills/agileflow-database/references/indexing-guide.md +313 -0
  175. package/content/plugins/database/skills/agileflow-database/references/migration-guide.md +328 -0
  176. package/content/plugins/database/skills/agileflow-database/references/schema-design-guide.md +467 -0
  177. package/content/plugins/database/skills/agileflow-database/workflows/design-schema.md +213 -0
  178. package/content/plugins/database/skills/agileflow-database/workflows/optimize-query.md +253 -0
  179. package/content/plugins/debugging/plugin.yaml +14 -0
  180. package/content/plugins/debugging/skills/agileflow-debug/SKILL.md +236 -0
  181. package/content/plugins/debugging/skills/agileflow-debug/references/common-patterns.md +350 -0
  182. package/content/plugins/debugging/skills/agileflow-debug/references/debugging-strategies.md +328 -0
  183. package/content/plugins/debugging/skills/agileflow-debug/workflows/debug-issue.md +187 -0
  184. package/content/plugins/debugging/skills/agileflow-debug/workflows/reproduce-bug.md +194 -0
  185. package/content/plugins/delivery/agents/ci.md +547 -0
  186. package/content/plugins/delivery/agents/devops.md +789 -0
  187. package/content/plugins/delivery/plugin.yaml +19 -0
  188. package/content/plugins/delivery/skills/agileflow-delivery/SKILL.md +111 -0
  189. package/content/plugins/delivery/skills/agileflow-delivery/references/changelog-format-guide.md +133 -0
  190. package/content/plugins/delivery/skills/agileflow-delivery/references/ci-pipeline-guide.md +158 -0
  191. package/content/plugins/delivery/skills/agileflow-delivery/references/pr-checklist-guide.md +133 -0
  192. package/content/plugins/delivery/skills/agileflow-delivery/references/release-checklist.md +142 -0
  193. package/content/plugins/delivery/skills/agileflow-delivery/workflows/changelog.md +72 -0
  194. package/content/plugins/delivery/skills/agileflow-delivery/workflows/deploy.md +74 -0
  195. package/content/plugins/delivery/skills/agileflow-delivery/workflows/pr.md +75 -0
  196. package/content/plugins/docs/agents/documentation.md +544 -0
  197. package/content/plugins/docs/agents/readme-updater.md +640 -0
  198. package/content/plugins/docs/plugin.yaml +19 -0
  199. package/content/plugins/docs/skills/agileflow-docs/SKILL.md +106 -0
  200. package/content/plugins/docs/skills/agileflow-docs/references/api-doc-template.md +167 -0
  201. package/content/plugins/docs/skills/agileflow-docs/references/doc-types-guide.md +141 -0
  202. package/content/plugins/docs/skills/agileflow-docs/references/readme-template.md +156 -0
  203. package/content/plugins/docs/skills/agileflow-docs/workflows/readme-sync.md +57 -0
  204. package/content/plugins/docs/skills/agileflow-docs/workflows/sync.md +64 -0
  205. package/content/plugins/engineering/agents/api.md +718 -0
  206. package/content/plugins/engineering/agents/codebase-query.md +285 -0
  207. package/content/plugins/engineering/agents/compliance.md +559 -0
  208. package/content/plugins/engineering/agents/database.md +644 -0
  209. package/content/plugins/engineering/agents/integrations.md +644 -0
  210. package/content/plugins/engineering/agents/mobile.md +552 -0
  211. package/content/plugins/engineering/agents/monitoring.md +585 -0
  212. package/content/plugins/engineering/agents/performance.md +529 -0
  213. package/content/plugins/engineering/agents/refactor.md +592 -0
  214. package/content/plugins/engineering/agents/security.md +524 -0
  215. package/content/plugins/engineering/agents/ui.md +1336 -0
  216. package/content/plugins/engineering/plugin.yaml +37 -0
  217. package/content/plugins/engineering/skills/agileflow-engineering/SKILL.md +127 -0
  218. package/content/plugins/engineering/skills/agileflow-engineering/references/code-review-guide.md +126 -0
  219. package/content/plugins/engineering/skills/agileflow-engineering/references/domain-routing-guide.md +89 -0
  220. package/content/plugins/engineering/skills/agileflow-engineering/references/refactoring-guide.md +136 -0
  221. package/content/plugins/engineering/skills/agileflow-engineering/workflows/diagnose.md +63 -0
  222. package/content/plugins/engineering/skills/agileflow-engineering/workflows/impact.md +60 -0
  223. package/content/plugins/ideation/agents/brainstorm-analyzer-features.md +179 -0
  224. package/content/plugins/ideation/agents/brainstorm-analyzer-growth.md +169 -0
  225. package/content/plugins/ideation/agents/brainstorm-analyzer-integration.md +181 -0
  226. package/content/plugins/ideation/agents/brainstorm-analyzer-market.md +150 -0
  227. package/content/plugins/ideation/agents/brainstorm-analyzer-ux.md +180 -0
  228. package/content/plugins/ideation/agents/brainstorm-consensus.md +245 -0
  229. package/content/plugins/ideation/agents/design.md +568 -0
  230. package/content/plugins/ideation/agents/product.md +582 -0
  231. package/content/plugins/ideation/plugin.yaml +31 -0
  232. package/content/plugins/ideation/skills/agileflow-ideation/SKILL.md +109 -0
  233. package/content/plugins/ideation/skills/agileflow-ideation/references/brainstorm-techniques.md +138 -0
  234. package/content/plugins/ideation/skills/agileflow-ideation/references/competitive-analysis-template.md +148 -0
  235. package/content/plugins/ideation/skills/agileflow-ideation/references/feature-prioritization-guide.md +147 -0
  236. package/content/plugins/ideation/skills/agileflow-ideation/references/user-story-patterns.md +152 -0
  237. package/content/plugins/ideation/skills/agileflow-ideation/workflows/features.md +65 -0
  238. package/content/plugins/ideation/skills/agileflow-ideation/workflows/ideate.md +54 -0
  239. package/content/plugins/migration/agents/datamigration.md +757 -0
  240. package/content/plugins/migration/plugin.yaml +17 -0
  241. package/content/plugins/migration/skills/agileflow-migration/SKILL.md +106 -0
  242. package/content/plugins/migration/skills/agileflow-migration/references/data-validation-checklist.md +154 -0
  243. package/content/plugins/migration/skills/agileflow-migration/references/migration-patterns.md +209 -0
  244. package/content/plugins/migration/skills/agileflow-migration/references/rollback-playbook.md +171 -0
  245. package/content/plugins/migration/skills/agileflow-migration/references/version-compatibility-matrix.md +155 -0
  246. package/content/plugins/migration/skills/agileflow-migration/workflows/plan.md +73 -0
  247. package/content/plugins/migration/skills/agileflow-migration/workflows/validate.md +71 -0
  248. package/content/plugins/performance/plugin.yaml +14 -0
  249. package/content/plugins/performance/skills/agileflow-performance/SKILL.md +224 -0
  250. package/content/plugins/performance/skills/agileflow-performance/references/optimization-patterns.md +554 -0
  251. package/content/plugins/performance/skills/agileflow-performance/references/profiling-guide.md +383 -0
  252. package/content/plugins/performance/skills/agileflow-performance/references/web-vitals-guide.md +360 -0
  253. package/content/plugins/performance/skills/agileflow-performance/workflows/improve-web-vitals.md +344 -0
  254. package/content/plugins/performance/skills/agileflow-performance/workflows/profile-and-fix.md +254 -0
  255. package/content/plugins/planning/agents/analytics.md +670 -0
  256. package/content/plugins/planning/agents/rlm-subcore.md +215 -0
  257. package/content/plugins/planning/plugin.yaml +19 -0
  258. package/content/plugins/planning/skills/agileflow-planning/SKILL.md +111 -0
  259. package/content/plugins/planning/skills/agileflow-planning/references/estimation-guide.md +114 -0
  260. package/content/plugins/planning/skills/agileflow-planning/references/rpi-workflow.md +119 -0
  261. package/content/plugins/planning/skills/agileflow-planning/references/sprint-planning-guide.md +145 -0
  262. package/content/plugins/planning/skills/agileflow-planning/workflows/impact.md +63 -0
  263. package/content/plugins/planning/skills/agileflow-planning/workflows/rpi.md +104 -0
  264. package/content/plugins/psychology/plugin.yaml +14 -0
  265. package/content/plugins/psychology/skills/agileflow-retention/SKILL.md +252 -0
  266. package/content/plugins/psychology/skills/agileflow-retention/references/competitor-analysis.md +240 -0
  267. package/content/plugins/psychology/skills/agileflow-retention/references/psychology-models.md +349 -0
  268. package/content/plugins/psychology/skills/agileflow-retention/references/retention-patterns.md +279 -0
  269. package/content/plugins/psychology/skills/agileflow-retention/workflows/design-retention-feature.md +287 -0
  270. package/content/plugins/psychology/skills/agileflow-retention/workflows/retention-audit.md +259 -0
  271. package/content/plugins/refactoring/plugin.yaml +14 -0
  272. package/content/plugins/refactoring/skills/agileflow-refactor/SKILL.md +235 -0
  273. package/content/plugins/refactoring/skills/agileflow-refactor/references/refactoring-patterns.md +405 -0
  274. package/content/plugins/refactoring/skills/agileflow-refactor/references/safety-checks.md +177 -0
  275. package/content/plugins/refactoring/skills/agileflow-refactor/workflows/extract-module.md +226 -0
  276. package/content/plugins/refactoring/skills/agileflow-refactor/workflows/safe-refactor.md +169 -0
  277. package/content/plugins/research/agents/research.md +503 -0
  278. package/content/plugins/research/plugin.yaml +17 -0
  279. package/content/plugins/research/skills/agileflow-research/SKILL.md +110 -0
  280. package/content/plugins/research/skills/agileflow-research/references/knowledge-decay-guide.md +121 -0
  281. package/content/plugins/research/skills/agileflow-research/references/research-prompt-guide.md +141 -0
  282. package/content/plugins/research/skills/agileflow-research/references/synthesis-template.md +154 -0
  283. package/content/plugins/research/skills/agileflow-research/workflows/analyze.md +60 -0
  284. package/content/plugins/research/skills/agileflow-research/workflows/ask.md +64 -0
  285. package/content/plugins/research/skills/agileflow-research/workflows/import.md +66 -0
  286. package/content/plugins/research/skills/agileflow-research/workflows/synthesize.md +66 -0
  287. package/content/plugins/reviews/plugin.yaml +14 -0
  288. package/content/plugins/reviews/skills/agileflow-pr-reviewer/SKILL.md +241 -0
  289. package/content/plugins/reviews/skills/agileflow-pr-reviewer/references/review-checklist.md +200 -0
  290. package/content/plugins/reviews/skills/agileflow-pr-reviewer/references/security-patterns.md +328 -0
  291. package/content/plugins/reviews/skills/agileflow-pr-reviewer/workflows/review-pr.md +153 -0
  292. package/content/plugins/reviews/skills/agileflow-pr-reviewer/workflows/security-review.md +177 -0
  293. package/content/plugins/seo/agents/seo-analyzer-content.md +169 -0
  294. package/content/plugins/seo/agents/seo-analyzer-images.md +198 -0
  295. package/content/plugins/seo/agents/seo-analyzer-performance.md +217 -0
  296. package/content/plugins/seo/agents/seo-analyzer-schema.md +184 -0
  297. package/content/plugins/seo/agents/seo-analyzer-sitemap.md +177 -0
  298. package/content/plugins/seo/agents/seo-analyzer-technical.md +151 -0
  299. package/content/plugins/seo/agents/seo-consensus.md +304 -0
  300. package/content/plugins/seo/plugin.yaml +19 -4
  301. package/content/plugins/seo/skills/agileflow-seo/SKILL.md +188 -0
  302. package/content/plugins/seo/skills/agileflow-seo/references/cwv-thresholds.md +110 -0
  303. package/content/plugins/seo/skills/agileflow-seo/references/eeat-framework.md +144 -0
  304. package/content/plugins/seo/skills/agileflow-seo/references/keyword-research-guide.md +125 -0
  305. package/content/plugins/seo/skills/agileflow-seo/references/schema-types.md +139 -0
  306. package/content/plugins/seo/skills/agileflow-seo/references/technical-seo-checklist.md +139 -0
  307. package/content/plugins/seo/skills/agileflow-seo/workflows/audit.md +98 -0
  308. package/content/plugins/seo/skills/agileflow-seo/workflows/page.md +118 -0
  309. package/content/plugins/testing/plugin.yaml +16 -0
  310. package/content/plugins/testing/skills/agileflow-test-writer/SKILL.md +260 -0
  311. package/content/plugins/testing/skills/agileflow-test-writer/references/coverage-targets.md +239 -0
  312. package/content/plugins/testing/skills/agileflow-test-writer/references/test-patterns.md +420 -0
  313. package/content/plugins/testing/skills/agileflow-test-writer/workflows/add-coverage.md +154 -0
  314. package/content/plugins/testing/skills/agileflow-test-writer/workflows/write-tests-from-ac.md +225 -0
  315. package/package.json +2 -2
  316. package/src/cli/commands/doctor.js +818 -30
  317. package/src/cli/commands/hook.js +17 -14
  318. package/src/cli/commands/launch.js +1454 -0
  319. package/src/cli/commands/learn.js +149 -0
  320. package/src/cli/commands/plugins.js +113 -0
  321. package/src/cli/commands/setup.js +455 -110
  322. package/src/cli/commands/skills.js +324 -0
  323. package/src/cli/commands/status.js +8 -10
  324. package/src/cli/commands/update.js +76 -15
  325. package/src/cli/index.js +90 -26
  326. package/src/cli/wizard/babysit-mode-picker.js +192 -0
  327. package/src/cli/wizard/behaviors-picker.js +208 -54
  328. package/src/cli/wizard/ide-picker.js +40 -28
  329. package/src/cli/wizard/install-scope-picker.js +57 -0
  330. package/src/cli/wizard/launch-alias-picker.js +50 -0
  331. package/src/cli/wizard/launch-cli-picker.js +129 -0
  332. package/src/cli/wizard/launch-tmux-picker.js +133 -0
  333. package/src/cli/wizard/learnings-picker.js +40 -0
  334. package/src/cli/wizard/plugin-picker.js +47 -16
  335. package/src/lib/brand.js +116 -0
  336. package/src/lib/errors.js +120 -0
  337. package/src/lib/path-check.js +39 -0
  338. package/src/runtime/config/defaults.js +22 -17
  339. package/src/runtime/config/loader.js +77 -8
  340. package/src/runtime/config/schema.json +43 -16
  341. package/src/runtime/config/writer.js +3 -1
  342. package/src/runtime/ide/babysit-skill.js +202 -0
  343. package/src/runtime/ide/capabilities.js +84 -29
  344. package/src/runtime/ide/claude-code-content.js +177 -0
  345. package/src/runtime/ide/claude-code-settings.js +67 -29
  346. package/src/runtime/ide/claude-code-skills.js +47 -32
  347. package/src/runtime/ide/codex-config.js +295 -0
  348. package/src/runtime/installer/install.js +252 -24
  349. package/src/runtime/launch/alias-installer.js +191 -0
  350. package/src/runtime/launch/cli-resume.js +244 -0
  351. package/src/runtime/launch/closed-windows.js +338 -0
  352. package/src/runtime/launch/defaults.js +66 -0
  353. package/src/runtime/launch/detect-clis.js +69 -0
  354. package/src/runtime/launch/doctor.js +464 -0
  355. package/src/runtime/launch/exec-wrapper.js +114 -0
  356. package/src/runtime/launch/parallel-session.js +247 -0
  357. package/src/runtime/launch/prefs.js +211 -0
  358. package/src/runtime/launch/project-prefs.js +234 -0
  359. package/src/runtime/launch/resolve-cli.js +56 -0
  360. package/src/runtime/launch/restore.js +152 -0
  361. package/src/runtime/launch/schema.json +75 -0
  362. package/src/runtime/launch/session-lifecycle.js +313 -0
  363. package/src/runtime/launch/session-registry.js +401 -0
  364. package/src/runtime/launch/spawn.js +103 -0
  365. package/src/runtime/launch/tabs.js +350 -0
  366. package/src/runtime/launch/tmux.js +764 -0
  367. package/src/runtime/launch/worktree.js +260 -0
  368. package/src/runtime/plugins/registry.js +16 -11
  369. package/src/runtime/plugins/validator.js +57 -43
  370. package/src/runtime/skills/learnings.js +308 -0
  371. package/content/plugins/core/hooks/babysit-mentor-injector.js +0 -55
  372. package/src/cli/wizard/personalization.js +0 -64
@@ -0,0 +1,401 @@
1
+ /**
2
+ * Cross-reboot session registry for `agileflow launch`.
3
+ *
4
+ * Lives at `~/.agileflow/launch-sessions.json`. Tracks every tmux
5
+ * session we create so we can resurrect them after the tmux server
6
+ * dies (machine reboot, manual `tmux kill-server`, etc.). For each
7
+ * session we keep:
8
+ * - name — the tmux session name (claude-myproject, ...)
9
+ * - cli — the AI CLI it wraps (claude, codex, ...)
10
+ * - cwd — directory the session was created in (or the
11
+ * worktree path for Alt+n sessions)
12
+ * - uuid — last-known conversation UUID for resume (captured
13
+ * from claude's `~/.claude/projects/<dir>/*.jsonl`
14
+ * or codex's session index)
15
+ * - lastSeen — ISO timestamp of the last __exec invocation
16
+ * - worktree — optional `{path, branch, base}` from createWorktree
17
+ *
18
+ * Atomic writes via tmp+rename — same pattern the prefs file uses.
19
+ */
20
+ const fs = require("fs");
21
+ const os = require("os");
22
+ const path = require("path");
23
+
24
+ const FILENAME = "launch-sessions.json";
25
+ const LOCK_SUFFIX = ".lock";
26
+ const LOCK_STALE_MS = 5000;
27
+ const LOCK_MAX_ATTEMPTS = 100;
28
+ const LOCK_RETRY_MS = 10;
29
+
30
+ /**
31
+ * Busy-wait sleep for `ms` milliseconds. Used inside the lock-acquire
32
+ * retry loop to back off briefly between attempts without bringing in
33
+ * any async / Atomics machinery (the rest of the registry API is sync,
34
+ * so async sleep would force every caller through Promise plumbing).
35
+ *
36
+ * @param {number} ms
37
+ */
38
+ function busyWait(ms) {
39
+ const target = Date.now() + ms;
40
+ while (Date.now() < target) {
41
+ /* spin */
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Acquire an exclusive write lock on the registry via an O_EXCL lock
47
+ * file, run `fn`, then release. Used by recordSession / updateSession /
48
+ * forgetSession so two concurrent `__exec` processes finishing claude
49
+ * at the same time don't lose each other's UUID updates (read-modify-
50
+ * write race).
51
+ *
52
+ * @template T
53
+ * @param {string | undefined} home
54
+ * @param {() => T} fn
55
+ * @returns {T}
56
+ */
57
+ function withRegistryLock(home, fn) {
58
+ const lockFile = registryPath(home) + LOCK_SUFFIX;
59
+ fs.mkdirSync(path.dirname(lockFile), { recursive: true });
60
+
61
+ /** @type {number | null} */
62
+ let lockFd = null;
63
+ for (let attempt = 0; attempt < LOCK_MAX_ATTEMPTS; attempt++) {
64
+ try {
65
+ // `wx` is the Node.js shorthand for O_CREAT | O_EXCL — fails with
66
+ // EEXIST when someone else already holds the lock.
67
+ lockFd = fs.openSync(lockFile, "wx");
68
+ break;
69
+ } catch (err) {
70
+ if (!err || err.code !== "EEXIST") throw err;
71
+ // Stale-lock detection: if the lockfile's mtime is older than
72
+ // LOCK_STALE_MS the holder probably crashed without unlinking.
73
+ // Best-effort takeover. The race here is benign: even if two
74
+ // processes both decide it's stale, only one O_EXCL open will
75
+ // succeed and the other loops again.
76
+ try {
77
+ const stat = fs.statSync(lockFile);
78
+ if (Date.now() - (stat.mtimeMs || 0) > LOCK_STALE_MS) {
79
+ try {
80
+ fs.unlinkSync(lockFile);
81
+ } catch {
82
+ /* swallow */
83
+ }
84
+ continue;
85
+ }
86
+ } catch {
87
+ /* lockfile vanished between EEXIST and stat — loop and retry */
88
+ }
89
+ busyWait(LOCK_RETRY_MS);
90
+ }
91
+ }
92
+ if (lockFd === null) {
93
+ throw new Error(
94
+ `could not acquire launch-sessions registry lock after ${LOCK_MAX_ATTEMPTS} attempts`,
95
+ );
96
+ }
97
+
98
+ try {
99
+ return fn();
100
+ } finally {
101
+ try {
102
+ fs.closeSync(lockFd);
103
+ } catch {
104
+ /* swallow */
105
+ }
106
+ try {
107
+ fs.unlinkSync(lockFile);
108
+ } catch {
109
+ /* swallow */
110
+ }
111
+ }
112
+ }
113
+
114
+ /**
115
+ * @typedef {Object} WorktreeMeta
116
+ * @property {string} path
117
+ * @property {string} branch
118
+ * @property {string} base
119
+ *
120
+ * @typedef {Object} SessionEntry
121
+ * @property {string} name
122
+ * @property {string} cli
123
+ * @property {string} cwd
124
+ * @property {string | null} uuid - null until the first __exec capture
125
+ * @property {string} lastSeen - ISO timestamp
126
+ * @property {boolean} [pinned] - user-marked as "always keep";
127
+ * pinned entries skip prune and
128
+ * are pre-selected in the auto-
129
+ * restore picker
130
+ * @property {WorktreeMeta} [worktree]
131
+ *
132
+ * @typedef {Object} RegistryShape
133
+ * @property {1} version
134
+ * @property {SessionEntry[]} sessions
135
+ */
136
+
137
+ /**
138
+ * @param {string} [home]
139
+ * @returns {string}
140
+ */
141
+ function registryPath(home) {
142
+ return path.join(home || os.homedir(), ".agileflow", FILENAME);
143
+ }
144
+
145
+ /** @returns {RegistryShape} */
146
+ function emptyRegistry() {
147
+ return { version: 1, sessions: [] };
148
+ }
149
+
150
+ /**
151
+ * Read the registry from disk; returns an empty registry if the file
152
+ * is missing or malformed (corrupted files shouldn't break `launch`).
153
+ * Unknown extras are dropped. Invalid entries (missing required keys)
154
+ * are filtered out.
155
+ *
156
+ * @param {string} [home]
157
+ * @returns {RegistryShape}
158
+ */
159
+ function loadRegistry(home) {
160
+ const file = registryPath(home);
161
+ let raw;
162
+ try {
163
+ raw = fs.readFileSync(file, "utf8");
164
+ } catch (err) {
165
+ if (err && err.code === "ENOENT") return emptyRegistry();
166
+ // Permission / I/O errors should NOT silently become an empty
167
+ // registry — that would tell a subsequent write to wipe out the
168
+ // user's whole session list. Surface a warning on stderr (so it
169
+ // shows up next to other launch chatter) and still return empty so
170
+ // the rest of the launch flow keeps working.
171
+ // eslint-disable-next-line no-console
172
+ console.error(
173
+ `agileflow launch: could not read ${file} (${err && err.code ? err.code : err && err.message ? err.message : "I/O error"}). ` +
174
+ "Saved sessions may not appear in `agileflow launch restore` until the file is readable.",
175
+ );
176
+ return emptyRegistry();
177
+ }
178
+ let parsed;
179
+ try {
180
+ parsed = JSON.parse(raw);
181
+ } catch {
182
+ return emptyRegistry();
183
+ }
184
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
185
+ return emptyRegistry();
186
+ }
187
+ const sessions = Array.isArray(parsed.sessions) ? parsed.sessions : [];
188
+ /** @type {SessionEntry[]} */
189
+ const sane = [];
190
+ for (const s of sessions) {
191
+ if (!s || typeof s !== "object") continue;
192
+ if (typeof s.name !== "string" || !s.name) continue;
193
+ if (typeof s.cli !== "string" || !s.cli) continue;
194
+ if (typeof s.cwd !== "string" || !s.cwd) continue;
195
+ sane.push({
196
+ name: s.name,
197
+ cli: s.cli,
198
+ cwd: s.cwd,
199
+ uuid: typeof s.uuid === "string" ? s.uuid : null,
200
+ lastSeen: typeof s.lastSeen === "string" ? s.lastSeen : "",
201
+ pinned: s.pinned === true,
202
+ worktree:
203
+ s.worktree && typeof s.worktree === "object"
204
+ ? {
205
+ path: String(s.worktree.path || ""),
206
+ branch: String(s.worktree.branch || ""),
207
+ base: String(s.worktree.base || ""),
208
+ }
209
+ : undefined,
210
+ });
211
+ }
212
+ return { version: 1, sessions: sane };
213
+ }
214
+
215
+ /**
216
+ * Atomically write the registry to disk. Ensures `~/.agileflow/` exists.
217
+ *
218
+ * @param {RegistryShape} reg
219
+ * @param {string} [home]
220
+ * @returns {string}
221
+ */
222
+ function writeRegistry(reg, home) {
223
+ const file = registryPath(home);
224
+ const dir = path.dirname(file);
225
+ const tmp = path.join(dir, `.${FILENAME}.tmp-${process.pid}`);
226
+ const payload = {
227
+ version: 1,
228
+ sessions: reg.sessions,
229
+ };
230
+ const content = JSON.stringify(payload, null, 2) + "\n";
231
+ fs.mkdirSync(dir, { recursive: true });
232
+ try {
233
+ fs.writeFileSync(tmp, content, "utf8");
234
+ fs.renameSync(tmp, file);
235
+ } catch (err) {
236
+ try {
237
+ fs.unlinkSync(tmp);
238
+ } catch {
239
+ /* swallow */
240
+ }
241
+ throw err;
242
+ }
243
+ return file;
244
+ }
245
+
246
+ /**
247
+ * Record a newly-created session. If an entry with the same name
248
+ * exists, it's replaced — same name = same logical session being
249
+ * re-created, which is what the restore flow does.
250
+ *
251
+ * @param {Omit<SessionEntry, "lastSeen"> & { lastSeen?: string }} entry
252
+ * @param {string} [home]
253
+ */
254
+ /**
255
+ * Reject entries that loadRegistry would silently drop on the next
256
+ * read. Validates the same required-string fields the load-side filter
257
+ * checks, so writes and reads agree.
258
+ *
259
+ * @param {Partial<SessionEntry>} entry
260
+ */
261
+ function validateEntry(entry) {
262
+ if (!entry || typeof entry !== "object") {
263
+ throw new TypeError("recordSession: entry must be an object");
264
+ }
265
+ if (typeof entry.name !== "string" || !entry.name) {
266
+ throw new TypeError("recordSession: entry.name must be a non-empty string");
267
+ }
268
+ if (typeof entry.cli !== "string" || !entry.cli) {
269
+ throw new TypeError("recordSession: entry.cli must be a non-empty string");
270
+ }
271
+ if (typeof entry.cwd !== "string" || !entry.cwd) {
272
+ throw new TypeError("recordSession: entry.cwd must be a non-empty string");
273
+ }
274
+ }
275
+
276
+ function recordSession(entry, home) {
277
+ validateEntry(entry);
278
+ withRegistryLock(home, () => {
279
+ const reg = loadRegistry(home);
280
+ const filtered = reg.sessions.filter((s) => s.name !== entry.name);
281
+ // Preserve `pinned` across re-records (the restore flow re-records
282
+ // every session it brings back from disk; without this carry-over a
283
+ // restored pinned session would silently un-pin itself).
284
+ const previous = reg.sessions.find((s) => s.name === entry.name);
285
+ const pinned =
286
+ typeof entry.pinned === "boolean"
287
+ ? entry.pinned
288
+ : previous
289
+ ? previous.pinned === true
290
+ : false;
291
+ filtered.push({
292
+ name: entry.name,
293
+ cli: entry.cli,
294
+ cwd: entry.cwd,
295
+ uuid: entry.uuid || null,
296
+ lastSeen: entry.lastSeen || new Date().toISOString(),
297
+ pinned,
298
+ worktree: entry.worktree,
299
+ });
300
+ writeRegistry({ version: 1, sessions: filtered }, home);
301
+ });
302
+ }
303
+
304
+ /**
305
+ * Patch an existing entry by name. Used by the __exec wrapper after
306
+ * the CLI exits to refresh the UUID + lastSeen. No-op if the entry
307
+ * doesn't exist.
308
+ *
309
+ * @param {string} name
310
+ * @param {Partial<SessionEntry>} patch
311
+ * @param {string} [home]
312
+ * @returns {boolean} - true if a row was updated
313
+ */
314
+ function updateSession(name, patch, home) {
315
+ return withRegistryLock(home, () => {
316
+ // Re-read under the lock so concurrent __exec processes finishing
317
+ // claude at the same time don't lose each other's UUID updates.
318
+ // Without the lock: A loads, B loads, A writes, B writes → A's
319
+ // change clobbered.
320
+ const reg = loadRegistry(home);
321
+ let updated = false;
322
+ for (const s of reg.sessions) {
323
+ if (s.name === name) {
324
+ if (patch.uuid !== undefined) {
325
+ // Accept string-or-null only; coerce anything else to null
326
+ // to keep the on-disk shape consistent with what the load
327
+ // side validates.
328
+ s.uuid = typeof patch.uuid === "string" ? patch.uuid : null;
329
+ }
330
+ if (patch.lastSeen !== undefined) s.lastSeen = patch.lastSeen;
331
+ if (patch.cwd !== undefined) s.cwd = patch.cwd;
332
+ if (patch.worktree !== undefined) s.worktree = patch.worktree;
333
+ if (patch.pinned !== undefined) s.pinned = patch.pinned === true;
334
+ updated = true;
335
+ }
336
+ }
337
+ if (updated) writeRegistry(reg, home);
338
+ return updated;
339
+ });
340
+ }
341
+
342
+ /**
343
+ * Look up a single entry by name.
344
+ *
345
+ * @param {string} name
346
+ * @param {string} [home]
347
+ * @returns {SessionEntry | null}
348
+ */
349
+ function findSession(name, home) {
350
+ const reg = loadRegistry(home);
351
+ return reg.sessions.find((s) => s.name === name) || null;
352
+ }
353
+
354
+ /**
355
+ * Set the pinned flag on a session. Convenience wrapper around
356
+ * updateSession that exists so callers don't have to remember the patch
357
+ * shape. Returns true when the entry was found and updated, false when
358
+ * the name isn't in the registry.
359
+ *
360
+ * @param {string} name
361
+ * @param {boolean} pinned
362
+ * @param {string} [home]
363
+ * @returns {boolean}
364
+ */
365
+ function pinSession(name, pinned, home) {
366
+ return updateSession(name, { pinned: pinned === true }, home);
367
+ }
368
+
369
+ /**
370
+ * Drop an entry by name (e.g., user killed the worktree session and
371
+ * doesn't want it auto-restored next time).
372
+ *
373
+ * @param {string} name
374
+ * @param {string} [home]
375
+ * @returns {boolean}
376
+ */
377
+ function forgetSession(name, home) {
378
+ return withRegistryLock(home, () => {
379
+ const reg = loadRegistry(home);
380
+ const before = reg.sessions.length;
381
+ reg.sessions = reg.sessions.filter((s) => s.name !== name);
382
+ if (reg.sessions.length !== before) {
383
+ writeRegistry(reg, home);
384
+ return true;
385
+ }
386
+ return false;
387
+ });
388
+ }
389
+
390
+ module.exports = {
391
+ FILENAME,
392
+ registryPath,
393
+ emptyRegistry,
394
+ loadRegistry,
395
+ writeRegistry,
396
+ recordSession,
397
+ updateSession,
398
+ findSession,
399
+ forgetSession,
400
+ pinSession,
401
+ };
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Spawn an AI CLI as a foreground child process.
3
+ *
4
+ * stdio is inherited so the child takes over the terminal — same UX as
5
+ * if the user had typed the CLI's name directly. We resolve to an
6
+ * `{ exitCode, signal }` shape after the child closes so the caller can
7
+ * mirror the child's exit status with `process.exit(exitCode)`.
8
+ *
9
+ * `spawnImpl` is injectable so tests don't actually fork a process.
10
+ */
11
+ const child_process = require("child_process");
12
+
13
+ /**
14
+ * @typedef {Object} SpawnResult
15
+ * @property {number} exitCode - 0..255 on normal exit; 128+signo on signal exits
16
+ * @property {NodeJS.Signals | null} signal
17
+ */
18
+
19
+ /**
20
+ * Map a signal name to the conventional `128 + signo` exit code so
21
+ * callers can `process.exit(result.exitCode)` and propagate the
22
+ * termination cause to the parent shell. Falls back to 1 when the
23
+ * signal isn't in the well-known set.
24
+ *
25
+ * @param {NodeJS.Signals | null} signal
26
+ * @returns {number}
27
+ */
28
+ function signalToExitCode(signal) {
29
+ if (!signal) return 1;
30
+ const table = {
31
+ SIGHUP: 129,
32
+ SIGINT: 130,
33
+ SIGQUIT: 131,
34
+ SIGILL: 132,
35
+ SIGTRAP: 133,
36
+ SIGABRT: 134,
37
+ SIGBUS: 135,
38
+ SIGFPE: 136,
39
+ SIGKILL: 137,
40
+ SIGUSR1: 138,
41
+ SIGSEGV: 139,
42
+ SIGUSR2: 140,
43
+ SIGPIPE: 141,
44
+ SIGALRM: 142,
45
+ SIGTERM: 143,
46
+ };
47
+ return table[signal] || 1;
48
+ }
49
+
50
+ /**
51
+ * Spawn `bin` with `args` and wait for it to close. Returns the
52
+ * exit code the parent should propagate.
53
+ *
54
+ * @param {string} bin
55
+ * @param {string[]} [args]
56
+ * @param {{ spawn?: typeof child_process.spawn, cwd?: string, env?: NodeJS.ProcessEnv }} [options]
57
+ * @returns {Promise<SpawnResult>}
58
+ */
59
+ function runCli(bin, args = [], options = {}) {
60
+ const spawn = options.spawn || child_process.spawn;
61
+ return new Promise((resolve, reject) => {
62
+ // Node's child_process fires `error` then `close` on a failed spawn.
63
+ // Promises absorb the second settlement, but relying on that is
64
+ // implicit — an explicit guard keeps behavior obvious in tests and
65
+ // future Node versions.
66
+ let settled = false;
67
+ const finish = (fn, value) => {
68
+ if (settled) return;
69
+ settled = true;
70
+ fn(value);
71
+ };
72
+
73
+ let child;
74
+ try {
75
+ child = spawn(bin, args, {
76
+ stdio: "inherit",
77
+ cwd: options.cwd,
78
+ // `??` (not `||`) is the precise guard for "was env provided" —
79
+ // future callers passing a falsy-but-meaningful value (e.g., `0`
80
+ // or an empty string from a misconfigured wrapper) won't be
81
+ // silently coerced to process.env. Node treats `null` and
82
+ // `undefined` identically here, so both still default to
83
+ // process.env via spawn's own handling.
84
+ env: options.env ?? process.env,
85
+ });
86
+ } catch (err) {
87
+ finish(reject, err);
88
+ return;
89
+ }
90
+
91
+ child.on("error", (err) => {
92
+ finish(reject, err);
93
+ });
94
+
95
+ child.on("close", (code, signal) => {
96
+ const exitCode =
97
+ typeof code === "number" ? code : signalToExitCode(signal);
98
+ finish(resolve, { exitCode, signal });
99
+ });
100
+ });
101
+ }
102
+
103
+ module.exports = { runCli, signalToExitCode };