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,764 @@
1
+ /**
2
+ * tmux session orchestration for `agileflow launch`.
3
+ *
4
+ * Slice 2b: wrap the user's AI CLI in a per-cwd tmux session so
5
+ * detach/reattach work like the v3 `af` script. Multiple `agileflow
6
+ * launch` invocations from the same directory either reattach to an
7
+ * existing detached session, or — if one is already attached — spawn
8
+ * a numbered sibling (`<cli>-<dir>`, `<cli>-<dir>-2`, etc.).
9
+ *
10
+ * Everything that shells out to tmux goes through the `runner` parameter
11
+ * (default: child_process), so unit tests can drive every code path
12
+ * without a real tmux daemon. The orchestrator also threads the
13
+ * preferred status-bar position from launch-prefs.json onto the session
14
+ * after it's created.
15
+ *
16
+ * What this module deliberately does NOT do (deferred):
17
+ * - keybind preset files (.tmux.conf snippets for default/minimal/none)
18
+ * - freeze recovery, kill, list subcommands
19
+ * - multi-pane / worktree session creation (Alt+N etc.)
20
+ */
21
+ const path = require("path");
22
+ const child_process = require("child_process");
23
+
24
+ const { commandExists: realCommandExists } = require("../../lib/path-check.js");
25
+ const { signalToExitCode } = require("./spawn.js");
26
+ const { resolveAgileflowBin } = require("./alias-installer.js");
27
+ const tabs = require("./tabs.js");
28
+
29
+ /**
30
+ * @typedef {Object} TmuxLaunchResult
31
+ * @property {number} exitCode - exit code of the attach (or session creation on failure)
32
+ * @property {NodeJS.Signals | null} [signal] - signal that terminated the attach, when applicable
33
+ */
34
+
35
+ /**
36
+ * @typedef {Object} TmuxRunSyncResult
37
+ * @property {number} status - subprocess exit status (1 when spawn itself failed)
38
+ * @property {string} stdout
39
+ * @property {string} stderr
40
+ * @property {Error | null} error - non-null when spawn ITSELF failed (e.g., ENOENT)
41
+ */
42
+
43
+ /**
44
+ * @typedef {Object} TmuxRunner
45
+ * @property {(args: string[]) => TmuxRunSyncResult} runSync - non-interactive tmux commands
46
+ * @property {(args: string[]) => Promise<{ exitCode: number, signal: NodeJS.Signals | null }>} runAttach - interactive `tmux attach` (foreground, stdio inherited)
47
+ */
48
+
49
+ /**
50
+ * Return true when the current process is already inside a tmux client.
51
+ * tmux exports `$TMUX` containing the socket path + session id; we treat
52
+ * any non-empty value as "inside". Callers should NOT nest sessions.
53
+ *
54
+ * @param {NodeJS.ProcessEnv} [env]
55
+ * @returns {boolean}
56
+ */
57
+ function isInsideTmux(env = process.env) {
58
+ return typeof env.TMUX === "string" && env.TMUX.length > 0;
59
+ }
60
+
61
+ /**
62
+ * Whether `tmux` resolves on PATH. Injectable for tests.
63
+ *
64
+ * @param {(name: string) => boolean} [exists]
65
+ * @returns {boolean}
66
+ */
67
+ function tmuxAvailable(exists = realCommandExists) {
68
+ return exists("tmux");
69
+ }
70
+
71
+ /**
72
+ * Derive a tmux session name from the AI CLI id and the current working
73
+ * directory. tmux session names can't contain `:` or `.`; we also strip
74
+ * other shell-noisy characters so names stay tab-completable.
75
+ *
76
+ * @param {string} cli - e.g. "claude" / "codex"
77
+ * @param {string} cwd - absolute path; the basename is what's used
78
+ * @returns {string}
79
+ */
80
+ function baseSessionName(cli, cwd) {
81
+ const dir = path.basename(cwd).replace(/[^A-Za-z0-9_-]+/g, "_") || "root";
82
+ return `${cli}-${dir}`;
83
+ }
84
+
85
+ /**
86
+ * Walk `<base>`, `<base>-2`, `<base>-3`, ... until one is unused (per
87
+ * `exists()`) — used to spawn a parallel session when the canonical name
88
+ * is already attached. Caller decides whether to attach to an existing
89
+ * detached session or always create new.
90
+ *
91
+ * @param {string} base
92
+ * @param {(name: string) => boolean} exists
93
+ * @param {number} [max] - safety bound; defaults to 32
94
+ * @returns {string}
95
+ */
96
+ function nextFreeSessionName(base, exists, max = 32) {
97
+ if (!exists(base)) return base;
98
+ for (let i = 2; i <= max; i++) {
99
+ const candidate = `${base}-${i}`;
100
+ if (!exists(candidate)) return candidate;
101
+ }
102
+ // Fall back to base — caller will see a tmux "duplicate session" error
103
+ // rather than us silently picking a 33rd session. Surfacing the failure
104
+ // is better than masking it.
105
+ return base;
106
+ }
107
+
108
+ /**
109
+ * @returns {TmuxRunner}
110
+ */
111
+ function defaultRunner() {
112
+ return {
113
+ runSync(args) {
114
+ const result = child_process.spawnSync("tmux", args, {
115
+ encoding: "utf8",
116
+ stdio: ["ignore", "pipe", "pipe"],
117
+ });
118
+ return {
119
+ // spawnSync returns `status: null` when the spawn itself failed
120
+ // (ENOENT etc.). In that case `error` is set; callers must check it
121
+ // before treating status=1 as a real tmux exit code.
122
+ status: typeof result.status === "number" ? result.status : 1,
123
+ stdout: result.stdout || "",
124
+ stderr: result.stderr || "",
125
+ error: result.error || null,
126
+ };
127
+ },
128
+ runAttach(args) {
129
+ return new Promise((resolve, reject) => {
130
+ let settled = false;
131
+ const finish = (fn, value) => {
132
+ if (settled) return;
133
+ settled = true;
134
+ fn(value);
135
+ };
136
+ let child;
137
+ try {
138
+ child = child_process.spawn("tmux", args, { stdio: "inherit" });
139
+ } catch (err) {
140
+ finish(reject, err);
141
+ return;
142
+ }
143
+ child.on("error", (err) => finish(reject, err));
144
+ child.on("close", (code, signal) => {
145
+ // Mirror spawn.js's signal-to-exit-code table so shell scripts
146
+ // can distinguish SIGINT (130) from SIGTERM (143) etc., instead
147
+ // of all signal exits collapsing to 128.
148
+ const exitCode =
149
+ typeof code === "number" ? code : signalToExitCode(signal);
150
+ finish(resolve, { exitCode, signal: signal || null });
151
+ });
152
+ });
153
+ },
154
+ };
155
+ }
156
+
157
+ /**
158
+ * @param {string} name
159
+ * @param {TmuxRunner} runner
160
+ * @returns {boolean}
161
+ */
162
+ function sessionExists(name, runner) {
163
+ const result = runner.runSync(["has-session", "-t", `=${name}`]);
164
+ return result.status === 0;
165
+ }
166
+
167
+ /**
168
+ * Detect the running tmux's version via `tmux -V`. Returns null when
169
+ * tmux isn't on PATH or its output is unparseable. Pure (no caching);
170
+ * `launchInTmux` calls it once per invocation and threads the result.
171
+ *
172
+ * @param {TmuxRunner} runner
173
+ * @returns {{ major: number, minor: number } | null}
174
+ */
175
+ function detectTmuxVersion(runner) {
176
+ const result = runner.runSync(["-V"]);
177
+ if (result.status !== 0) return null;
178
+ return tabs.parseTmuxVersion(result.stdout || "");
179
+ }
180
+
181
+ /**
182
+ * Apply the tab-strip status-format for `sessionName`. Per-session
183
+ * (status-format is one of the options that accepts `-t <session>`),
184
+ * so the user's other tmux sessions are unaffected.
185
+ *
186
+ * On tmux >= 3.2 emits the cascading 5-tier compaction format; on
187
+ * older tmux falls back to a single-tier themed format. Failure is
188
+ * non-fatal — returns `{ applied: false, stderr }` so the caller can
189
+ * surface a warning without aborting the launch.
190
+ *
191
+ * @param {string} sessionName
192
+ * @param {TmuxRunner} runner
193
+ * @param {{
194
+ * tmuxVersion?: { major: number, minor: number } | null,
195
+ * theme?: Partial<typeof tabs.DEFAULT_TAB_THEME>,
196
+ * }} [opts]
197
+ * @returns {{ applied: boolean, stderr: string }}
198
+ */
199
+ function applyTabFormat(sessionName, runner, opts = {}) {
200
+ const theme = { ...tabs.DEFAULT_TAB_THEME, ...(opts.theme || {}) };
201
+ // Use the standard per-window formats instead of overriding the full
202
+ // status-format[0] — broader tmux compatibility, and tmux silently
203
+ // ignores invalid status-format expressions on some versions, which
204
+ // makes overrides hard to debug. window-status-format works on every
205
+ // tmux 2.0+.
206
+ // jakobwesthoff/tmux-from-scratch inspired styling: active tab is a
207
+ // rounded "pill" (Powerline half-round glyphs) over a lighter
208
+ // background, with brand-orange accent on the index:name separator
209
+ // colon. Inactive tabs are plain text with the same accent colon.
210
+ // Session name appears as a rounded pill on the left, hostname as
211
+ // a rounded pill on the right.
212
+ //
213
+ // Requires a Nerd Font / Powerline-patched font in the user's
214
+ // terminal. Without one the glyphs render as tofu boxes; users on
215
+ // a vanilla font can switch back via a future `tabStyle: "flat"`
216
+ // pref.
217
+ const HALF_ROUND_OPEN = ""; //
218
+ const HALF_ROUND_CLOSE = ""; //
219
+ const TRIANGLE_OPEN = ""; //
220
+ const TRIANGLE_CLOSE = ""; //
221
+ const BG = theme.stripBg;
222
+ const PILL_BG = theme.activeNameBg;
223
+ const ACCENT = theme.activeBg;
224
+ const FG = theme.activeNameFg;
225
+
226
+ // Active tab uses the BRAND ACCENT (orange) as its pill background
227
+ // with the strip's dark color as text — maximum contrast against
228
+ // the muted inactive tabs so the user always knows which tab is
229
+ // focused after Alt+1..9 / Alt+Tab.
230
+ const inactiveFormat = ` #I:#W `;
231
+ const activeFormat =
232
+ `#[fg=${ACCENT},bg=${BG}]${HALF_ROUND_OPEN}` +
233
+ `#[bg=${ACCENT},fg=${theme.activeFg},bold] #I:#W ` +
234
+ `#[fg=${ACCENT},bg=${BG}]${HALF_ROUND_CLOSE}`;
235
+ const statusLeft =
236
+ `#[fg=${PILL_BG},bg=${BG}]${HALF_ROUND_OPEN}` +
237
+ `#[bg=${PILL_BG},fg=${ACCENT}] #S ` +
238
+ `#[fg=${PILL_BG},bg=${BG}]${TRIANGLE_CLOSE}`;
239
+ const statusRight =
240
+ `#[fg=${PILL_BG},bg=${BG}]${TRIANGLE_OPEN}` +
241
+ `#[bg=${PILL_BG},fg=${ACCENT}] #h ` +
242
+ `#[fg=${PILL_BG},bg=${BG}]${HALF_ROUND_CLOSE}`;
243
+
244
+ // Reduce escape-time so pressing Esc inside command-prompt (Alt+n
245
+ // worktree-name prompt, etc.) cancels immediately. tmux's default
246
+ // is 500ms — it waits for a follow-up key in case Esc is the start
247
+ // of an Alt+key sequence. 0ms is "treat Esc as Esc immediately"
248
+ // and matches what most modern terminals support.
249
+ runner.runSync(["set-option", "-sg", "escape-time", "0"]);
250
+ // Force emacs key bindings in the command-prompt so Esc cancels
251
+ // the prompt instead of entering vi-normal mode (which makes the
252
+ // prompt look like it changed color but never closes). If the
253
+ // user's .tmux.conf set status-keys vi globally, our per-session
254
+ // override wins for AgileFlow sessions.
255
+ runner.runSync(["set-option", "-g", "status-keys", "emacs"]);
256
+ runner.runSync(["set-option", "-g", "mode-keys", "emacs"]);
257
+
258
+ // Apply each option with the correct tmux scope. Session-scope opts
259
+ // (status-style, status-left/right, status-justify) take
260
+ // `-t <session>`. Window-scope opts (window-status-format,
261
+ // window-status-current-format, window-status-separator) take
262
+ // `-wg` so every window in every session picks them up. Earlier
263
+ // code applied ALL options with `-t session`, which made tmux
264
+ // silently ignore the window-scope ones — the visible symptom
265
+ // was tmux's default green status bar surviving on every session.
266
+ const ops = [
267
+ {
268
+ scope: "session",
269
+ option: "status-style",
270
+ value: `bg=${BG},fg=${theme.inactiveFg}`,
271
+ },
272
+ { scope: "session", option: "status-justify", value: "centre" },
273
+ { scope: "session", option: "status-left", value: statusLeft },
274
+ { scope: "session", option: "status-left-length", value: "100" },
275
+ { scope: "session", option: "status-right", value: statusRight },
276
+ { scope: "session", option: "status-right-length", value: "100" },
277
+ // Number windows from 1 so Alt+1 maps to the first tab (matches
278
+ // Chrome's Ctrl+1 mental model). Tmux default is base-index 0,
279
+ // which means Alt+1 with no other tabs open does nothing.
280
+ { scope: "session", option: "base-index", value: "1" },
281
+ // Keep tab indices contiguous after a close — without this,
282
+ // closing window 2 leaves indices 1, 3, 4 and Alt+2 becomes
283
+ // dead. tmux renumbers on close so Alt+1..N always works.
284
+ { scope: "session", option: "renumber-windows", value: "on" },
285
+ { scope: "window-global", option: "window-status-separator", value: "" },
286
+ {
287
+ scope: "window-global",
288
+ option: "window-status-format",
289
+ value: inactiveFormat,
290
+ },
291
+ {
292
+ scope: "window-global",
293
+ option: "window-status-current-format",
294
+ value: activeFormat,
295
+ },
296
+ ];
297
+ let lastResult = { status: 0, stderr: "" };
298
+ const failures = [];
299
+ for (const { scope, option, value } of ops) {
300
+ const args =
301
+ scope === "session"
302
+ ? ["set-option", "-t", sessionName, option, value]
303
+ : ["set-option", "-wg", option, value];
304
+ const r = runner.runSync(args);
305
+ lastResult = r;
306
+ if (r.status !== 0) {
307
+ failures.push({ option, stderr: (r.stderr || "").trim() });
308
+ }
309
+ }
310
+ if (failures.length > 0) {
311
+ // eslint-disable-next-line no-console
312
+ console.error(
313
+ `agileflow launch: tab strip styling — ${failures.length} of ${ops.length} options rejected by tmux:`,
314
+ );
315
+ for (const f of failures) {
316
+ // eslint-disable-next-line no-console
317
+ console.error(` ${f.option}: ${f.stderr || "(no error message)"}`);
318
+ }
319
+ }
320
+ return {
321
+ applied: lastResult.status === 0,
322
+ stderr: lastResult.stderr || "",
323
+ };
324
+ }
325
+
326
+ /**
327
+ * Create a new detached tmux session that immediately runs `bin args...`.
328
+ * Returns the runner's exit status (0 on success).
329
+ *
330
+ * @param {{ name: string, bin: string, args: string[], cwd?: string, statusPosition?: string }} opts
331
+ * @param {TmuxRunner} runner
332
+ * @returns {{ status: number, stderr: string }}
333
+ */
334
+ function createSession(opts, runner) {
335
+ const args = ["new-session", "-d", "-s", opts.name];
336
+ if (opts.cwd) args.push("-c", opts.cwd);
337
+ args.push(opts.bin, ...opts.args);
338
+ const result = runner.runSync(args);
339
+ if (result.status === 0 && opts.statusPosition) {
340
+ // Per-session option; safe even if the user has a global tmux config.
341
+ runner.runSync([
342
+ "set-option",
343
+ "-t",
344
+ opts.name,
345
+ "status-position",
346
+ opts.statusPosition,
347
+ ]);
348
+ }
349
+ return {
350
+ status: result.status,
351
+ stderr: result.stderr,
352
+ // Propagate the spawn-itself-failed error so callers can re-throw
353
+ // ENOENT (= tmux not on PATH, e.g., TOCTOU after tmuxAvailable()
354
+ // succeeded) instead of masking it as a generic tmux failure.
355
+ error: result.error || null,
356
+ };
357
+ }
358
+
359
+ /**
360
+ * Attach to an existing session in the foreground. Returns the user's
361
+ * exit code from the attach (typically 0 on clean detach).
362
+ *
363
+ * @param {string} name
364
+ * @param {TmuxRunner} runner
365
+ * @returns {Promise<TmuxLaunchResult>}
366
+ */
367
+ async function attachSession(name, runner) {
368
+ const result = await runner.runAttach(["attach-session", "-t", name]);
369
+ return { exitCode: result.exitCode, signal: result.signal || null };
370
+ }
371
+
372
+ /**
373
+ * Keybinds to install per preset. Each entry is the argv suffix passed
374
+ * to `tmux bind-key`, expanded with `-T root -t <session>` at apply
375
+ * time. The `-T root` table means keys fire WITHOUT the tmux prefix
376
+ * (typically Ctrl+B), so users get the v3 `af` script's "tap Alt+q
377
+ * to detach" feel rather than the stock tmux "prefix then bind".
378
+ *
379
+ * Keep this list narrow: every binding has to work with stock tmux,
380
+ * no external helper scripts. Worktree / same-dir spawn shortcuts
381
+ * (the old Alt+N / Alt+S) require shell helpers that don't exist in
382
+ * v4 yet; they'll come in a follow-up slice.
383
+ *
384
+ * @typedef {Object} KeybindEntry
385
+ * @property {string} key - tmux key spec, e.g. "M-q" (Alt+q)
386
+ * @property {string[]} action - tmux command args, e.g. ["detach-client"]
387
+ * @property {string} hint - human description; surfaced in errors if a bind fails
388
+ */
389
+
390
+ /** @type {Record<string, KeybindEntry[]>} */
391
+ const KEYBIND_PRESET_BINDINGS = {
392
+ default: [
393
+ {
394
+ key: "M-q",
395
+ action: ["detach-client"],
396
+ hint: "Alt+q → detach",
397
+ },
398
+ {
399
+ // Soft interrupt — send Ctrl+C twice to the foreground pane.
400
+ // Mirrors v3 `af`'s "if Claude is hung, gently nudge it" behavior.
401
+ key: "M-k",
402
+ action: ["send-keys", "C-c", "C-c"],
403
+ hint: "Alt+k → send Ctrl+C twice (soft interrupt)",
404
+ },
405
+ {
406
+ key: "M-K",
407
+ action: ["kill-pane"],
408
+ hint: "Alt+Shift+k → force kill the current pane",
409
+ },
410
+ {
411
+ key: "M-r",
412
+ action: ["respawn-pane", "-k"],
413
+ hint: "Alt+r → respawn the current pane",
414
+ },
415
+ {
416
+ // Spawn a parallel same-dir session and switch to it. The bound CLI
417
+ // runs `agileflow launch new` which creates the session detached
418
+ // and calls tmux switch-client — so the user's pane swaps to it.
419
+ // `%AGILEFLOW%` is substituted at apply time with the actual binary
420
+ // path so this works for dogfooded `node bin/agileflow.js` as well
421
+ // as PATH-installed `agileflow`.
422
+ key: "M-s",
423
+ action: ["run-shell", "%AGILEFLOW% launch new"],
424
+ hint: "Alt+s → spawn a same-dir parallel session",
425
+ },
426
+ {
427
+ // Prompt for a worktree name, then spawn. tmux's command-prompt
428
+ // natively cancels on Escape (or Ctrl+G) without firing the
429
+ // deferred command — the prompt label calls that out so users
430
+ // know they can back out. The agileflow CLI also bails cleanly
431
+ // if `%%` came in empty (user hit Enter with no input).
432
+ key: "M-n",
433
+ action: [
434
+ "command-prompt",
435
+ "-p",
436
+ "worktree name (esc to cancel):",
437
+ "run-shell '%AGILEFLOW% launch new \"%%\"'",
438
+ ],
439
+ hint: "Alt+n → prompt for a name, create a worktree, spawn there",
440
+ },
441
+ // v3-equivalent tab (tmux window) keybinds. Living in a separate
442
+ // module so the format builder + keybind table stay testable in
443
+ // isolation; spread into the default preset so they participate in
444
+ // the same apply / unbind sweep as everything else.
445
+ ...tabs.TAB_KEYBINDS,
446
+ ],
447
+ minimal: [
448
+ {
449
+ key: "M-q",
450
+ action: ["detach-client"],
451
+ hint: "Alt+q → detach",
452
+ },
453
+ ],
454
+ none: [],
455
+ };
456
+
457
+ /**
458
+ * Install the chosen keybind preset. tmux `bind-key` is server-wide
459
+ * (the command doesn't accept `-t <session>`), so applying a preset
460
+ * affects every tmux client on the same server, not just sessions
461
+ * created by `agileflow launch`. This mirrors the v3 `af` script and is
462
+ * the standard tmux config pattern — users who want isolation pick the
463
+ * "none" preset.
464
+ *
465
+ * Bindings go in the `root` table so keys fire without the tmux prefix
466
+ * (typically Ctrl+B), giving the v3 "tap Alt+q to detach" feel.
467
+ *
468
+ * Failures are not fatal: `tmux bind-key` either succeeds or the caller
469
+ * gets a `failures` list to surface as a warning. Returning a
470
+ * structured result keeps the function pure-ish — no console output
471
+ * inside tmux.js.
472
+ *
473
+ * @param {string} preset - one of "default" | "minimal" | "none"
474
+ * @param {TmuxRunner} runner
475
+ * @returns {{ applied: number, failures: { hint: string, stderr: string }[] }}
476
+ */
477
+ /**
478
+ * Union of all keys touched by any preset. Used to clear stale binds
479
+ * before installing the chosen preset, so switching from "default" to
480
+ * "minimal" doesn't leave Alt+k/Alt+K/Alt+r from the previous run
481
+ * still active in the tmux server. Recomputed once at module load so
482
+ * adding a binding to a preset automatically extends the unbind sweep.
483
+ */
484
+ const ALL_PRESET_KEYS = (() => {
485
+ const set = new Set();
486
+ for (const list of Object.values(KEYBIND_PRESET_BINDINGS)) {
487
+ for (const b of list) set.add(b.key);
488
+ }
489
+ return [...set];
490
+ })();
491
+
492
+ /**
493
+ * Substitute `%AGILEFLOW%` in a key binding's action with the actual
494
+ * binary path. Pure helper so tests can pin the substitution result.
495
+ *
496
+ * @param {string[]} action
497
+ * @param {string} agileflowBin
498
+ * @returns {string[]}
499
+ */
500
+ function substituteBinding(action, agileflowBin) {
501
+ return action.map((arg) =>
502
+ typeof arg === "string" ? arg.replace(/%AGILEFLOW%/g, agileflowBin) : arg,
503
+ );
504
+ }
505
+
506
+ /**
507
+ * @param {string} preset
508
+ * @param {TmuxRunner} runner
509
+ * @param {{ agileflowBin?: string }} [opts]
510
+ * @returns {{ applied: number, failures: { hint: string, stderr: string }[] }}
511
+ */
512
+ function applyKeybindPreset(preset, runner, opts = {}) {
513
+ const bindings = KEYBIND_PRESET_BINDINGS[preset] || [];
514
+ let applied = 0;
515
+ /** @type {{ hint: string, stderr: string }[]} */
516
+ const failures = [];
517
+
518
+ // Resolve the agileflow binary path once per preset apply. Falls back
519
+ // to the literal string "agileflow" if argv[1] isn't a real file
520
+ // (matches the alias-installer convention).
521
+ const agileflowBin = opts.agileflowBin || resolveAgileflowBin();
522
+
523
+ // Idempotent reset: unbind every key any preset could install BEFORE
524
+ // applying the new set. Without this, switching default → minimal
525
+ // leaves Alt+k/Alt+K/Alt+r from the previous run still bound (tmux's
526
+ // `bind-key` is server-wide and persists across sessions). Unbinding
527
+ // a key that wasn't bound is harmless — tmux exits non-zero with a
528
+ // "key not found" but we don't care; the post-state is the same.
529
+ for (const key of ALL_PRESET_KEYS) {
530
+ runner.runSync(["unbind-key", "-T", "root", key]);
531
+ }
532
+
533
+ for (const b of bindings) {
534
+ const action = substituteBinding(b.action, agileflowBin);
535
+ const result = runner.runSync(["bind-key", "-T", "root", b.key, ...action]);
536
+ if (result.status === 0) {
537
+ applied++;
538
+ } else {
539
+ failures.push({ hint: b.hint, stderr: result.stderr || "" });
540
+ }
541
+ }
542
+ return { applied, failures };
543
+ }
544
+
545
+ /**
546
+ * List existing session names belonging to a specific CLI.
547
+ *
548
+ * Uses `tmux ls -F '#{session_name}'` and filters by the `<cli>-`
549
+ * prefix our `baseSessionName` produces. Returns an empty array if no
550
+ * tmux server is running (tmux ls exits non-zero), the call fails, or
551
+ * no sessions match.
552
+ *
553
+ * @param {string} cli
554
+ * @param {TmuxRunner} runner
555
+ * @returns {string[]}
556
+ */
557
+ function listSessionsForCli(cli, runner) {
558
+ const result = runner.runSync(["ls", "-F", "#{session_name}"]);
559
+ if (result.status !== 0 || !result.stdout) return [];
560
+ const prefix = `${cli}-`;
561
+ return result.stdout
562
+ .split(/\r?\n/)
563
+ .map((s) => s.trim())
564
+ .filter((s) => s.length > 0 && s.startsWith(prefix));
565
+ }
566
+
567
+ /**
568
+ * Kill a tmux session by name. Returns true on success, false on any
569
+ * tmux error (already gone, permission, etc.) — caller decides whether
570
+ * to surface the failure.
571
+ *
572
+ * @param {string} name
573
+ * @param {TmuxRunner} runner
574
+ * @returns {boolean}
575
+ */
576
+ function killSession(name, runner) {
577
+ const result = runner.runSync(["kill-session", "-t", `=${name}`]);
578
+ return result.status === 0;
579
+ }
580
+
581
+ /**
582
+ * Top-level orchestrator: pick a session name, ensure it exists with
583
+ * the user's CLI running inside it, then attach. If the canonical name
584
+ * is already attached elsewhere, spawn a numbered sibling.
585
+ *
586
+ * Caller has already verified tmux is available and we're not nested.
587
+ *
588
+ * @param {{
589
+ * bin: string,
590
+ * args?: string[],
591
+ * cwd?: string,
592
+ * statusPosition?: string,
593
+ * keybindPreset?: string,
594
+ * runner?: TmuxRunner,
595
+ * log?: (msg: string) => void,
596
+ * }} opts
597
+ * @returns {Promise<TmuxLaunchResult>}
598
+ */
599
+ async function launchInTmux(opts) {
600
+ const runner = opts.runner || defaultRunner();
601
+ const cwd = opts.cwd || process.cwd();
602
+ const base = baseSessionName(path.basename(opts.bin), cwd);
603
+ // Narration: tells the user whether they're resuming an existing
604
+ // session (their context survives) or starting fresh. Defaults to
605
+ // stderr so it doesn't pollute scripts that pipe stdout. Injectable
606
+ // for tests; pass a no-op `log` to silence.
607
+ const log =
608
+ typeof opts.log === "function"
609
+ ? opts.log
610
+ : (msg) => {
611
+ // eslint-disable-next-line no-console
612
+ console.error(msg);
613
+ };
614
+
615
+ // Reattach to the canonical session if it exists. tmux's `attach-session`
616
+ // is the right command for both "exists detached" and "exists attached"
617
+ // — the latter steals or shares the session, mirroring v3 `af` behavior.
618
+ // If the user wants a fresh parallel session, they use `launch --new` (slice 2c).
619
+ // Detect tmux version once per invocation; threaded into the tab
620
+ // format builder so we can degrade to a single-tier format on tmux
621
+ // < 3.2 instead of emitting `#{e|...}` operators it doesn't understand.
622
+ const tmuxVersion = detectTmuxVersion(runner);
623
+
624
+ const existsSync = (name) => sessionExists(name, runner);
625
+ if (existsSync(base)) {
626
+ // Apply prefs on every attach — cheap, and keeps the session in sync
627
+ // if the user changed their pref since the session was created.
628
+ if (opts.statusPosition) {
629
+ runner.runSync([
630
+ "set-option",
631
+ "-t",
632
+ base,
633
+ "status-position",
634
+ opts.statusPosition,
635
+ ]);
636
+ }
637
+ if (opts.keybindPreset) {
638
+ // Always invoke applyKeybindPreset — even for "none" — so the
639
+ // unbind sweep runs and clears any leftover binds from a previous
640
+ // "default" or "minimal" run. Without this, switching default → none
641
+ // leaves Alt+k/Alt+K/Alt+r still bound in the tmux server.
642
+ const result = applyKeybindPreset(opts.keybindPreset, runner);
643
+ for (const f of result.failures) {
644
+ log(`agileflow launch: keybind skipped — ${f.hint}`);
645
+ }
646
+ }
647
+ // Re-apply the tab strip every attach so prefs / theme changes
648
+ // since session creation take effect (and so a session created by
649
+ // an older agileflow without a strip picks one up on reattach).
650
+ // Single dark status line — the tab strip on line[0] replaces
651
+ // tmux's default green status bar entirely.
652
+ runner.runSync(["set-option", "-t", base, "status", "1"]);
653
+ applyTabFormat(base, runner, { tmuxVersion });
654
+ log(`agileflow launch: resuming session ${base}`);
655
+ return attachSession(base, runner);
656
+ }
657
+
658
+ // No existing session — create fresh. Run the agileflow `__exec`
659
+ // wrapper instead of the raw CLI so the per-CLI resume strategy
660
+ // fires (claude `--resume <uuid>` etc.) and so the session is
661
+ // recorded in the cross-reboot registry. The wrapper reads the
662
+ // session name from argv to look itself up in the registry.
663
+ // `base` is confirmed free above; no `nextFreeSessionName` walk needed.
664
+ // Lazy-load to avoid a require cycle (session-registry → tmux is fine,
665
+ // but record-on-create wants the registry which depends on this module
666
+ // transitively via parallel-session).
667
+ const { recordSession } = require("./session-registry.js");
668
+ const agileflowBin = resolveAgileflowBin();
669
+ const name = base;
670
+ const cliId = path.basename(opts.bin);
671
+ recordSession({
672
+ name,
673
+ cli: cliId,
674
+ cwd,
675
+ uuid: null,
676
+ });
677
+ const create = createSession(
678
+ {
679
+ name,
680
+ bin: agileflowBin,
681
+ args: ["launch", "__exec", name],
682
+ cwd,
683
+ statusPosition: opts.statusPosition,
684
+ },
685
+ runner,
686
+ );
687
+ if (create.status !== 0) {
688
+ // Spawn-itself-failed: tmux disappeared between tmuxAvailable() and
689
+ // here. Re-throw the original ENOENT so launch.js's existing TOCTOU
690
+ // branch can produce the install-or-reconfigure suggestion.
691
+ if (create.error) throw create.error;
692
+
693
+ // Concurrent-launch race: another `agileflow launch` from the same
694
+ // cwd created the session between our existsSync probe and our
695
+ // new-session call. The session exists now and is what the user
696
+ // wanted; just attach to it instead of surfacing a misleading
697
+ // "duplicate session" error. Apply the same setup the non-racey
698
+ // reattach path does (keybinds + status height + tab format) so
699
+ // the race-recovered user gets the same UX as everyone else.
700
+ if (sessionExists(name, runner)) {
701
+ log(`agileflow launch: resuming session ${name} (race-recovered)`);
702
+ if (opts.statusPosition) {
703
+ runner.runSync([
704
+ "set-option",
705
+ "-t",
706
+ name,
707
+ "status-position",
708
+ opts.statusPosition,
709
+ ]);
710
+ }
711
+ if (opts.keybindPreset) {
712
+ const result = applyKeybindPreset(opts.keybindPreset, runner);
713
+ for (const f of result.failures) {
714
+ log(`agileflow launch: keybind skipped — ${f.hint}`);
715
+ }
716
+ }
717
+ runner.runSync(["set-option", "-t", name, "status", "1"]);
718
+ applyTabFormat(name, runner, { tmuxVersion });
719
+ return attachSession(name, runner);
720
+ }
721
+
722
+ const stderr = create.stderr.trim() || "tmux new-session failed";
723
+ const err = new Error(`tmux: ${stderr}`);
724
+ /** @type {any} */ (err).code = "ETMUX_CREATE";
725
+ throw err;
726
+ }
727
+
728
+ if (opts.keybindPreset) {
729
+ // Always invoke applyKeybindPreset — even for "none" — so the unbind
730
+ // sweep runs and clears any prior preset's binds. Same reasoning as
731
+ // the reattach branch above.
732
+ const result = applyKeybindPreset(opts.keybindPreset, runner);
733
+ for (const f of result.failures) {
734
+ log(`agileflow launch: keybind skipped — ${f.hint}`);
735
+ }
736
+ }
737
+ // Single dark status line — the tab strip on status-format[0]
738
+ // replaces tmux's default green status bar entirely. Per-session
739
+ // so other tmux clients are unaffected. Then write the tab format
740
+ // itself. Both are best-effort; failure shouldn't block the attach.
741
+ runner.runSync(["set-option", "-t", name, "status", "1"]);
742
+ applyTabFormat(name, runner, { tmuxVersion });
743
+ log(`agileflow launch: starting new session ${name}`);
744
+ return attachSession(name, runner);
745
+ }
746
+
747
+ module.exports = {
748
+ isInsideTmux,
749
+ tmuxAvailable,
750
+ baseSessionName,
751
+ nextFreeSessionName,
752
+ sessionExists,
753
+ createSession,
754
+ attachSession,
755
+ launchInTmux,
756
+ listSessionsForCli,
757
+ killSession,
758
+ applyKeybindPreset,
759
+ substituteBinding,
760
+ detectTmuxVersion,
761
+ applyTabFormat,
762
+ KEYBIND_PRESET_BINDINGS,
763
+ defaultRunner,
764
+ };