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,247 @@
1
+ /**
2
+ * Spawn a parallel tmux session — same-dir or worktree-backed — and
3
+ * switch the user's existing client to it.
4
+ *
5
+ * Used by `agileflow launch new [name]`, which the default keybind
6
+ * preset binds to Alt+s (no name → same-dir) and Alt+n (prompts for a
7
+ * name → worktree). The caller has already verified we're inside a
8
+ * tmux client; this module assumes that and uses `switch-client` to
9
+ * swap rather than `attach-session`.
10
+ *
11
+ * No-name vs name flow:
12
+ * - No name: target dir is `process.cwd()`. New session uses
13
+ * `nextFreeSessionName(<cli>-<dir>, ...)` so it gets a `-2`, `-3`
14
+ * suffix when the canonical name is taken.
15
+ * - Name: `createWorktree({ name })` first, then the same fresh-name
16
+ * spawn against the worktree dir.
17
+ */
18
+ const path = require("path");
19
+ const {
20
+ baseSessionName,
21
+ nextFreeSessionName,
22
+ sessionExists,
23
+ createSession,
24
+ applyKeybindPreset,
25
+ defaultRunner,
26
+ } = require("./tmux.js");
27
+ const { createWorktree, removeWorktree } = require("./worktree.js");
28
+ const { recordSession, forgetSession } = require("./session-registry.js");
29
+ const { resolveAgileflowBin } = require("./alias-installer.js");
30
+
31
+ /**
32
+ * @typedef {Object} ParallelSpawnResult
33
+ * @property {string} sessionName - the new session's tmux name
34
+ * @property {string} cwd - the directory the session was created in (cwd or worktree path)
35
+ * @property {{ path: string, branch: string, base: string }} [worktree]
36
+ * - only set when `name` was supplied
37
+ */
38
+
39
+ /**
40
+ * Build a typed error so callers can branch on `err.code`. Mirrors the
41
+ * pattern used elsewhere in the launch runtime.
42
+ *
43
+ * @param {string} message
44
+ * @param {string} code
45
+ * @returns {Error}
46
+ */
47
+ function makeSpawnError(message, code) {
48
+ const err = new Error(message);
49
+ /** @type {any} */ (err).code = code;
50
+ return err;
51
+ }
52
+
53
+ /**
54
+ * Resolve the directory for the new session. With `name`, create a
55
+ * worktree first and use its path. Pure logic so tests can verify the
56
+ * branching without spawning anything.
57
+ *
58
+ * @param {{
59
+ * name?: string,
60
+ * cwd: string,
61
+ * createWorktreeImpl?: typeof createWorktree,
62
+ * }} opts
63
+ * @returns {{ cwd: string, worktree?: { path: string, branch: string, base: string } }}
64
+ */
65
+ function resolveSpawnDir(opts) {
66
+ if (!opts.name) {
67
+ return { cwd: opts.cwd };
68
+ }
69
+ const impl = opts.createWorktreeImpl || createWorktree;
70
+ const wt = impl({ name: opts.name });
71
+ return { cwd: wt.path, worktree: wt };
72
+ }
73
+
74
+ /**
75
+ * Spawn a new tmux session and switch the user's client to it.
76
+ *
77
+ * @param {{
78
+ * bin: string,
79
+ * name?: string,
80
+ * prefs: import("./defaults.js").LaunchPrefs,
81
+ * cwd?: string,
82
+ * runner?: ReturnType<typeof defaultRunner>,
83
+ * log?: (msg: string) => void,
84
+ * createWorktreeImpl?: typeof createWorktree,
85
+ * removeWorktreeImpl?: typeof removeWorktree,
86
+ * }} opts
87
+ * @returns {Promise<ParallelSpawnResult>}
88
+ */
89
+ async function runParallelSpawn(opts) {
90
+ const runner = opts.runner || defaultRunner();
91
+ const removeWt = opts.removeWorktreeImpl || removeWorktree;
92
+ const log =
93
+ typeof opts.log === "function"
94
+ ? opts.log
95
+ : (msg) => {
96
+ // eslint-disable-next-line no-console
97
+ console.error(msg);
98
+ };
99
+
100
+ const { cwd: targetCwd, worktree } = resolveSpawnDir({
101
+ name: opts.name,
102
+ cwd: opts.cwd || process.cwd(),
103
+ createWorktreeImpl: opts.createWorktreeImpl,
104
+ });
105
+
106
+ if (worktree) {
107
+ log(
108
+ `agileflow launch: created worktree at ${worktree.path} on branch ${worktree.branch}`,
109
+ );
110
+ }
111
+
112
+ // Once we've created the worktree we OWN it — if any of the tmux steps
113
+ // below fail, roll it back so the user isn't left with an orphan dir
114
+ // + branch they have to clean up by hand. The rollback helper itself
115
+ // is best-effort; on failure we surface a warning but still re-throw
116
+ // the original tmux error.
117
+ // Track the session name across the try/catch boundary so the
118
+ // rollback path can also forget the registry entry we added.
119
+ /** @type {string | null} */
120
+ let registeredName = null;
121
+ try {
122
+ const cliId = path.basename(opts.bin);
123
+ const base = baseSessionName(cliId, targetCwd);
124
+ // Always pick a fresh name. `new` semantics are "I want a parallel
125
+ // session" — never "reattach". `nextFreeSessionName` walks
126
+ // base, base-2, base-3, ... until it finds an unused slot.
127
+ const sessionName = nextFreeSessionName(base, (n) =>
128
+ sessionExists(n, runner),
129
+ );
130
+ registeredName = sessionName;
131
+
132
+ // Record the session in the cross-reboot registry BEFORE spawning
133
+ // so the __exec wrapper can find its entry. If the spawn fails we
134
+ // unrecord in the catch block below.
135
+ recordSession({
136
+ name: sessionName,
137
+ cli: cliId,
138
+ cwd: targetCwd,
139
+ uuid: null,
140
+ worktree: worktree
141
+ ? { path: worktree.path, branch: worktree.branch, base: worktree.base }
142
+ : undefined,
143
+ });
144
+
145
+ const agileflowBin = resolveAgileflowBin();
146
+ const create = createSession(
147
+ {
148
+ name: sessionName,
149
+ bin: agileflowBin,
150
+ args: ["launch", "__exec", sessionName],
151
+ cwd: targetCwd,
152
+ statusPosition: opts.prefs.tmux.statusPosition,
153
+ },
154
+ runner,
155
+ );
156
+ if (create.status !== 0) {
157
+ if (create.error) throw create.error;
158
+
159
+ // Race recovery: in the same-dir (no-name) case, another
160
+ // `agileflow launch new` invocation could have grabbed our
161
+ // candidate name between the nextFreeSessionName probe and the
162
+ // new-session call. If the session is alive now, treat this as
163
+ // "user got what they wanted" and switch-client to it. We don't
164
+ // do this for the worktree path because the worktree dir is
165
+ // freshly created and unique, so the name collision shouldn't
166
+ // happen — if it does, something weirder is going on and the
167
+ // user should see the original error.
168
+ if (!opts.name && sessionExists(sessionName, runner)) {
169
+ log(
170
+ `agileflow launch: race-recovered, attaching to session ${sessionName}`,
171
+ );
172
+ } else {
173
+ const stderr = create.stderr.trim() || "tmux new-session failed";
174
+ throw makeSpawnError(`tmux: ${stderr}`, "ETMUX_CREATE");
175
+ }
176
+ }
177
+
178
+ // Apply the user's keybind preset to the (server-wide) bindings table.
179
+ // Same call the engine makes on every launch, so the new session
180
+ // inherits the same Alt+q etc. as the parent.
181
+ if (opts.prefs.keybinds && opts.prefs.keybinds.preset) {
182
+ const result = applyKeybindPreset(opts.prefs.keybinds.preset, runner);
183
+ for (const f of result.failures) {
184
+ log(`agileflow launch: keybind skipped — ${f.hint}`);
185
+ }
186
+ }
187
+
188
+ // Swap the user's tmux client to the new session. If switch-client
189
+ // fails the session is still alive — surface its name so the user
190
+ // can attach manually.
191
+ const sw = runner.runSync(["switch-client", "-t", sessionName]);
192
+ if (sw.status !== 0) {
193
+ throw makeSpawnError(
194
+ `switch-client failed: ${sw.stderr.trim() || "unknown error"}; ` +
195
+ `session "${sessionName}" is still running — \`tmux attach -t ${sessionName}\` to enter it.`,
196
+ "ETMUX_SWITCH",
197
+ );
198
+ }
199
+
200
+ log(`agileflow launch: switched to new session ${sessionName}`);
201
+ return { sessionName, cwd: targetCwd, worktree };
202
+ } catch (err) {
203
+ // Forget the registry entry we added — the spawn didn't succeed
204
+ // so there's nothing to restore later. Worst case: a tmux session
205
+ // exists but isn't in the registry (cosmetic; user can attach
206
+ // manually via `tmux attach -t <name>`).
207
+ if (registeredName) {
208
+ try {
209
+ forgetSession(registeredName);
210
+ } catch {
211
+ /* swallow */
212
+ }
213
+ }
214
+ // Rollback path. Worktree got created but a subsequent step (tmux
215
+ // create / keybind apply / switch-client) failed — remove the
216
+ // worktree dir + branch so the repo state matches the launch state
217
+ // (i.e., as if the user had never pressed Alt+n).
218
+ if (worktree) {
219
+ try {
220
+ const result = removeWt({
221
+ path: worktree.path,
222
+ branch: worktree.branch,
223
+ });
224
+ if (result.removed && result.branchRemoved) {
225
+ log(
226
+ `agileflow launch: rolled back worktree ${worktree.path} + branch ${worktree.branch}`,
227
+ );
228
+ } else {
229
+ log(
230
+ `agileflow launch: worktree rollback partial — manual cleanup may be needed (${result.stderr})`,
231
+ );
232
+ }
233
+ } catch (rollbackErr) {
234
+ log(
235
+ `agileflow launch: rollback failed — leftover at ${worktree.path}: ${rollbackErr.message}`,
236
+ );
237
+ }
238
+ }
239
+ throw err;
240
+ }
241
+ }
242
+
243
+ module.exports = {
244
+ runParallelSpawn,
245
+ resolveSpawnDir,
246
+ makeSpawnError,
247
+ };
@@ -0,0 +1,211 @@
1
+ /**
2
+ * Read / write `~/.agileflow/launch-prefs.json` — the first user-level
3
+ * config in v4.
4
+ *
5
+ * Mirrors the atomic-write pattern from `runtime/config/writer.js`:
6
+ * render to a sibling temp file, then `fs.rename()` into place so a
7
+ * concurrent read either sees the old content or the new content,
8
+ * never a torn half-write.
9
+ *
10
+ * Validation here is *shape-light* on read: missing keys are filled
11
+ * from `defaultPrefs()`, unknown extras are dropped. The JSON schema
12
+ * (`schema.json`) is the authoritative contract for editor tooling.
13
+ */
14
+ const fs = require("fs");
15
+ const os = require("os");
16
+ const path = require("path");
17
+
18
+ const {
19
+ defaultPrefs,
20
+ KNOWN_CLI_IDS,
21
+ STATUS_POSITIONS,
22
+ KEYBIND_PRESETS,
23
+ } = require("./defaults.js");
24
+
25
+ const FILENAME = "launch-prefs.json";
26
+ const SCHEMA_REF = "./node_modules/agileflow/src/runtime/launch/schema.json";
27
+
28
+ /**
29
+ * @param {string} [home] - override for tests; defaults to os.homedir()
30
+ * @returns {string}
31
+ */
32
+ function prefsPath(home) {
33
+ const root = home || os.homedir();
34
+ return path.join(root, ".agileflow", FILENAME);
35
+ }
36
+
37
+ /**
38
+ * Merge a partial prefs object on top of defaults, sanitizing unknown
39
+ * enum values back to their defaults. Returns a fresh object — never
40
+ * mutates the input.
41
+ *
42
+ * @param {Partial<import('./defaults.js').LaunchPrefs>} [partial]
43
+ * @returns {import('./defaults.js').LaunchPrefs}
44
+ */
45
+ function mergeWithDefaults(partial = {}) {
46
+ const base = defaultPrefs();
47
+
48
+ const cli = partial.cli || {};
49
+ const preferred = KNOWN_CLI_IDS.includes(cli.preferred)
50
+ ? cli.preferred
51
+ : base.cli.preferred;
52
+ const fallbackOrder = Array.isArray(cli.fallbackOrder)
53
+ ? cli.fallbackOrder.filter((id) => KNOWN_CLI_IDS.includes(id))
54
+ : base.cli.fallbackOrder;
55
+
56
+ const tmux = partial.tmux || {};
57
+ const tmuxEnabled =
58
+ typeof tmux.enabled === "boolean" ? tmux.enabled : base.tmux.enabled;
59
+ const statusPosition = STATUS_POSITIONS.includes(tmux.statusPosition)
60
+ ? tmux.statusPosition
61
+ : base.tmux.statusPosition;
62
+
63
+ const keybinds = partial.keybinds || {};
64
+ const preset = KEYBIND_PRESETS.includes(keybinds.preset)
65
+ ? keybinds.preset
66
+ : base.keybinds.preset;
67
+
68
+ const aliases = partial.aliases || {};
69
+ const afEnabled =
70
+ aliases.af && typeof aliases.af.enabled === "boolean"
71
+ ? aliases.af.enabled
72
+ : base.aliases.af.enabled;
73
+
74
+ const pinned = Array.isArray(partial.pinned)
75
+ ? partial.pinned.filter((s) => typeof s === "string")
76
+ : base.pinned;
77
+
78
+ // Invariant: `preferred` must appear in `fallbackOrder` (the latter is
79
+ // documented as "order to try if preferred is missing", which only
80
+ // makes sense if preferred itself is in the chain). Hand-edited prefs
81
+ // can violate this — we repair it here rather than at every read site.
82
+ let resolvedFallback = fallbackOrder.length
83
+ ? fallbackOrder
84
+ : base.cli.fallbackOrder;
85
+ if (!resolvedFallback.includes(preferred)) {
86
+ resolvedFallback = [preferred, ...resolvedFallback];
87
+ }
88
+
89
+ return {
90
+ version: 1,
91
+ cli: {
92
+ preferred,
93
+ fallbackOrder: resolvedFallback,
94
+ },
95
+ tmux: { enabled: tmuxEnabled, statusPosition },
96
+ keybinds: { preset },
97
+ aliases: { af: { enabled: afEnabled } },
98
+ pinned,
99
+ };
100
+ }
101
+
102
+ /**
103
+ * Load prefs from disk. Returns defaults if the file is absent.
104
+ * Throws on invalid JSON so the caller can surface an actionable error.
105
+ *
106
+ * @param {string} [home]
107
+ * @returns {Promise<{
108
+ * prefs: import('./defaults.js').LaunchPrefs,
109
+ * source: 'file' | 'defaults',
110
+ * path: string,
111
+ * }>}
112
+ */
113
+ async function loadPrefs(home) {
114
+ const file = prefsPath(home);
115
+ let raw;
116
+ try {
117
+ raw = await fs.promises.readFile(file, "utf8");
118
+ } catch (err) {
119
+ if (err && err.code === "ENOENT") {
120
+ return { prefs: defaultPrefs(), source: "defaults", path: file };
121
+ }
122
+ throw err;
123
+ }
124
+
125
+ let parsed;
126
+ try {
127
+ parsed = JSON.parse(raw);
128
+ } catch (err) {
129
+ const wrapped = new Error(`could not parse ${file}: ${err.message}`);
130
+ wrapped.cause = err;
131
+ throw wrapped;
132
+ }
133
+
134
+ // JSON.parse can return null, primitives, or arrays — any of which
135
+ // would crash `mergeWithDefaults` at the first property access. Reject
136
+ // them with the same "could not parse" shape so the caller's error
137
+ // path is the only one users see.
138
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
139
+ const kind =
140
+ parsed === null
141
+ ? "null"
142
+ : Array.isArray(parsed)
143
+ ? "array"
144
+ : typeof parsed;
145
+ throw new Error(
146
+ `could not parse ${file}: expected a JSON object, got ${kind}`,
147
+ );
148
+ }
149
+
150
+ return { prefs: mergeWithDefaults(parsed), source: "file", path: file };
151
+ }
152
+
153
+ /**
154
+ * Atomically write prefs to disk. Stamps `lastUpdated` with the current
155
+ * ISO timestamp. Ensures `~/.agileflow/` exists.
156
+ *
157
+ * @param {import('./defaults.js').LaunchPrefs} next
158
+ * @param {string} [home]
159
+ * @returns {Promise<string>} absolute path of the written file
160
+ */
161
+ async function writePrefs(next, home) {
162
+ const merged = mergeWithDefaults(next);
163
+ const file = prefsPath(home);
164
+ const dir = path.dirname(file);
165
+ const tmp = path.join(dir, `.${FILENAME}.tmp-${process.pid}`);
166
+
167
+ const payload = {
168
+ $schema: SCHEMA_REF,
169
+ ...merged,
170
+ lastUpdated: new Date().toISOString(),
171
+ };
172
+ const content = JSON.stringify(payload, null, 2) + "\n";
173
+
174
+ try {
175
+ await fs.promises.mkdir(dir, { recursive: true });
176
+ await fs.promises.writeFile(tmp, content, "utf8");
177
+ await fs.promises.rename(tmp, file);
178
+ } catch (err) {
179
+ try {
180
+ await fs.promises.unlink(tmp);
181
+ } catch {
182
+ /* swallow */
183
+ }
184
+ throw err;
185
+ }
186
+ return file;
187
+ }
188
+
189
+ /**
190
+ * Whether a prefs file exists on disk.
191
+ * @param {string} [home]
192
+ * @returns {Promise<boolean>}
193
+ */
194
+ async function prefsExist(home) {
195
+ try {
196
+ await fs.promises.access(prefsPath(home), fs.constants.F_OK);
197
+ return true;
198
+ } catch {
199
+ return false;
200
+ }
201
+ }
202
+
203
+ module.exports = {
204
+ FILENAME,
205
+ SCHEMA_REF,
206
+ prefsPath,
207
+ mergeWithDefaults,
208
+ loadPrefs,
209
+ writePrefs,
210
+ prefsExist,
211
+ };
@@ -0,0 +1,234 @@
1
+ /**
2
+ * Per-project launch prefs cascade.
3
+ *
4
+ * Layering (lowest-priority first):
5
+ * 1. Built-in defaults from `defaults.js`
6
+ * 2. Global user prefs `~/.agileflow/launch-prefs.json`
7
+ * 3. Project prefs `<repo>/.agileflow/launch.json`
8
+ *
9
+ * The project file is searched by walking up from `cwd` until either:
10
+ * - we find a `.agileflow/launch.json`, or
11
+ * - we cross a `.git` boundary (the repo root — beyond it we're in a
12
+ * different project's territory), or
13
+ * - we hit the filesystem root.
14
+ *
15
+ * Per-key partial override: a project file can specify ONLY the keys it
16
+ * cares about. Unspecified keys fall through to the global file's value,
17
+ * which in turn falls through to defaults. This lets a repo flip just
18
+ * `tmux.enabled` without having to mirror the whole prefs shape.
19
+ *
20
+ * Project file shape mirrors `launch-prefs.json` minus `$schema` and
21
+ * `lastUpdated`. Hand-edited files are normal; unknown extras are
22
+ * dropped silently (same posture as the global loader).
23
+ */
24
+ const fs = require("fs");
25
+ const path = require("path");
26
+
27
+ const { loadPrefs } = require("./prefs.js");
28
+
29
+ const PROJECT_DIR = ".agileflow";
30
+ const PROJECT_FILENAME = "launch.json";
31
+
32
+ /**
33
+ * Walk up from `cwd` looking for a `.agileflow/launch.json`. Stops at
34
+ * the filesystem root, or at the first directory containing a `.git`
35
+ * entry — beyond a repo boundary we shouldn't be inheriting another
36
+ * project's prefs.
37
+ *
38
+ * @param {string} cwd
39
+ * @param {{
40
+ * existsSync?: (p: string) => boolean,
41
+ * statSync?: typeof fs.statSync,
42
+ * }} [opts]
43
+ * @returns {string | null}
44
+ */
45
+ function findProjectPrefsFile(cwd, opts = {}) {
46
+ const existsSync = opts.existsSync || ((p) => fs.existsSync(p));
47
+ const statSync = opts.statSync || fs.statSync;
48
+ let dir = path.resolve(cwd);
49
+ // Cap the walk at 64 ancestors. Filesystems hit root long before that
50
+ // in practice; the cap exists so a pathological symlink loop can't
51
+ // stall us.
52
+ for (let i = 0; i < 64; i++) {
53
+ const candidate = path.join(dir, PROJECT_DIR, PROJECT_FILENAME);
54
+ if (existsSync(candidate)) {
55
+ try {
56
+ if (statSync(candidate).isFile()) return candidate;
57
+ } catch {
58
+ /* swallow — vanished between exists + stat */
59
+ }
60
+ }
61
+ // Repo boundary check happens AFTER the in-dir search so the repo
62
+ // root itself can host the project prefs (the common case).
63
+ if (existsSync(path.join(dir, ".git"))) return null;
64
+ const parent = path.dirname(dir);
65
+ if (parent === dir) return null; // hit filesystem root
66
+ dir = parent;
67
+ }
68
+ return null;
69
+ }
70
+
71
+ /**
72
+ * Read + parse a project prefs file. Returns null on missing file,
73
+ * malformed JSON, or non-object payload — same forgiving posture as
74
+ * `loadRegistry`. Surfacing a hard error here would block `launch` for
75
+ * what is almost always a manual-edit typo.
76
+ *
77
+ * @param {string} file
78
+ * @param {{ readFileSync?: typeof fs.readFileSync }} [opts]
79
+ * @returns {Partial<import("./defaults.js").LaunchPrefs> | null}
80
+ */
81
+ function readProjectPrefs(file, opts = {}) {
82
+ const readFileSync = opts.readFileSync || fs.readFileSync;
83
+ let raw;
84
+ try {
85
+ raw = readFileSync(file, "utf8");
86
+ } catch {
87
+ return null;
88
+ }
89
+ let parsed;
90
+ try {
91
+ parsed = JSON.parse(raw);
92
+ } catch {
93
+ // eslint-disable-next-line no-console
94
+ console.error(
95
+ `agileflow launch: ${file} is not valid JSON — ignoring project prefs.`,
96
+ );
97
+ return null;
98
+ }
99
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
100
+ return null;
101
+ }
102
+ return /** @type {Partial<import("./defaults.js").LaunchPrefs>} */ (parsed);
103
+ }
104
+
105
+ /**
106
+ * Layer a partial prefs object over an already-merged base. Mirrors the
107
+ * shape and validation of `mergeWithDefaults` but uses `base` (the
108
+ * global-merged prefs) as the fallback instead of the built-in defaults.
109
+ *
110
+ * Per-key semantics:
111
+ * - cli, tmux, keybinds, aliases: partial sub-object override. Any
112
+ * leaf that the project omits inherits from `base`.
113
+ * - pinned: project array (when present) REPLACES base entirely —
114
+ * "pinned" is conceptually a list, not a deep-merge dictionary.
115
+ *
116
+ * Unknown enum values fall back to the corresponding `base` value (NOT
117
+ * defaults) so a typo in the project file degrades gracefully.
118
+ *
119
+ * @param {import("./defaults.js").LaunchPrefs} base
120
+ * @param {Partial<import("./defaults.js").LaunchPrefs>} partial
121
+ * @returns {import("./defaults.js").LaunchPrefs}
122
+ */
123
+ function mergePartialOver(base, partial) {
124
+ const {
125
+ KNOWN_CLI_IDS,
126
+ STATUS_POSITIONS,
127
+ KEYBIND_PRESETS,
128
+ } = require("./defaults.js");
129
+
130
+ const cli = partial.cli || {};
131
+ const preferred = KNOWN_CLI_IDS.includes(cli.preferred)
132
+ ? cli.preferred
133
+ : base.cli.preferred;
134
+ const fallbackOrder = Array.isArray(cli.fallbackOrder)
135
+ ? cli.fallbackOrder.filter((id) => KNOWN_CLI_IDS.includes(id))
136
+ : base.cli.fallbackOrder;
137
+
138
+ const tmux = partial.tmux || {};
139
+ const tmuxEnabled =
140
+ typeof tmux.enabled === "boolean" ? tmux.enabled : base.tmux.enabled;
141
+ const statusPosition = STATUS_POSITIONS.includes(tmux.statusPosition)
142
+ ? tmux.statusPosition
143
+ : base.tmux.statusPosition;
144
+
145
+ const keybinds = partial.keybinds || {};
146
+ const preset = KEYBIND_PRESETS.includes(keybinds.preset)
147
+ ? keybinds.preset
148
+ : base.keybinds.preset;
149
+
150
+ const aliases = partial.aliases || {};
151
+ const afEnabled =
152
+ aliases.af && typeof aliases.af.enabled === "boolean"
153
+ ? aliases.af.enabled
154
+ : base.aliases.af.enabled;
155
+
156
+ const pinned = Array.isArray(partial.pinned)
157
+ ? partial.pinned.filter((s) => typeof s === "string")
158
+ : base.pinned;
159
+
160
+ // Same invariant as the global loader: preferred must be in fallbackOrder.
161
+ // Project-level override could break it (e.g., partial only sets
162
+ // cli.preferred and inherits a fallbackOrder that doesn't include it).
163
+ let resolvedFallback = fallbackOrder.length
164
+ ? fallbackOrder
165
+ : base.cli.fallbackOrder;
166
+ if (!resolvedFallback.includes(preferred)) {
167
+ resolvedFallback = [preferred, ...resolvedFallback];
168
+ }
169
+
170
+ return {
171
+ version: /** @type {1} */ (1),
172
+ cli: { preferred, fallbackOrder: resolvedFallback },
173
+ tmux: { enabled: tmuxEnabled, statusPosition },
174
+ keybinds: { preset },
175
+ aliases: { af: { enabled: afEnabled } },
176
+ pinned,
177
+ };
178
+ }
179
+
180
+ /**
181
+ * Load prefs with the full cascade. The returned `sources` array records
182
+ * which files contributed (in increasing precedence), so the `launch
183
+ * where` subcommand can show the user exactly what's being layered.
184
+ *
185
+ * @param {{
186
+ * home?: string,
187
+ * cwd?: string,
188
+ * loadGlobalPrefs?: typeof loadPrefs,
189
+ * findProjectFile?: typeof findProjectPrefsFile,
190
+ * readProjectFile?: typeof readProjectPrefs,
191
+ * }} [opts]
192
+ * @returns {Promise<{
193
+ * prefs: import("./defaults.js").LaunchPrefs,
194
+ * sources: Array<{ layer: 'defaults' | 'global' | 'project', path?: string }>,
195
+ * }>}
196
+ */
197
+ async function loadCascadedPrefs(opts = {}) {
198
+ const loadGlobal = opts.loadGlobalPrefs || loadPrefs;
199
+ const findProject = opts.findProjectFile || findProjectPrefsFile;
200
+ const readProject = opts.readProjectFile || readProjectPrefs;
201
+ const cwd = opts.cwd || process.cwd();
202
+
203
+ /** @type {Array<{ layer: 'defaults' | 'global' | 'project', path?: string }>} */
204
+ const sources = [{ layer: "defaults" }];
205
+
206
+ const globalLoaded = await loadGlobal(opts.home);
207
+ if (globalLoaded.source === "file") {
208
+ sources.push({ layer: "global", path: globalLoaded.path });
209
+ }
210
+
211
+ const projectFile = findProject(cwd);
212
+ if (!projectFile) {
213
+ return { prefs: globalLoaded.prefs, sources };
214
+ }
215
+ const projectPartial = readProject(projectFile);
216
+ if (!projectPartial) {
217
+ // File present but unreadable / malformed — already logged by
218
+ // readProjectPrefs. Treat as absent so launch keeps working.
219
+ return { prefs: globalLoaded.prefs, sources };
220
+ }
221
+
222
+ const merged = mergePartialOver(globalLoaded.prefs, projectPartial);
223
+ sources.push({ layer: "project", path: projectFile });
224
+ return { prefs: merged, sources };
225
+ }
226
+
227
+ module.exports = {
228
+ PROJECT_DIR,
229
+ PROJECT_FILENAME,
230
+ findProjectPrefsFile,
231
+ readProjectPrefs,
232
+ mergePartialOver,
233
+ loadCascadedPrefs,
234
+ };