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
@@ -9,28 +9,126 @@
9
9
  * Exit code: 0 on green, 1 if any errors. Warnings are surfaced but
10
10
  * don't fail the command.
11
11
  */
12
- const path = require('path');
13
- const fs = require('fs');
12
+ const path = require("path");
13
+ const fs = require("fs");
14
14
 
15
- const { discoverPlugins } = require('../../runtime/plugins/registry.js');
15
+ const { discoverPlugins } = require("../../runtime/plugins/registry.js");
16
16
  const {
17
17
  validatePluginSet,
18
18
  hasErrors: pluginHasErrors,
19
- } = require('../../runtime/plugins/validator.js');
20
- const {
21
- loadHookManifest,
22
- } = require('../../runtime/hooks/manifest-loader.js');
23
- const {
24
- buildHookManifest,
25
- } = require('../../runtime/hooks/aggregator.js');
26
- const { resolvePlugins } = require('../../runtime/plugins/resolver.js');
19
+ } = require("../../runtime/plugins/validator.js");
20
+ const { loadHookManifest } = require("../../runtime/hooks/manifest-loader.js");
21
+ const { buildHookManifest } = require("../../runtime/hooks/aggregator.js");
22
+ const { resolvePlugins } = require("../../runtime/plugins/resolver.js");
27
23
  const {
28
24
  validateSkillsAtRoot,
29
25
  validateSkill,
30
26
  loadSkill,
31
27
  detectKeywordCollisions,
32
28
  hasErrors: skillHasErrors,
33
- } = require('../../runtime/skills/validator.js');
29
+ } = require("../../runtime/skills/validator.js");
30
+ const { loadConfig } = require("../../runtime/config/loader.js");
31
+ const { IDE_CAPABILITIES } = require("../../runtime/ide/capabilities.js");
32
+ const {
33
+ MANAGED_EVENTS,
34
+ LEGACY_MANAGED_EVENTS,
35
+ HOOK_COMMAND_MARKER,
36
+ isAgileflowEntry,
37
+ } = require("../../runtime/ide/claude-code-settings.js");
38
+
39
+ /**
40
+ * v3-era directories under `.agileflow/` that v4 never writes.
41
+ * Detection-only — actual removal is the follow-up `--fix` work.
42
+ */
43
+ const LEGACY_AGILEFLOW_SUBDIRS = [
44
+ "agents",
45
+ "base-prompts",
46
+ "cache",
47
+ "commands",
48
+ "config",
49
+ "council",
50
+ "experts",
51
+ "hooks",
52
+ "knowledge",
53
+ "lib",
54
+ "mixins",
55
+ "profiles",
56
+ "scripts",
57
+ "snippets",
58
+ ];
59
+
60
+ /** v3-era files at `.agileflow/` root that v4 doesn't write. */
61
+ const LEGACY_AGILEFLOW_FILES = ["CHANGELOG.md", "config.yaml"];
62
+
63
+ /**
64
+ * v3-era directories under `.claude/` that v4 doesn't populate.
65
+ * `agents/` and `commands/` are intentionally NOT here — v4 still
66
+ * mirrors plugin slash-commands and subagents into them via
67
+ * claude-code-content.js. Only flagged if they contain agileflow-*
68
+ * entries (see section C below).
69
+ */
70
+ const LEGACY_CLAUDE_SUBDIRS = ["hooks", "plans"];
71
+
72
+ /** Narrow a value to a plain object (not array, not null, not primitive). */
73
+ function isPlainObject(v) {
74
+ return v != null && typeof v === "object" && !Array.isArray(v);
75
+ }
76
+
77
+ /**
78
+ * Read JSON file, returning null on any failure (missing, malformed,
79
+ * permission denied). The detector treats unreadable config as "skip
80
+ * this section" rather than crashing.
81
+ */
82
+ function readJSONSafe(p) {
83
+ try {
84
+ return JSON.parse(fs.readFileSync(p, "utf8"));
85
+ } catch {
86
+ return null;
87
+ }
88
+ }
89
+
90
+ /** readdirSync wrapped to return [] on permission-denied / vanished dirs. */
91
+ function readdirSafe(p) {
92
+ try {
93
+ return fs.readdirSync(p);
94
+ } catch {
95
+ return [];
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Expand env vars and ~ in a hook command's script path, then check if
101
+ * that file exists. Returns the resolved path if it's broken (file
102
+ * referenced but doesn't exist), or null otherwise (no path, or path
103
+ * exists, or path is opaque).
104
+ *
105
+ * Recognizes paths ending in common script extensions; conservative on
106
+ * purpose so we don't false-positive on shell builtins or commands
107
+ * that just happen to have a slash.
108
+ *
109
+ * @param {string} command
110
+ * @param {string} cwd
111
+ * @returns {string|null}
112
+ */
113
+ function brokenScriptInCommand(command, cwd) {
114
+ if (typeof command !== "string") return null;
115
+ // Skip our own dispatcher — handled by other detection kinds.
116
+ if (command.includes(HOOK_COMMAND_MARKER)) return null;
117
+ // Extract path-like tokens with a known script extension.
118
+ const tokens = command.match(
119
+ /[\w$~/.\-\\]+\.(?:js|sh|py|ts|mjs|cjs|bash|zsh|rb)\b/g,
120
+ );
121
+ if (!tokens) return null;
122
+ for (const tok of tokens) {
123
+ let p = tok
124
+ .replace(/\$CLAUDE_PROJECT_DIR/g, cwd)
125
+ .replace(/\$HOME/g, require("os").homedir())
126
+ .replace(/^~(?=\/|$)/, require("os").homedir());
127
+ if (!path.isAbsolute(p)) p = path.join(cwd, p);
128
+ if (!fs.existsSync(p)) return p;
129
+ }
130
+ return null;
131
+ }
34
132
 
35
133
  /**
36
134
  * Walk `content/plugins/<plugin>/skills/<skill>/SKILL.md` for every
@@ -43,7 +141,7 @@ async function validateBundledSkills() {
43
141
  /** @type {Array<import('../../runtime/skills/validator.js').SkillManifest>} */
44
142
  const allSkills = [];
45
143
  for (const p of plugins) {
46
- const root = path.join(p.dir, 'skills');
144
+ const root = path.join(p.dir, "skills");
47
145
  const r = await validateSkillsAtRoot(root);
48
146
  issues.push(...r.issues);
49
147
  allSkills.push(...r.skills);
@@ -65,7 +163,9 @@ async function validateAggregatedHookManifest() {
65
163
  const plugins = discoverPlugins();
66
164
  const { ordered } = resolvePlugins(plugins, []);
67
165
  const manifestObj = buildHookManifest(ordered);
68
- const { normalizeManifest } = require('../../runtime/hooks/manifest-loader.js');
166
+ const {
167
+ normalizeManifest,
168
+ } = require("../../runtime/hooks/manifest-loader.js");
69
169
  normalizeManifest(manifestObj);
70
170
  } catch (err) {
71
171
  errors.push(err.message);
@@ -77,7 +177,7 @@ async function validateAggregatedHookManifest() {
77
177
  * Validate a project's installed hook manifest file (if present).
78
178
  */
79
179
  async function validateInstalledManifest(cwd) {
80
- const manifestPath = path.join(cwd, '.agileflow', 'hook-manifest.yaml');
180
+ const manifestPath = path.join(cwd, ".agileflow", "hook-manifest.yaml");
81
181
  if (!fs.existsSync(manifestPath)) return [];
82
182
  /** @type {string[]} */
83
183
  const errors = [];
@@ -101,54 +201,739 @@ function printSection(title, issues) {
101
201
  console.log(`\n${title}`);
102
202
  if (issues.length === 0) {
103
203
  // eslint-disable-next-line no-console
104
- console.log(' ok');
204
+ console.log(" ok");
105
205
  return 0;
106
206
  }
107
207
  let errorCount = 0;
108
208
  for (const issue of issues) {
109
- const sev = issue.severity || 'error';
110
- if (sev === 'error') errorCount += 1;
111
- const label = sev === 'error' ? 'ERROR' : 'WARN ';
112
- const id = issue.skillId || issue.pluginId || '';
113
- const idPrefix = id ? `[${id}] ` : '';
209
+ const sev = issue.severity || "error";
210
+ if (sev === "error") errorCount += 1;
211
+ const label = sev === "error" ? "ERROR" : "WARN ";
212
+ const id = issue.skillId || issue.pluginId || "";
213
+ const idPrefix = id ? `[${id}] ` : "";
114
214
  // eslint-disable-next-line no-console
115
215
  console.log(` ${label} ${idPrefix}${issue.message}`);
116
216
  }
117
217
  return errorCount;
118
218
  }
119
219
 
120
- async function doctor() {
220
+ /**
221
+ * Check installation health in the current project directory.
222
+ * Returns issues describing missing/misconfigured IDE setup.
223
+ */
224
+ async function checkInstallHealth(cwd) {
225
+ const issues = [];
226
+ const configPath = path.join(cwd, "agileflow.config.json");
227
+
228
+ if (!fs.existsSync(configPath)) {
229
+ issues.push({
230
+ severity: "warn",
231
+ message:
232
+ "agileflow.config.json not found — run `agileflow setup` to configure",
233
+ });
234
+ return issues;
235
+ }
236
+
237
+ let cfg;
238
+ try {
239
+ cfg = JSON.parse(fs.readFileSync(configPath, "utf8"));
240
+ } catch {
241
+ issues.push({
242
+ severity: "error",
243
+ message: "agileflow.config.json is not valid JSON",
244
+ });
245
+ return issues;
246
+ }
247
+
248
+ const targets = cfg?.ide?.targets;
249
+ if (!Array.isArray(targets) || targets.length === 0) {
250
+ issues.push({
251
+ severity: "warn",
252
+ message: "No IDE targets configured in agileflow.config.json",
253
+ });
254
+ return issues;
255
+ }
256
+
257
+ for (const ide of targets) {
258
+ const caps = IDE_CAPABILITIES[ide];
259
+ if (!caps) {
260
+ issues.push({
261
+ severity: "warn",
262
+ message: `Unknown IDE target "${ide}" in config`,
263
+ });
264
+ continue;
265
+ }
266
+
267
+ // Check skills directory exists and has at least one SKILL.md
268
+ if (caps.skills) {
269
+ const skillsDir = path.join(cwd, caps.skillsDir);
270
+ if (!fs.existsSync(skillsDir)) {
271
+ issues.push({
272
+ severity: "warn",
273
+ message: `[${ide}] Skills not installed — ${caps.skillsDir}/ missing. Run \`agileflow setup\``,
274
+ });
275
+ } else {
276
+ const skillFiles = fs.readdirSync(skillsDir).filter((f) => {
277
+ const skillPath = path.join(skillsDir, f, "SKILL.md");
278
+ return fs.existsSync(skillPath);
279
+ });
280
+ if (skillFiles.length === 0) {
281
+ issues.push({
282
+ severity: "error",
283
+ message: `[${ide}] Skills directory exists but contains no SKILL.md files`,
284
+ });
285
+ } else {
286
+ issues.push({
287
+ severity: "info",
288
+ message: `[${ide}] ${skillFiles.length} skill(s) installed in ${caps.skillsDir}/`,
289
+ });
290
+ }
291
+ }
292
+ }
293
+
294
+ // Check hooks wired in IDE settings
295
+ if (caps.hooks && caps.settingsFile) {
296
+ const settingsPath = path.join(cwd, caps.settingsFile);
297
+ if (!fs.existsSync(settingsPath)) {
298
+ issues.push({
299
+ severity: "warn",
300
+ message: `[${ide}] Settings file not found at ${caps.settingsFile} — hooks may not fire`,
301
+ });
302
+ } else {
303
+ try {
304
+ const settings = JSON.parse(fs.readFileSync(settingsPath, "utf8"));
305
+ const hasHooks =
306
+ settings?.hooks && Object.keys(settings.hooks).length > 0;
307
+ if (!hasHooks) {
308
+ issues.push({
309
+ severity: "warn",
310
+ message: `[${ide}] No hooks found in ${caps.settingsFile} — SessionStart injection unavailable`,
311
+ });
312
+ } else {
313
+ const hookEvents = Object.keys(settings.hooks);
314
+ issues.push({
315
+ severity: "info",
316
+ message: `[${ide}] Hooks wired: ${hookEvents
317
+ .filter((e) => !LEGACY_MANAGED_EVENTS.has(e))
318
+ .join(", ")}`,
319
+ });
320
+ }
321
+ } catch {
322
+ issues.push({
323
+ severity: "warn",
324
+ message: `[${ide}] Could not parse ${caps.settingsFile}`,
325
+ });
326
+ }
327
+ }
328
+ }
329
+ }
330
+
331
+ return issues;
332
+ }
333
+
334
+ /**
335
+ * Build the shared input bundle every detector / fixer needs. Reading
336
+ * once here avoids the duplicate settings.json parse the old monolith
337
+ * did (once for hook-event detection, once for broken-command).
338
+ *
339
+ * @param {string} cwd
340
+ * @returns {{
341
+ * cwd: string,
342
+ * settingsPath: string,
343
+ * settings: any,
344
+ * agileflowDir: string,
345
+ * claudeDir: string,
346
+ * configPath: string,
347
+ * config: any,
348
+ * manifestPath: string,
349
+ * }}
350
+ */
351
+ function buildContext(cwd) {
352
+ const settingsPath = path.join(cwd, ".claude", "settings.json");
353
+ const configPath = path.join(cwd, "agileflow.config.json");
354
+ return {
355
+ cwd,
356
+ settingsPath,
357
+ settings: fs.existsSync(settingsPath) ? readJSONSafe(settingsPath) : null,
358
+ agileflowDir: path.join(cwd, ".agileflow"),
359
+ claudeDir: path.join(cwd, ".claude"),
360
+ configPath,
361
+ config: fs.existsSync(configPath) ? readJSONSafe(configPath) : null,
362
+ manifestPath: path.join(cwd, ".agileflow", "hook-manifest.yaml"),
363
+ };
364
+ }
365
+
366
+ /**
367
+ * Walk `.claude/settings.json` hooks, coerce malformed shapes, and yield
368
+ * `[event, entries]` pairs every hook-related detector / fixer iterates.
369
+ * Returns [] when settings or hooks are missing / wrong-shape.
370
+ *
371
+ * @param {any} settings
372
+ * @returns {Array<[string, Array<any>]>}
373
+ */
374
+ function iterHookEvents(settings) {
375
+ if (!settings || !isPlainObject(settings.hooks)) return [];
376
+ return Object.keys(settings.hooks).map((event) => {
377
+ const raw = settings.hooks[event];
378
+ let entries;
379
+ if (Array.isArray(raw)) entries = raw;
380
+ else if (isPlainObject(raw)) entries = [raw];
381
+ else entries = [];
382
+ return [event, entries];
383
+ });
384
+ }
385
+
386
+ /** (A) Legacy / orphan hook events — AgileFlow entries in events we no longer manage. */
387
+ function detectLegacyHookEvents(ctx) {
388
+ const issues = [];
389
+ for (const [event, entries] of iterHookEvents(ctx.settings)) {
390
+ const ours = entries.filter(isAgileflowEntry);
391
+ if (ours.length === 0) continue;
392
+ if (LEGACY_MANAGED_EVENTS.has(event)) {
393
+ issues.push({
394
+ severity: "warn",
395
+ kind: "legacy-hook-event",
396
+ path: `${ctx.settingsPath}#hooks.${event}`,
397
+ message: `Legacy hook event "${event}" in .claude/settings.json — AgileFlow used to write this, no longer does. Safe to remove ${ours.length} entry/entries.`,
398
+ });
399
+ } else if (!MANAGED_EVENTS.has(event)) {
400
+ issues.push({
401
+ severity: "warn",
402
+ kind: "orphan-hook-event",
403
+ path: `${ctx.settingsPath}#hooks.${event}`,
404
+ message: `Unknown event "${event}" in .claude/settings.json contains \`${HOOK_COMMAND_MARKER}\` command — not a current AgileFlow event. Probably from a much older install.`,
405
+ });
406
+ }
407
+ }
408
+ return issues;
409
+ }
410
+
411
+ /** (F) Broken hook commands — entries whose `command` references a missing file. */
412
+ function detectBrokenHookCommands(ctx) {
413
+ const issues = [];
414
+ for (const [event, entries] of iterHookEvents(ctx.settings)) {
415
+ for (const entry of entries) {
416
+ if (!entry || !Array.isArray(entry.hooks)) continue;
417
+ for (const h of entry.hooks) {
418
+ if (!h || h.type !== "command" || typeof h.command !== "string") {
419
+ continue;
420
+ }
421
+ const missingPath = brokenScriptInCommand(h.command, ctx.cwd);
422
+ if (!missingPath) continue;
423
+ issues.push({
424
+ severity: "error",
425
+ kind: "broken-hook-command",
426
+ path: `${ctx.settingsPath}#hooks.${event}`,
427
+ event,
428
+ command: h.command,
429
+ missingPath,
430
+ message: `Hook in .claude/settings.json#hooks.${event} references missing file: ${missingPath} (command: \`${h.command}\`)`,
431
+ });
432
+ }
433
+ }
434
+ }
435
+ return issues;
436
+ }
437
+
438
+ /** (B) v3-era directories and files under `.agileflow/`. */
439
+ function detectLegacyAgileflowPaths(ctx) {
440
+ const issues = [];
441
+ if (!fs.existsSync(ctx.agileflowDir)) return issues;
442
+ for (const name of LEGACY_AGILEFLOW_SUBDIRS) {
443
+ const p = path.join(ctx.agileflowDir, name);
444
+ if (fs.existsSync(p)) {
445
+ issues.push({
446
+ severity: "warn",
447
+ kind: "legacy-agileflow-subdir",
448
+ path: p,
449
+ message: `v3 directory \`.agileflow/${name}/\` present — v4 (skills-first) doesn't use this. Safe to delete unless you've kept custom content.`,
450
+ });
451
+ }
452
+ }
453
+ for (const name of LEGACY_AGILEFLOW_FILES) {
454
+ const p = path.join(ctx.agileflowDir, name);
455
+ if (fs.existsSync(p)) {
456
+ issues.push({
457
+ severity: "warn",
458
+ kind: "legacy-agileflow-file",
459
+ path: p,
460
+ message: `v3 file \`.agileflow/${name}\` present — not written by v4.`,
461
+ });
462
+ }
463
+ }
464
+ return issues;
465
+ }
466
+
467
+ /** (C) v3-era directories under `.claude/` — flagged only if they hold agileflow-* entries. */
468
+ function detectLegacyClaudeSubdirs(ctx) {
469
+ const issues = [];
470
+ if (!fs.existsSync(ctx.claudeDir)) return issues;
471
+ for (const name of LEGACY_CLAUDE_SUBDIRS) {
472
+ const p = path.join(ctx.claudeDir, name);
473
+ if (!fs.existsSync(p)) continue;
474
+ const agileflowOwned = readdirSafe(p).filter(
475
+ (e) => e.startsWith("agileflow") || e.startsWith("AgileFlow"),
476
+ );
477
+ if (agileflowOwned.length === 0) continue;
478
+ issues.push({
479
+ severity: "warn",
480
+ kind: "legacy-claude-subdir",
481
+ path: p,
482
+ message: `v3 \`.claude/${name}/\` contains ${agileflowOwned.length} AgileFlow item(s) — v4 ships skills only; this dir isn't used.`,
483
+ });
484
+ }
485
+ return issues;
486
+ }
487
+
488
+ /**
489
+ * (D) Broken hook-manifest script references. YAML parse failures fall
490
+ * through silently — they're reported separately by
491
+ * `validateInstalledManifest()`, so we just skip the walk on parse fail.
492
+ */
493
+ async function detectBrokenHookScripts(ctx) {
494
+ const issues = [];
495
+ if (!fs.existsSync(ctx.manifestPath)) return issues;
496
+ /** @type {{ hooks?: Array<{id: string, event: string, script: string}> } | null} */
497
+ let manifest = null;
498
+ try {
499
+ manifest = await loadHookManifest(ctx.manifestPath);
500
+ } catch {
501
+ return issues;
502
+ }
503
+ if (!manifest || !Array.isArray(manifest.hooks)) return issues;
504
+ for (const h of manifest.hooks) {
505
+ if (!h || typeof h.script !== "string") continue;
506
+ const scriptPath = path.isAbsolute(h.script)
507
+ ? h.script
508
+ : path.join(ctx.cwd, h.script);
509
+ if (!fs.existsSync(scriptPath)) {
510
+ issues.push({
511
+ severity: "error",
512
+ kind: "broken-hook-script",
513
+ path: scriptPath,
514
+ message: `Hook "${h.id}" (${h.event}) points at missing script ${h.script}`,
515
+ });
516
+ }
517
+ }
518
+ return issues;
519
+ }
520
+
521
+ /** (E) Orphan skill dirs — installed agileflow-* skills not claimed by an enabled plugin. */
522
+ async function detectOrphanSkillDirs(ctx) {
523
+ const issues = [];
524
+ // Guard against malformed plugins fields: string, array, primitive.
525
+ // Object.entries on a string returns char-indexed entries; on an
526
+ // array it returns numeric-indexed ones — both produce garbage.
527
+ if (!ctx.config || !isPlainObject(ctx.config.plugins)) return issues;
528
+ const enabled = Object.entries(ctx.config.plugins)
529
+ .filter(([, v]) => v && v.enabled !== false)
530
+ .map(([k]) => k);
531
+ // De-dupe: don't push "core" if config already has it enabled.
532
+ if (!enabled.includes("core")) enabled.push("core");
533
+ /** @type {Set<string>} */
534
+ const expectedSkillIds = new Set();
535
+ for (const p of discoverPlugins()) {
536
+ if (!enabled.includes(p.id)) continue;
537
+ const skillsRoot = path.join(p.dir, "skills");
538
+ if (!fs.existsSync(skillsRoot)) continue;
539
+ for (const entry of readdirSafe(skillsRoot)) {
540
+ const skillFile = path.join(skillsRoot, entry, "SKILL.md");
541
+ if (!fs.existsSync(skillFile)) continue;
542
+ // loadSkill returns { skillId, frontmatter, body, ... } or throws.
543
+ // Either way, claim the dir name so we don't false-positive on a
544
+ // bundled skill.
545
+ let id = entry;
546
+ try {
547
+ const s = await loadSkill(skillFile);
548
+ id = (s && s.frontmatter && s.frontmatter.name) || s.skillId || entry;
549
+ } catch {
550
+ // unparseable bundled skill — still treat dir as owned
551
+ }
552
+ expectedSkillIds.add(id);
553
+ }
554
+ }
555
+ const skillsDir = path.join(ctx.claudeDir, "skills");
556
+ if (!fs.existsSync(skillsDir)) return issues;
557
+ for (const entry of readdirSafe(skillsDir)) {
558
+ if (!entry.startsWith("agileflow")) continue;
559
+ if (expectedSkillIds.has(entry)) continue;
560
+ issues.push({
561
+ severity: "warn",
562
+ kind: "orphan-skill-dir",
563
+ path: path.join(skillsDir, entry),
564
+ message: `Orphan skill \`.claude/skills/${entry}/\` — not owned by any enabled plugin. Likely from a disabled or removed plugin.`,
565
+ });
566
+ }
567
+ return issues;
568
+ }
569
+
570
+ /**
571
+ * Parse a `<settingsPath>#hooks.<event>` issue.path. Returns null if
572
+ * the marker is missing.
573
+ *
574
+ * @param {string} issuePath
575
+ * @returns {{settingsPath: string, event: string} | null}
576
+ */
577
+ function parseHookEventPath(issuePath) {
578
+ const hashIdx = (issuePath || "").lastIndexOf("#hooks.");
579
+ if (hashIdx < 0) return null;
580
+ return {
581
+ settingsPath: issuePath.slice(0, hashIdx),
582
+ event: issuePath.slice(hashIdx + "#hooks.".length),
583
+ };
584
+ }
585
+
586
+ /**
587
+ * Persist a filtered hooks object back to settings.json, deleting the
588
+ * event entirely if no entries remain and dropping the hooks key if
589
+ * empty. Returns the relative .claude/settings.json#hooks.<event>
590
+ * string for use in success messages.
591
+ */
592
+ function writeFilteredHooks(settings, settingsPath, event, kept) {
593
+ if (kept.length === 0) delete settings.hooks[event];
594
+ else settings.hooks[event] = kept;
595
+ if (Object.keys(settings.hooks).length === 0) delete settings.hooks;
596
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
597
+ }
598
+
599
+ /** Fix: strip AgileFlow entries from a hook event, preserving user entries. */
600
+ function fixHookEventEntry(issue) {
601
+ const parsed = parseHookEventPath(issue.path);
602
+ if (!parsed) {
603
+ return {
604
+ ok: false,
605
+ message: "Malformed issue.path; expected #hooks.<event>",
606
+ };
607
+ }
608
+ const { settingsPath, event } = parsed;
609
+ const settings = readJSONSafe(settingsPath);
610
+ if (!settings || !isPlainObject(settings.hooks)) {
611
+ return { ok: false, message: `No hooks object in ${settingsPath}` };
612
+ }
613
+ const raw = settings.hooks[event];
614
+ const entries = Array.isArray(raw) ? raw : isPlainObject(raw) ? [raw] : [];
615
+ const userEntries = entries.filter((e) => !isAgileflowEntry(e));
616
+ writeFilteredHooks(settings, settingsPath, event, userEntries);
617
+ return {
618
+ ok: true,
619
+ message: `Removed AgileFlow entries from .claude/settings.json#hooks.${event}`,
620
+ };
621
+ }
622
+
623
+ /** Fix: recursively remove a directory artifact. */
624
+ function fixDirectoryRemoval(issue, ctx) {
625
+ if (!issue.path || !fs.existsSync(issue.path)) {
626
+ return { ok: false, message: `Path missing: ${issue.path}` };
627
+ }
628
+ fs.rmSync(issue.path, { recursive: true, force: true });
629
+ return { ok: true, message: `Removed ${path.relative(ctx.cwd, issue.path)}` };
630
+ }
631
+
632
+ /** Fix: unlink a file artifact. */
633
+ function fixFileRemoval(issue, ctx) {
634
+ if (!issue.path || !fs.existsSync(issue.path)) {
635
+ return { ok: false, message: `Path missing: ${issue.path}` };
636
+ }
637
+ fs.unlinkSync(issue.path);
638
+ return { ok: true, message: `Removed ${path.relative(ctx.cwd, issue.path)}` };
639
+ }
640
+
641
+ /**
642
+ * Fix: delete agileflow-* children of a legacy .claude/ subdir, leaving
643
+ * user files alone. Empty parent dir is cleaned up best-effort.
644
+ */
645
+ function fixClaudeSubdirSelective(issue, ctx) {
646
+ if (!issue.path || !fs.existsSync(issue.path)) {
647
+ return { ok: false, message: `Path missing: ${issue.path}` };
648
+ }
649
+ let removed = 0;
650
+ for (const entry of readdirSafe(issue.path)) {
651
+ if (!entry.startsWith("agileflow") && !entry.startsWith("AgileFlow"))
652
+ continue;
653
+ const p = path.join(issue.path, entry);
654
+ fs.rmSync(p, { recursive: true, force: true });
655
+ removed += 1;
656
+ }
657
+ // Empty-dir cleanup — fail silently if dir has other content.
658
+ try {
659
+ fs.rmdirSync(issue.path);
660
+ } catch {
661
+ /* dir not empty (user files present); leave it */
662
+ }
663
+ return {
664
+ ok: true,
665
+ message: `Removed ${removed} AgileFlow item(s) from ${path.relative(ctx.cwd, issue.path)}`,
666
+ };
667
+ }
668
+
669
+ /** Fix: broken hook script — not auto-fixable; the script file itself is gone. */
670
+ function fixBrokenHookScriptStub() {
671
+ return {
672
+ ok: false,
673
+ message:
674
+ "Cannot auto-fix — the script file is gone. Run `agileflow update` to reinstall plugin scripts.",
675
+ };
676
+ }
677
+
678
+ /** Fix: strip a hook entry whose command matches a missing path, preserving siblings. */
679
+ function fixBrokenHookCommand(issue) {
680
+ const parsed = parseHookEventPath(issue.path);
681
+ if (!parsed || !issue.command) {
682
+ return {
683
+ ok: false,
684
+ message: "Malformed issue — expected path and command",
685
+ };
686
+ }
687
+ const { settingsPath, event } = parsed;
688
+ const settings = readJSONSafe(settingsPath);
689
+ if (!settings || !isPlainObject(settings.hooks)) {
690
+ return { ok: false, message: `No hooks object in ${settingsPath}` };
691
+ }
692
+ const raw = settings.hooks[event];
693
+ const entries = Array.isArray(raw) ? raw : isPlainObject(raw) ? [raw] : [];
694
+ const kept = entries.filter((e) => {
695
+ if (!e || !Array.isArray(e.hooks)) return true;
696
+ // Drop entry if ANY of its hooks matches the broken command.
697
+ return !e.hooks.some(
698
+ (h) => h && h.type === "command" && h.command === issue.command,
699
+ );
700
+ });
701
+ writeFilteredHooks(settings, settingsPath, event, kept);
702
+ return {
703
+ ok: true,
704
+ message: `Removed broken hook from .claude/settings.json#hooks.${event} (command: ${issue.command})`,
705
+ };
706
+ }
707
+
708
+ /**
709
+ * Registry of stale-artifact handlers. Adding a new kind: append one
710
+ * entry with a detect function returning issues and a fix function
711
+ * returning {ok, message}. `handles` lists every issue.kind this entry
712
+ * accepts (most singular; a pair when one fix covers two kinds).
713
+ */
714
+ const STALE_DETECTORS = [
715
+ {
716
+ detect: detectLegacyHookEvents,
717
+ fix: fixHookEventEntry,
718
+ handles: new Set(["legacy-hook-event", "orphan-hook-event"]),
719
+ },
720
+ {
721
+ detect: detectBrokenHookCommands,
722
+ fix: fixBrokenHookCommand,
723
+ handles: new Set(["broken-hook-command"]),
724
+ },
725
+ {
726
+ detect: detectLegacyAgileflowPaths,
727
+ // Split between two fixers based on issue.kind; the runner picks
728
+ // by handles, so we declare them separately below for clarity.
729
+ fix: (issue, ctx) =>
730
+ issue.kind === "legacy-agileflow-file"
731
+ ? fixFileRemoval(issue, ctx)
732
+ : fixDirectoryRemoval(issue, ctx),
733
+ handles: new Set(["legacy-agileflow-subdir", "legacy-agileflow-file"]),
734
+ },
735
+ {
736
+ detect: detectLegacyClaudeSubdirs,
737
+ fix: fixClaudeSubdirSelective,
738
+ handles: new Set(["legacy-claude-subdir"]),
739
+ },
740
+ {
741
+ detect: detectBrokenHookScripts,
742
+ fix: fixBrokenHookScriptStub,
743
+ handles: new Set(["broken-hook-script"]),
744
+ },
745
+ {
746
+ detect: detectOrphanSkillDirs,
747
+ fix: fixDirectoryRemoval,
748
+ handles: new Set(["orphan-skill-dir"]),
749
+ },
750
+ ];
751
+
752
+ /**
753
+ * Detect stale AgileFlow artifacts left over from older versions, plugin
754
+ * renames, or aborted installs. Each issue identifies a path or hook
755
+ * entry that update wouldn't currently sweep.
756
+ *
757
+ * Diagnose-only: never modifies anything. Always returns an array — IO
758
+ * failures are caught and either yield an issue or are skipped (never
759
+ * thrown to the caller).
760
+ *
761
+ * Issue shape: { severity: "warn"|"error", kind: string, path?: string, message: string }
762
+ *
763
+ * @param {string} cwd
764
+ * @returns {Promise<Array<{severity: string, kind: string, path?: string, message: string}>>}
765
+ */
766
+ async function checkStaleArtifacts(cwd) {
767
+ const ctx = buildContext(cwd);
768
+ const issues = [];
769
+ for (const entry of STALE_DETECTORS) {
770
+ issues.push(...(await entry.detect(ctx)));
771
+ }
772
+ return issues;
773
+ }
774
+
775
+ /**
776
+ * Apply a single stale-artifact fix. Hook-event entries are stripped
777
+ * from settings.json (preserving user entries in that event); fs
778
+ * artifacts are removed. Broken hook scripts cannot be auto-fixed —
779
+ * the script file itself is gone.
780
+ *
781
+ * @param {{kind: string, path?: string, message: string}} issue
782
+ * @param {string} cwd
783
+ * @returns {{ok: boolean, message: string}}
784
+ */
785
+ function applyStaleFix(issue, cwd) {
786
+ const entry = STALE_DETECTORS.find((d) => d.handles.has(issue.kind));
787
+ if (!entry) {
788
+ return { ok: false, message: `Unknown issue kind: ${issue.kind}` };
789
+ }
790
+ return entry.fix(issue, buildContext(cwd));
791
+ }
792
+
793
+ /**
794
+ * `agileflow doctor --fix` body. Detects stale artifacts (same as
795
+ * `doctor`), then either previews removal (`--fix` alone) or executes
796
+ * (`--fix --yes`). Returns counts for testing.
797
+ *
798
+ * @param {string} cwd
799
+ * @param {{yes?: boolean, log?: (msg: string) => void}} [opts]
800
+ * @returns {Promise<{detected: number, fixed: number, failed: number, dryRun: boolean}>}
801
+ */
802
+ async function doctorFix(cwd, opts = {}) {
803
+ const log = opts.log || ((m) => console.log(m)); // eslint-disable-line no-console
804
+ const issues = await checkStaleArtifacts(cwd);
805
+ if (issues.length === 0) {
806
+ log("Stale artifacts: ok — nothing to fix.");
807
+ return { detected: 0, fixed: 0, failed: 0, dryRun: !opts.yes };
808
+ }
809
+ if (!opts.yes) {
810
+ log(
811
+ `\n${issues.length} stale artifact(s) — dry-run preview (use --yes to actually remove):\n`,
812
+ );
813
+ for (const issue of issues) {
814
+ log(` • [${issue.kind}] ${issue.message}`);
815
+ if (issue.path) log(` ${issue.path}`);
816
+ }
817
+ log("\n Re-run with `agileflow doctor --fix --yes` to apply.");
818
+ return { detected: issues.length, fixed: 0, failed: 0, dryRun: true };
819
+ }
820
+ let fixed = 0;
821
+ let failed = 0;
822
+ log(`\nApplying fixes for ${issues.length} stale artifact(s):\n`);
823
+ for (const issue of issues) {
824
+ // Guard against synchronous throws from fs.rmSync / unlinkSync on
825
+ // EPERM/EBUSY/race-deleted paths — otherwise a mid-loop throw
826
+ // bypasses the final summary log line.
827
+ try {
828
+ const r = applyStaleFix(issue, cwd);
829
+ if (r.ok) {
830
+ fixed += 1;
831
+ log(` ✓ ${r.message}`);
832
+ } else {
833
+ failed += 1;
834
+ log(` ✗ [${issue.kind}] ${r.message}`);
835
+ }
836
+ } catch (err) {
837
+ failed += 1;
838
+ log(` ✗ [${issue.kind}] threw: ${err.message}`);
839
+ }
840
+ }
841
+ log(`\n ${fixed} fixed, ${failed} skipped/failed.`);
842
+ return { detected: issues.length, fixed, failed, dryRun: false };
843
+ }
844
+
845
+ async function doctor(opts = {}) {
846
+ if (opts && opts.fix) {
847
+ const cwd = process.cwd();
848
+ const r = await doctorFix(cwd, { yes: !!opts.yes });
849
+ if (r.failed > 0) process.exit(1);
850
+ return;
851
+ }
121
852
  const cwd = process.cwd();
122
853
  let totalErrors = 0;
123
854
 
124
855
  // 1. Plugin manifests (every bundled plugin.yaml).
125
856
  const plugins = discoverPlugins();
126
857
  const pluginIssues = validatePluginSet(plugins);
127
- totalErrors += printSection('Plugin manifests:', pluginIssues);
858
+ totalErrors += printSection("Plugin manifests:", pluginIssues);
128
859
 
129
860
  // 2. Skills (every SKILL.md across every bundled plugin).
130
861
  const { issues: skillIssues } = await validateBundledSkills();
131
- totalErrors += printSection('Skills:', skillIssues);
862
+ totalErrors += printSection(
863
+ "Skills:",
864
+ skillIssues.filter(
865
+ (i) =>
866
+ (i.severity !== "warn" && i.severity !== "warning") ||
867
+ !i.message.includes("_learnings"),
868
+ ),
869
+ );
132
870
 
133
871
  // 3. Aggregated hook manifest (what install would write).
134
872
  const aggrErrors = await validateAggregatedHookManifest();
135
873
  totalErrors += printSection(
136
- 'Hook manifest (aggregated):',
137
- aggrErrors.map((m) => ({ severity: 'error', message: m })),
874
+ "Hook manifest (aggregated):",
875
+ aggrErrors.map((m) => ({ severity: "error", message: m })),
138
876
  );
139
877
 
140
878
  // 4. Installed hook manifest in this cwd (if any).
141
879
  const installedErrors = await validateInstalledManifest(cwd);
142
880
  totalErrors += printSection(
143
- 'Hook manifest (installed):',
144
- installedErrors.map((m) => ({ severity: 'error', message: m })),
881
+ "Hook manifest (installed):",
882
+ installedErrors.map((m) => ({ severity: "error", message: m })),
145
883
  );
146
884
 
885
+ // 5. Stale artifacts from older versions / aborted installs.
886
+ const staleIssues = await checkStaleArtifacts(cwd);
887
+ // eslint-disable-next-line no-console
888
+ console.log("\nStale artifacts:");
889
+ if (staleIssues.length === 0) {
890
+ // eslint-disable-next-line no-console
891
+ console.log(" ok");
892
+ } else {
893
+ for (const issue of staleIssues) {
894
+ const label = issue.severity === "error" ? "ERROR" : "WARN ";
895
+ if (issue.severity === "error") totalErrors += 1;
896
+ // eslint-disable-next-line no-console
897
+ console.log(` ${label} ${issue.message}`);
898
+ if (issue.path) {
899
+ // eslint-disable-next-line no-console
900
+ console.log(` ${issue.path}`);
901
+ }
902
+ }
903
+ // eslint-disable-next-line no-console
904
+ console.log(
905
+ `\n ${staleIssues.length} stale artifact(s). Run \`agileflow doctor --fix\` to preview removal, or \`--fix --yes\` to apply.`,
906
+ );
907
+ }
908
+
909
+ // 6. Installation health — skills + hooks in the current project.
910
+ const installIssues = await checkInstallHealth(cwd);
911
+ // eslint-disable-next-line no-console
912
+ console.log("\nInstallation health:");
913
+ if (installIssues.length === 0) {
914
+ // eslint-disable-next-line no-console
915
+ console.log(" ok");
916
+ } else {
917
+ for (const issue of installIssues) {
918
+ if (issue.severity === "info") {
919
+ // eslint-disable-next-line no-console
920
+ console.log(` ✓ ${issue.message}`);
921
+ } else if (issue.severity === "warn") {
922
+ // eslint-disable-next-line no-console
923
+ console.log(` WARN ${issue.message}`);
924
+ } else {
925
+ totalErrors += 1;
926
+ // eslint-disable-next-line no-console
927
+ console.log(` ERROR ${issue.message}`);
928
+ }
929
+ }
930
+ }
931
+
147
932
  // eslint-disable-next-line no-console
148
- console.log('');
933
+ console.log("");
149
934
  if (totalErrors === 0) {
150
935
  // eslint-disable-next-line no-console
151
- console.log('✓ doctor: all checks passed');
936
+ console.log("✓ doctor: all checks passed");
152
937
  return;
153
938
  }
154
939
  // eslint-disable-next-line no-console
@@ -157,3 +942,6 @@ async function doctor() {
157
942
  }
158
943
 
159
944
  module.exports = doctor;
945
+ module.exports.checkStaleArtifacts = checkStaleArtifacts;
946
+ module.exports.applyStaleFix = applyStaleFix;
947
+ module.exports.doctorFix = doctorFix;