@planu/cli 0.89.0 → 0.90.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (446) hide show
  1. package/dist/cli/commands/activate.d.ts +14 -0
  2. package/dist/cli/commands/activate.d.ts.map +1 -0
  3. package/dist/cli/commands/activate.js +174 -0
  4. package/dist/cli/commands/activate.js.map +1 -0
  5. package/dist/cli/commands/doctor.d.ts +16 -0
  6. package/dist/cli/commands/doctor.d.ts.map +1 -0
  7. package/dist/cli/commands/doctor.js +162 -0
  8. package/dist/cli/commands/doctor.js.map +1 -0
  9. package/dist/cli/commands/install.d.ts +48 -0
  10. package/dist/cli/commands/install.d.ts.map +1 -0
  11. package/dist/cli/commands/install.js +348 -0
  12. package/dist/cli/commands/install.js.map +1 -0
  13. package/dist/cli/commands/uninstall.d.ts +10 -0
  14. package/dist/cli/commands/uninstall.d.ts.map +1 -0
  15. package/dist/cli/commands/uninstall.js +133 -0
  16. package/dist/cli/commands/uninstall.js.map +1 -0
  17. package/dist/cli/router.d.ts.map +1 -1
  18. package/dist/cli/router.js +9 -1
  19. package/dist/cli/router.js.map +1 -1
  20. package/dist/config/license-plans.json +2 -1
  21. package/dist/engine/agent-generator.test.d.ts +2 -0
  22. package/dist/engine/agent-generator.test.d.ts.map +1 -0
  23. package/dist/engine/agent-generator.test.js +556 -0
  24. package/dist/engine/agent-generator.test.js.map +1 -0
  25. package/dist/engine/analyzer.test.d.ts +2 -0
  26. package/dist/engine/analyzer.test.d.ts.map +1 -0
  27. package/dist/engine/analyzer.test.js +1461 -0
  28. package/dist/engine/analyzer.test.js.map +1 -0
  29. package/dist/engine/auditor.test.d.ts +2 -0
  30. package/dist/engine/auditor.test.d.ts.map +1 -0
  31. package/dist/engine/auditor.test.js +2075 -0
  32. package/dist/engine/auditor.test.js.map +1 -0
  33. package/dist/engine/doc-generator.test.d.ts +2 -0
  34. package/dist/engine/doc-generator.test.d.ts.map +1 -0
  35. package/dist/engine/doc-generator.test.js +961 -0
  36. package/dist/engine/doc-generator.test.js.map +1 -0
  37. package/dist/engine/estimator.test.d.ts +2 -0
  38. package/dist/engine/estimator.test.d.ts.map +1 -0
  39. package/dist/engine/estimator.test.js +334 -0
  40. package/dist/engine/estimator.test.js.map +1 -0
  41. package/dist/engine/skill-generator.test.d.ts +2 -0
  42. package/dist/engine/skill-generator.test.d.ts.map +1 -0
  43. package/dist/engine/skill-generator.test.js +742 -0
  44. package/dist/engine/skill-generator.test.js.map +1 -0
  45. package/dist/engine/spec-migrator/filesystem-import.d.ts +14 -0
  46. package/dist/engine/spec-migrator/filesystem-import.d.ts.map +1 -0
  47. package/dist/engine/spec-migrator/filesystem-import.js +96 -0
  48. package/dist/engine/spec-migrator/filesystem-import.js.map +1 -0
  49. package/dist/engine/spec-migrator/flatten-specs.d.ts +12 -0
  50. package/dist/engine/spec-migrator/flatten-specs.d.ts.map +1 -0
  51. package/dist/engine/spec-migrator/flatten-specs.js +111 -0
  52. package/dist/engine/spec-migrator/flatten-specs.js.map +1 -0
  53. package/dist/engine/spec-migrator/folder-operations.d.ts +9 -0
  54. package/dist/engine/spec-migrator/folder-operations.d.ts.map +1 -0
  55. package/dist/engine/spec-migrator/folder-operations.js +109 -0
  56. package/dist/engine/spec-migrator/folder-operations.js.map +1 -0
  57. package/dist/engine/spec-migrator/frontmatter-parser.d.ts +11 -0
  58. package/dist/engine/spec-migrator/frontmatter-parser.d.ts.map +1 -0
  59. package/dist/engine/spec-migrator/frontmatter-parser.js +92 -0
  60. package/dist/engine/spec-migrator/frontmatter-parser.js.map +1 -0
  61. package/dist/engine/spec-migrator/index.d.ts +9 -0
  62. package/dist/engine/spec-migrator/index.d.ts.map +1 -0
  63. package/dist/engine/spec-migrator/index.js +18 -0
  64. package/dist/engine/spec-migrator/index.js.map +1 -0
  65. package/dist/engine/spec-migrator/legacy-migration.d.ts +13 -0
  66. package/dist/engine/spec-migrator/legacy-migration.d.ts.map +1 -0
  67. package/dist/engine/spec-migrator/legacy-migration.js +75 -0
  68. package/dist/engine/spec-migrator/legacy-migration.js.map +1 -0
  69. package/dist/engine/spec-migrator/migration-validator.d.ts +20 -0
  70. package/dist/engine/spec-migrator/migration-validator.d.ts.map +1 -0
  71. package/dist/engine/spec-migrator/migration-validator.js +35 -0
  72. package/dist/engine/spec-migrator/migration-validator.js.map +1 -0
  73. package/dist/engine/spec-migrator/path-utils.d.ts +13 -0
  74. package/dist/engine/spec-migrator/path-utils.d.ts.map +1 -0
  75. package/dist/engine/spec-migrator/path-utils.js +40 -0
  76. package/dist/engine/spec-migrator/path-utils.js.map +1 -0
  77. package/dist/engine/spec-migrator/prefix-migration.d.ts +11 -0
  78. package/dist/engine/spec-migrator/prefix-migration.d.ts.map +1 -0
  79. package/dist/engine/spec-migrator/prefix-migration.js +73 -0
  80. package/dist/engine/spec-migrator/prefix-migration.js.map +1 -0
  81. package/dist/engine/spec-migrator/reconcile-paths.d.ts +12 -0
  82. package/dist/engine/spec-migrator/reconcile-paths.d.ts.map +1 -0
  83. package/dist/engine/spec-migrator/reconcile-paths.js +77 -0
  84. package/dist/engine/spec-migrator/reconcile-paths.js.map +1 -0
  85. package/dist/engine/spec-migrator/version-detection.d.ts +5 -0
  86. package/dist/engine/spec-migrator/version-detection.d.ts.map +1 -0
  87. package/dist/engine/spec-migrator/version-detection.js +19 -0
  88. package/dist/engine/spec-migrator/version-detection.js.map +1 -0
  89. package/dist/engine/spec-migrator.d.ts +1 -58
  90. package/dist/engine/spec-migrator.d.ts.map +1 -1
  91. package/dist/engine/spec-migrator.js +2 -658
  92. package/dist/engine/spec-migrator.js.map +1 -1
  93. package/dist/engine/spec-summary-html/dashboard-renderer.d.ts +6 -0
  94. package/dist/engine/spec-summary-html/dashboard-renderer.d.ts.map +1 -0
  95. package/dist/engine/spec-summary-html/dashboard-renderer.js +333 -0
  96. package/dist/engine/spec-summary-html/dashboard-renderer.js.map +1 -0
  97. package/dist/engine/spec-summary-html/hash-utils.d.ts +11 -0
  98. package/dist/engine/spec-summary-html/hash-utils.d.ts.map +1 -0
  99. package/dist/engine/spec-summary-html/hash-utils.js +39 -0
  100. package/dist/engine/spec-summary-html/hash-utils.js.map +1 -0
  101. package/dist/engine/spec-summary-html/index.d.ts +4 -0
  102. package/dist/engine/spec-summary-html/index.d.ts.map +1 -0
  103. package/dist/engine/spec-summary-html/index.js +6 -0
  104. package/dist/engine/spec-summary-html/index.js.map +1 -0
  105. package/dist/engine/spec-summary-html/report-renderer.d.ts +9 -0
  106. package/dist/engine/spec-summary-html/report-renderer.d.ts.map +1 -0
  107. package/dist/engine/spec-summary-html/report-renderer.js +139 -0
  108. package/dist/engine/spec-summary-html/report-renderer.js.map +1 -0
  109. package/dist/engine/spec-summary-html.d.ts +1 -0
  110. package/dist/engine/spec-summary-html.d.ts.map +1 -1
  111. package/dist/engine/spec-summary-html.js +19 -473
  112. package/dist/engine/spec-summary-html.js.map +1 -1
  113. package/dist/engine/update-notifier.d.ts +8 -0
  114. package/dist/engine/update-notifier.d.ts.map +1 -0
  115. package/dist/engine/update-notifier.js +130 -0
  116. package/dist/engine/update-notifier.js.map +1 -0
  117. package/dist/engine/validator/dor-dod.d.ts.map +1 -1
  118. package/dist/engine/validator/dor-dod.js +8 -5
  119. package/dist/engine/validator/dor-dod.js.map +1 -1
  120. package/dist/engine/validator.d.ts.map +1 -1
  121. package/dist/engine/validator.js +4 -3
  122. package/dist/engine/validator.js.map +1 -1
  123. package/dist/engine/validator.test.d.ts +2 -0
  124. package/dist/engine/validator.test.d.ts.map +1 -0
  125. package/dist/engine/validator.test.js +2371 -0
  126. package/dist/engine/validator.test.js.map +1 -0
  127. package/dist/engine/web-fetcher.test.d.ts +2 -0
  128. package/dist/engine/web-fetcher.test.d.ts.map +1 -0
  129. package/dist/engine/web-fetcher.test.js +360 -0
  130. package/dist/engine/web-fetcher.test.js.map +1 -0
  131. package/dist/i18n/index.test.d.ts +2 -0
  132. package/dist/i18n/index.test.d.ts.map +1 -0
  133. package/dist/i18n/index.test.js +375 -0
  134. package/dist/i18n/index.test.js.map +1 -0
  135. package/dist/index.js +8 -0
  136. package/dist/index.js.map +1 -1
  137. package/dist/index.test.d.ts +2 -0
  138. package/dist/index.test.d.ts.map +1 -0
  139. package/dist/index.test.js +124 -0
  140. package/dist/index.test.js.map +1 -0
  141. package/dist/resources/patterns.test.d.ts +2 -0
  142. package/dist/resources/patterns.test.d.ts.map +1 -0
  143. package/dist/resources/patterns.test.js +142 -0
  144. package/dist/resources/patterns.test.js.map +1 -0
  145. package/dist/resources/process.test.d.ts +2 -0
  146. package/dist/resources/process.test.d.ts.map +1 -0
  147. package/dist/resources/process.test.js +48 -0
  148. package/dist/resources/process.test.js.map +1 -0
  149. package/dist/resources/registry.test.d.ts +2 -0
  150. package/dist/resources/registry.test.d.ts.map +1 -0
  151. package/dist/resources/registry.test.js +138 -0
  152. package/dist/resources/registry.test.js.map +1 -0
  153. package/dist/resources/specs.test.d.ts +2 -0
  154. package/dist/resources/specs.test.d.ts.map +1 -0
  155. package/dist/resources/specs.test.js +130 -0
  156. package/dist/resources/specs.test.js.map +1 -0
  157. package/dist/resources/templates.test.d.ts +2 -0
  158. package/dist/resources/templates.test.d.ts.map +1 -0
  159. package/dist/resources/templates.test.js +119 -0
  160. package/dist/resources/templates.test.js.map +1 -0
  161. package/dist/smoke.test.d.ts +2 -0
  162. package/dist/smoke.test.d.ts.map +1 -0
  163. package/dist/smoke.test.js +229 -0
  164. package/dist/smoke.test.js.map +1 -0
  165. package/dist/storage/base-store.test.d.ts +2 -0
  166. package/dist/storage/base-store.test.d.ts.map +1 -0
  167. package/dist/storage/base-store.test.js +180 -0
  168. package/dist/storage/base-store.test.js.map +1 -0
  169. package/dist/storage/global-store.test.d.ts +2 -0
  170. package/dist/storage/global-store.test.d.ts.map +1 -0
  171. package/dist/storage/global-store.test.js +327 -0
  172. package/dist/storage/global-store.test.js.map +1 -0
  173. package/dist/storage/index.test.d.ts +2 -0
  174. package/dist/storage/index.test.d.ts.map +1 -0
  175. package/dist/storage/index.test.js +56 -0
  176. package/dist/storage/index.test.js.map +1 -0
  177. package/dist/storage/knowledge-store.test.d.ts +2 -0
  178. package/dist/storage/knowledge-store.test.d.ts.map +1 -0
  179. package/dist/storage/knowledge-store.test.js +368 -0
  180. package/dist/storage/knowledge-store.test.js.map +1 -0
  181. package/dist/storage/metrics-store.test.d.ts +2 -0
  182. package/dist/storage/metrics-store.test.d.ts.map +1 -0
  183. package/dist/storage/metrics-store.test.js +212 -0
  184. package/dist/storage/metrics-store.test.js.map +1 -0
  185. package/dist/storage/pattern-store.test.d.ts +2 -0
  186. package/dist/storage/pattern-store.test.d.ts.map +1 -0
  187. package/dist/storage/pattern-store.test.js +224 -0
  188. package/dist/storage/pattern-store.test.js.map +1 -0
  189. package/dist/storage/spec-store.test.d.ts +2 -0
  190. package/dist/storage/spec-store.test.d.ts.map +1 -0
  191. package/dist/storage/spec-store.test.js +227 -0
  192. package/dist/storage/spec-store.test.js.map +1 -0
  193. package/dist/tools/audit.test.d.ts +2 -0
  194. package/dist/tools/audit.test.d.ts.map +1 -0
  195. package/dist/tools/audit.test.js +169 -0
  196. package/dist/tools/audit.test.js.map +1 -0
  197. package/dist/tools/challenge-spec.test.d.ts +2 -0
  198. package/dist/tools/challenge-spec.test.d.ts.map +1 -0
  199. package/dist/tools/challenge-spec.test.js +782 -0
  200. package/dist/tools/challenge-spec.test.js.map +1 -0
  201. package/dist/tools/check-versions.test.d.ts +2 -0
  202. package/dist/tools/check-versions.test.d.ts.map +1 -0
  203. package/dist/tools/check-versions.test.js +214 -0
  204. package/dist/tools/check-versions.test.js.map +1 -0
  205. package/dist/tools/clarify-requirements.test.d.ts +2 -0
  206. package/dist/tools/clarify-requirements.test.d.ts.map +1 -0
  207. package/dist/tools/clarify-requirements.test.js +161 -0
  208. package/dist/tools/clarify-requirements.test.js.map +1 -0
  209. package/dist/tools/consult-docs.test.d.ts +2 -0
  210. package/dist/tools/consult-docs.test.d.ts.map +1 -0
  211. package/dist/tools/consult-docs.test.js +140 -0
  212. package/dist/tools/consult-docs.test.js.map +1 -0
  213. package/dist/tools/create-spec.test.d.ts +2 -0
  214. package/dist/tools/create-spec.test.d.ts.map +1 -0
  215. package/dist/tools/create-spec.test.js +233 -0
  216. package/dist/tools/create-spec.test.js.map +1 -0
  217. package/dist/tools/define-ui-contract.test.d.ts +2 -0
  218. package/dist/tools/define-ui-contract.test.d.ts.map +1 -0
  219. package/dist/tools/define-ui-contract.test.js +479 -0
  220. package/dist/tools/define-ui-contract.test.js.map +1 -0
  221. package/dist/tools/design-schema.test.d.ts +2 -0
  222. package/dist/tools/design-schema.test.d.ts.map +1 -0
  223. package/dist/tools/design-schema.test.js +301 -0
  224. package/dist/tools/design-schema.test.js.map +1 -0
  225. package/dist/tools/detect-agent.test.d.ts +2 -0
  226. package/dist/tools/detect-agent.test.d.ts.map +1 -0
  227. package/dist/tools/detect-agent.test.js +133 -0
  228. package/dist/tools/detect-agent.test.js.map +1 -0
  229. package/dist/tools/detect-drift.test.d.ts +2 -0
  230. package/dist/tools/detect-drift.test.d.ts.map +1 -0
  231. package/dist/tools/detect-drift.test.js +312 -0
  232. package/dist/tools/detect-drift.test.js.map +1 -0
  233. package/dist/tools/discover-mcps.test.d.ts +2 -0
  234. package/dist/tools/discover-mcps.test.d.ts.map +1 -0
  235. package/dist/tools/discover-mcps.test.js +345 -0
  236. package/dist/tools/discover-mcps.test.js.map +1 -0
  237. package/dist/tools/estimate.test.d.ts +2 -0
  238. package/dist/tools/estimate.test.d.ts.map +1 -0
  239. package/dist/tools/estimate.test.js +137 -0
  240. package/dist/tools/estimate.test.js.map +1 -0
  241. package/dist/tools/generate-adr.test.d.ts +2 -0
  242. package/dist/tools/generate-adr.test.d.ts.map +1 -0
  243. package/dist/tools/generate-adr.test.js +206 -0
  244. package/dist/tools/generate-adr.test.js.map +1 -0
  245. package/dist/tools/generate-checklist.test.d.ts +2 -0
  246. package/dist/tools/generate-checklist.test.d.ts.map +1 -0
  247. package/dist/tools/generate-checklist.test.js +201 -0
  248. package/dist/tools/generate-checklist.test.js.map +1 -0
  249. package/dist/tools/generate-docs.test.d.ts +2 -0
  250. package/dist/tools/generate-docs.test.d.ts.map +1 -0
  251. package/dist/tools/generate-docs.test.js +183 -0
  252. package/dist/tools/generate-docs.test.js.map +1 -0
  253. package/dist/tools/generate-execution-plan.test.d.ts +2 -0
  254. package/dist/tools/generate-execution-plan.test.d.ts.map +1 -0
  255. package/dist/tools/generate-execution-plan.test.js +643 -0
  256. package/dist/tools/generate-execution-plan.test.js.map +1 -0
  257. package/dist/tools/generate-rules.test.d.ts +2 -0
  258. package/dist/tools/generate-rules.test.d.ts.map +1 -0
  259. package/dist/tools/generate-rules.test.js +148 -0
  260. package/dist/tools/generate-rules.test.js.map +1 -0
  261. package/dist/tools/generate-skill.test.d.ts +2 -0
  262. package/dist/tools/generate-skill.test.d.ts.map +1 -0
  263. package/dist/tools/generate-skill.test.js +138 -0
  264. package/dist/tools/generate-skill.test.js.map +1 -0
  265. package/dist/tools/generate-sub-agent.test.d.ts +2 -0
  266. package/dist/tools/generate-sub-agent.test.d.ts.map +1 -0
  267. package/dist/tools/generate-sub-agent.test.js +162 -0
  268. package/dist/tools/generate-sub-agent.test.js.map +1 -0
  269. package/dist/tools/generate-tests.test.d.ts +2 -0
  270. package/dist/tools/generate-tests.test.d.ts.map +1 -0
  271. package/dist/tools/generate-tests.test.js +222 -0
  272. package/dist/tools/generate-tests.test.js.map +1 -0
  273. package/dist/tools/init-constitution.test.d.ts +2 -0
  274. package/dist/tools/init-constitution.test.d.ts.map +1 -0
  275. package/dist/tools/init-constitution.test.js +398 -0
  276. package/dist/tools/init-constitution.test.js.map +1 -0
  277. package/dist/tools/init-project/config-builder.d.ts +12 -0
  278. package/dist/tools/init-project/config-builder.d.ts.map +1 -0
  279. package/dist/tools/init-project/config-builder.js +31 -0
  280. package/dist/tools/init-project/config-builder.js.map +1 -0
  281. package/dist/tools/init-project/git-setup.d.ts +8 -0
  282. package/dist/tools/init-project/git-setup.d.ts.map +1 -0
  283. package/dist/tools/init-project/git-setup.js +70 -0
  284. package/dist/tools/init-project/git-setup.js.map +1 -0
  285. package/dist/tools/init-project/handler.d.ts.map +1 -1
  286. package/dist/tools/init-project/handler.js +25 -371
  287. package/dist/tools/init-project/handler.js.map +1 -1
  288. package/dist/tools/init-project/lifecycle-helpers.d.ts +32 -0
  289. package/dist/tools/init-project/lifecycle-helpers.d.ts.map +1 -0
  290. package/dist/tools/init-project/lifecycle-helpers.js +153 -0
  291. package/dist/tools/init-project/lifecycle-helpers.js.map +1 -0
  292. package/dist/tools/init-project/migration-runner.d.ts +28 -0
  293. package/dist/tools/init-project/migration-runner.d.ts.map +1 -0
  294. package/dist/tools/init-project/migration-runner.js +57 -0
  295. package/dist/tools/init-project/migration-runner.js.map +1 -0
  296. package/dist/tools/init-project/rules-writer.d.ts +14 -0
  297. package/dist/tools/init-project/rules-writer.d.ts.map +1 -0
  298. package/dist/tools/init-project/rules-writer.js +43 -0
  299. package/dist/tools/init-project/rules-writer.js.map +1 -0
  300. package/dist/tools/init-project/scaffold-writer.d.ts +29 -0
  301. package/dist/tools/init-project/scaffold-writer.d.ts.map +1 -0
  302. package/dist/tools/init-project/scaffold-writer.js +76 -0
  303. package/dist/tools/init-project/scaffold-writer.js.map +1 -0
  304. package/dist/tools/init-project/stack-detector.d.ts +16 -0
  305. package/dist/tools/init-project/stack-detector.d.ts.map +1 -0
  306. package/dist/tools/init-project/stack-detector.js +19 -0
  307. package/dist/tools/init-project/stack-detector.js.map +1 -0
  308. package/dist/tools/init-project.test.d.ts +2 -0
  309. package/dist/tools/init-project.test.d.ts.map +1 -0
  310. package/dist/tools/init-project.test.js +158 -0
  311. package/dist/tools/init-project.test.js.map +1 -0
  312. package/dist/tools/integrate-pm.test.d.ts +2 -0
  313. package/dist/tools/integrate-pm.test.d.ts.map +1 -0
  314. package/dist/tools/integrate-pm.test.js +558 -0
  315. package/dist/tools/integrate-pm.test.js.map +1 -0
  316. package/dist/tools/learn.test.d.ts +2 -0
  317. package/dist/tools/learn.test.d.ts.map +1 -0
  318. package/dist/tools/learn.test.js +123 -0
  319. package/dist/tools/learn.test.js.map +1 -0
  320. package/dist/tools/list-specs.js +1 -1
  321. package/dist/tools/list-specs.js.map +1 -1
  322. package/dist/tools/list-specs.test.d.ts +2 -0
  323. package/dist/tools/list-specs.test.d.ts.map +1 -0
  324. package/dist/tools/list-specs.test.js +110 -0
  325. package/dist/tools/list-specs.test.js.map +1 -0
  326. package/dist/tools/manage-context.test.d.ts +2 -0
  327. package/dist/tools/manage-context.test.d.ts.map +1 -0
  328. package/dist/tools/manage-context.test.js +359 -0
  329. package/dist/tools/manage-context.test.js.map +1 -0
  330. package/dist/tools/manage-git.test.d.ts +2 -0
  331. package/dist/tools/manage-git.test.d.ts.map +1 -0
  332. package/dist/tools/manage-git.test.js +882 -0
  333. package/dist/tools/manage-git.test.js.map +1 -0
  334. package/dist/tools/orchestrate.test.d.ts +2 -0
  335. package/dist/tools/orchestrate.test.d.ts.map +1 -0
  336. package/dist/tools/orchestrate.test.js +1117 -0
  337. package/dist/tools/orchestrate.test.js.map +1 -0
  338. package/dist/tools/reconcile-spec.test.d.ts +2 -0
  339. package/dist/tools/reconcile-spec.test.d.ts.map +1 -0
  340. package/dist/tools/reconcile-spec.test.js +259 -0
  341. package/dist/tools/reconcile-spec.test.js.map +1 -0
  342. package/dist/tools/red-team.d.ts +3 -0
  343. package/dist/tools/red-team.d.ts.map +1 -0
  344. package/dist/tools/red-team.js +302 -0
  345. package/dist/tools/red-team.js.map +1 -0
  346. package/dist/tools/register-platform-tools/design-stack-tools.d.ts.map +1 -1
  347. package/dist/tools/register-platform-tools/design-stack-tools.js +14 -0
  348. package/dist/tools/register-platform-tools/design-stack-tools.js.map +1 -1
  349. package/dist/tools/register-platform-tools.test.d.ts +2 -0
  350. package/dist/tools/register-platform-tools.test.d.ts.map +1 -0
  351. package/dist/tools/register-platform-tools.test.js +404 -0
  352. package/dist/tools/register-platform-tools.test.js.map +1 -0
  353. package/dist/tools/register-spec-tools.test.d.ts +2 -0
  354. package/dist/tools/register-spec-tools.test.d.ts.map +1 -0
  355. package/dist/tools/register-spec-tools.test.js +407 -0
  356. package/dist/tools/register-spec-tools.test.js.map +1 -0
  357. package/dist/tools/reverse-engineer.test.d.ts +2 -0
  358. package/dist/tools/reverse-engineer.test.d.ts.map +1 -0
  359. package/dist/tools/reverse-engineer.test.js +206 -0
  360. package/dist/tools/reverse-engineer.test.js.map +1 -0
  361. package/dist/tools/schemas.d.ts +20 -0
  362. package/dist/tools/schemas.d.ts.map +1 -0
  363. package/dist/tools/schemas.js +133 -0
  364. package/dist/tools/schemas.js.map +1 -0
  365. package/dist/tools/schemas.test.d.ts +2 -0
  366. package/dist/tools/schemas.test.d.ts.map +1 -0
  367. package/dist/tools/schemas.test.js +245 -0
  368. package/dist/tools/schemas.test.js.map +1 -0
  369. package/dist/tools/set-locale.test.d.ts +2 -0
  370. package/dist/tools/set-locale.test.d.ts.map +1 -0
  371. package/dist/tools/set-locale.test.js +74 -0
  372. package/dist/tools/set-locale.test.js.map +1 -0
  373. package/dist/tools/suggest-mcps.test.d.ts +2 -0
  374. package/dist/tools/suggest-mcps.test.d.ts.map +1 -0
  375. package/dist/tools/suggest-mcps.test.js +198 -0
  376. package/dist/tools/suggest-mcps.test.js.map +1 -0
  377. package/dist/tools/suggest-stack.test.d.ts +2 -0
  378. package/dist/tools/suggest-stack.test.d.ts.map +1 -0
  379. package/dist/tools/suggest-stack.test.js +181 -0
  380. package/dist/tools/suggest-stack.test.js.map +1 -0
  381. package/dist/tools/suggest-tooling.test.d.ts +2 -0
  382. package/dist/tools/suggest-tooling.test.d.ts.map +1 -0
  383. package/dist/tools/suggest-tooling.test.js +213 -0
  384. package/dist/tools/suggest-tooling.test.js.map +1 -0
  385. package/dist/tools/summarize-spec.test.d.ts +2 -0
  386. package/dist/tools/summarize-spec.test.d.ts.map +1 -0
  387. package/dist/tools/summarize-spec.test.js +180 -0
  388. package/dist/tools/summarize-spec.test.js.map +1 -0
  389. package/dist/tools/update-status/dod-gates.d.ts +16 -0
  390. package/dist/tools/update-status/dod-gates.d.ts.map +1 -0
  391. package/dist/tools/update-status/dod-gates.js +117 -0
  392. package/dist/tools/update-status/dod-gates.js.map +1 -0
  393. package/dist/tools/update-status/file-sync.d.ts +6 -0
  394. package/dist/tools/update-status/file-sync.d.ts.map +1 -0
  395. package/dist/tools/update-status/file-sync.js +112 -0
  396. package/dist/tools/update-status/file-sync.js.map +1 -0
  397. package/dist/tools/update-status/index.d.ts +3 -0
  398. package/dist/tools/update-status/index.d.ts.map +1 -0
  399. package/dist/tools/update-status/index.js +181 -0
  400. package/dist/tools/update-status/index.js.map +1 -0
  401. package/dist/tools/update-status/response-builder.d.ts +4 -0
  402. package/dist/tools/update-status/response-builder.d.ts.map +1 -0
  403. package/dist/tools/update-status/response-builder.js +69 -0
  404. package/dist/tools/update-status/response-builder.js.map +1 -0
  405. package/dist/tools/update-status/side-effects.d.ts +15 -0
  406. package/dist/tools/update-status/side-effects.d.ts.map +1 -0
  407. package/dist/tools/update-status/side-effects.js +64 -0
  408. package/dist/tools/update-status/side-effects.js.map +1 -0
  409. package/dist/tools/update-status/transition-guard.d.ts +20 -0
  410. package/dist/tools/update-status/transition-guard.d.ts.map +1 -0
  411. package/dist/tools/update-status/transition-guard.js +75 -0
  412. package/dist/tools/update-status/transition-guard.js.map +1 -0
  413. package/dist/tools/update-status.d.ts +1 -2
  414. package/dist/tools/update-status.d.ts.map +1 -1
  415. package/dist/tools/update-status.js +2 -481
  416. package/dist/tools/update-status.js.map +1 -1
  417. package/dist/tools/update-status.test.d.ts +2 -0
  418. package/dist/tools/update-status.test.d.ts.map +1 -0
  419. package/dist/tools/update-status.test.js +142 -0
  420. package/dist/tools/update-status.test.js.map +1 -0
  421. package/dist/tools/validate.d.ts.map +1 -1
  422. package/dist/tools/validate.js +6 -4
  423. package/dist/tools/validate.js.map +1 -1
  424. package/dist/tools/validate.test.d.ts +2 -0
  425. package/dist/tools/validate.test.d.ts.map +1 -0
  426. package/dist/tools/validate.test.js +137 -0
  427. package/dist/tools/validate.test.js.map +1 -0
  428. package/dist/types/analysis.d.ts +2 -1
  429. package/dist/types/analysis.d.ts.map +1 -1
  430. package/dist/types/index.d.ts +2 -0
  431. package/dist/types/index.d.ts.map +1 -1
  432. package/dist/types/index.js +2 -0
  433. package/dist/types/index.js.map +1 -1
  434. package/dist/types/red-team.d.ts +29 -0
  435. package/dist/types/red-team.d.ts.map +1 -0
  436. package/dist/types/red-team.js +3 -0
  437. package/dist/types/red-team.js.map +1 -0
  438. package/dist/types/update-notifier.d.ts +5 -0
  439. package/dist/types/update-notifier.d.ts.map +1 -0
  440. package/dist/types/update-notifier.js +3 -0
  441. package/dist/types/update-notifier.js.map +1 -0
  442. package/package.json +9 -2
  443. package/src/config/license-plans.json +2 -1
  444. package/src/i18n/messages/en.json +5 -0
  445. package/src/i18n/messages/es.json +5 -0
  446. package/src/i18n/messages/pt.json +5 -0
@@ -0,0 +1,742 @@
1
+ // SpecForge — Skill & Rules Generator Engine Tests
2
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
3
+ vi.mock('node:fs/promises', () => ({
4
+ readFile: vi.fn(),
5
+ }));
6
+ import { readFile } from 'node:fs/promises';
7
+ import { generateSkill, generateRules, getAgentConfig, detectAgentPlatform, } from './skill-generator.js';
8
+ const mockedReadFile = vi.mocked(readFile);
9
+ // === Helper factories ===
10
+ function makeKnowledge(overrides = {}) {
11
+ return {
12
+ projectId: 'proj-1',
13
+ projectPath: '/home/user/my-project',
14
+ locale: 'en',
15
+ experienceLevel: 'intermediate',
16
+ language: 'typescript',
17
+ framework: 'express',
18
+ packageManager: 'npm',
19
+ buildCommand: 'npm run build',
20
+ testCommand: 'npm test',
21
+ stack: ['typescript', 'express', 'postgresql'],
22
+ database: 'postgresql',
23
+ architecture: {
24
+ primary: 'layered',
25
+ secondary: [],
26
+ layers: [
27
+ { name: 'API', directories: ['src/routes'], responsibility: 'HTTP handling', dependsOn: ['Service'], neverDependsOn: ['Database'] },
28
+ { name: 'Service', directories: ['src/services'], responsibility: 'Business logic', dependsOn: [], neverDependsOn: [] },
29
+ ],
30
+ boundaries: [
31
+ { name: 'Auth', directories: ['src/auth'], publicApi: ['src/auth/index.ts'], internalOnly: [] },
32
+ ],
33
+ communicationPatterns: ['REST'],
34
+ deploymentUnits: ['docker'],
35
+ },
36
+ apps: [],
37
+ conventions: { 'naming-files': 'kebab-case', 'naming-variables': 'camelCase' },
38
+ layers: ['routes', 'services'],
39
+ environments: ['development'],
40
+ apiContracts: [],
41
+ specLocation: 'docs/sdd/specs',
42
+ envSetup: {
43
+ projectId: 'proj-1',
44
+ variables: [],
45
+ missing: [],
46
+ security: { gitignoreHasEnv: true, noSecretsInCode: true, noEnvInGit: true, noSecretsInLogs: true, usesSecretManager: false, issues: [] },
47
+ setupSteps: [],
48
+ },
49
+ linting: {
50
+ projectId: 'proj-1',
51
+ language: 'typescript',
52
+ detectedLinters: [],
53
+ missingRecommended: [],
54
+ rulesConflicts: [],
55
+ customRulesFromProject: [],
56
+ maturityScore: 0.5,
57
+ },
58
+ availableMcps: [],
59
+ docsRegistry: {},
60
+ qualityProfile: {
61
+ enabledCategories: ['solid', 'clean-code'],
62
+ customRules: [],
63
+ principles: ['SOLID', 'DRY'],
64
+ strictness: 'strict',
65
+ },
66
+ lastAnalyzed: '2025-01-01T00:00:00Z',
67
+ ...overrides,
68
+ };
69
+ }
70
+ function makePlatformsConfig() {
71
+ return {
72
+ platforms: {
73
+ 'claude-code': {
74
+ name: 'Claude Code',
75
+ rulesFile: 'CLAUDE.md',
76
+ skillsDir: '.claude/skills/',
77
+ format: 'markdown',
78
+ capabilities: ['mcp', 'file-edit', 'bash'],
79
+ mcpSupport: true,
80
+ },
81
+ cursor: {
82
+ name: 'Cursor',
83
+ rulesFile: '.cursorrules',
84
+ skillsDir: null,
85
+ format: 'markdown',
86
+ capabilities: ['file-edit'],
87
+ mcpSupport: false,
88
+ },
89
+ windsurf: {
90
+ name: 'Windsurf',
91
+ rulesFile: '.windsurfrules',
92
+ skillsDir: null,
93
+ format: 'markdown',
94
+ capabilities: ['file-edit'],
95
+ mcpSupport: false,
96
+ },
97
+ aider: {
98
+ name: 'Aider',
99
+ rulesFile: '.aider.conf.yml',
100
+ skillsDir: null,
101
+ format: 'yaml',
102
+ capabilities: ['file-edit'],
103
+ mcpSupport: false,
104
+ },
105
+ copilot: {
106
+ name: 'GitHub Copilot',
107
+ rulesFile: '.github/copilot-instructions.md',
108
+ skillsDir: null,
109
+ format: 'markdown',
110
+ capabilities: ['completion'],
111
+ mcpSupport: false,
112
+ },
113
+ cline: {
114
+ name: 'Cline',
115
+ rulesFile: '.clinerules',
116
+ skillsDir: null,
117
+ format: 'markdown',
118
+ capabilities: ['file-edit', 'bash'],
119
+ mcpSupport: true,
120
+ },
121
+ continue: {
122
+ name: 'Continue.dev',
123
+ rulesFile: '.continue/config.json',
124
+ skillsDir: null,
125
+ format: 'json',
126
+ capabilities: ['completion'],
127
+ mcpSupport: false,
128
+ },
129
+ },
130
+ subAgentFrameworks: {},
131
+ };
132
+ }
133
+ function makeConstitution() {
134
+ return {
135
+ projectId: 'proj-1',
136
+ principles: [
137
+ {
138
+ id: 'p-1',
139
+ category: 'architecture',
140
+ principle: 'Use layered architecture',
141
+ rationale: 'Separation of concerns',
142
+ enforceLevel: 'strict',
143
+ autoEnforceable: true,
144
+ source: 'auto-detected',
145
+ immutable: false,
146
+ },
147
+ {
148
+ id: 'p-2',
149
+ category: 'security',
150
+ principle: 'No hardcoded secrets',
151
+ rationale: 'Security best practice',
152
+ enforceLevel: 'recommended',
153
+ autoEnforceable: true,
154
+ source: 'user-defined',
155
+ immutable: true,
156
+ },
157
+ ],
158
+ createdAt: '2025-01-01T00:00:00Z',
159
+ updatedAt: '2025-01-01T00:00:00Z',
160
+ version: 1,
161
+ };
162
+ }
163
+ // === Setup ===
164
+ function setupConfigMock() {
165
+ mockedReadFile.mockResolvedValue(JSON.stringify(makePlatformsConfig()));
166
+ }
167
+ beforeEach(() => {
168
+ vi.resetAllMocks();
169
+ });
170
+ // === Tests for generateSkill ===
171
+ describe('generateSkill', () => {
172
+ describe('unsupported platform', () => {
173
+ it('returns error for unknown platform', async () => {
174
+ setupConfigMock();
175
+ const knowledge = makeKnowledge();
176
+ const result = await generateSkill('nonexistent-platform', 'project-rules', 'My Rules', 'Some description', knowledge);
177
+ expect(result.files).toEqual([]);
178
+ expect(result.message).toContain('Unknown platform');
179
+ expect(result.installInstructions).toContain('not yet supported');
180
+ });
181
+ });
182
+ describe('project-rules skill type', () => {
183
+ it('generates project rules for claude-code', async () => {
184
+ setupConfigMock();
185
+ const knowledge = makeKnowledge();
186
+ const result = await generateSkill('claude-code', 'project-rules', 'My Project Rules', 'Rules for the project', knowledge);
187
+ expect(result.platform).toBe('claude-code');
188
+ expect(result.files).toHaveLength(1);
189
+ expect(result.files[0].path).toContain('.claude/skills/my-project-rules.md');
190
+ expect(result.files[0].content).toContain('My Project Rules');
191
+ expect(result.files[0].content).toContain('Rules for the project');
192
+ expect(result.files[0].content).toContain('typescript');
193
+ expect(result.files[0].content).toContain('express');
194
+ expect(result.files[0].content).toContain('postgresql');
195
+ expect(result.files[0].content).toContain('layered');
196
+ expect(result.files[0].content).toContain('SOLID');
197
+ expect(result.files[0].content).toContain('API');
198
+ expect(result.message).toContain('project-rules');
199
+ });
200
+ it('uses rulesFile when no skillsDir', async () => {
201
+ setupConfigMock();
202
+ const knowledge = makeKnowledge();
203
+ const result = await generateSkill('cursor', 'project-rules', 'My Rules', 'Description', knowledge);
204
+ expect(result.files[0].path).toBe('.cursorrules');
205
+ });
206
+ it('handles null framework', async () => {
207
+ setupConfigMock();
208
+ const knowledge = makeKnowledge({ framework: null });
209
+ const result = await generateSkill('claude-code', 'project-rules', 'Rules', 'Desc', knowledge);
210
+ expect(result.files[0].content).toContain('Framework: none');
211
+ });
212
+ });
213
+ describe('domain-skill skill type', () => {
214
+ it('generates domain skill with specContent', async () => {
215
+ setupConfigMock();
216
+ const knowledge = makeKnowledge();
217
+ const result = await generateSkill('claude-code', 'domain-skill', 'Auth Domain', 'Authentication domain skill', knowledge, 'This spec covers JWT authentication with refresh tokens.');
218
+ expect(result.files).toHaveLength(1);
219
+ expect(result.files[0].path).toContain('.claude/skills/auth-domain.md');
220
+ expect(result.files[0].content).toContain('Auth Domain');
221
+ expect(result.files[0].content).toContain('Authentication domain skill');
222
+ expect(result.files[0].content).toContain('Spec Content');
223
+ expect(result.files[0].content).toContain('JWT authentication');
224
+ });
225
+ it('generates domain skill without specContent', async () => {
226
+ setupConfigMock();
227
+ const knowledge = makeKnowledge();
228
+ const result = await generateSkill('claude-code', 'domain-skill', 'Auth Domain', 'Authentication domain skill', knowledge);
229
+ expect(result.files[0].content).not.toContain('Spec Content');
230
+ });
231
+ it('uses skills/ fallback path when no skillsDir', async () => {
232
+ setupConfigMock();
233
+ const knowledge = makeKnowledge();
234
+ const result = await generateSkill('cursor', 'domain-skill', 'Auth Domain', 'Desc', knowledge);
235
+ expect(result.files[0].path).toBe('skills/auth-domain.md');
236
+ });
237
+ it('handles null framework in context', async () => {
238
+ setupConfigMock();
239
+ const knowledge = makeKnowledge({ framework: null });
240
+ const result = await generateSkill('claude-code', 'domain-skill', 'Auth', 'Desc', knowledge);
241
+ expect(result.files[0].content).toContain('typescript/native');
242
+ });
243
+ });
244
+ describe('workflow skill type', () => {
245
+ it('generates workflow skill', async () => {
246
+ setupConfigMock();
247
+ const knowledge = makeKnowledge();
248
+ const result = await generateSkill('claude-code', 'workflow', 'SDD Workflow', 'Follow the SDD process', knowledge);
249
+ expect(result.files).toHaveLength(1);
250
+ expect(result.files[0].content).toContain('Workflow Steps');
251
+ expect(result.files[0].content).toContain('npm run build');
252
+ expect(result.files[0].content).toContain('npm test');
253
+ expect(result.files[0].content).toContain('docs/sdd/specs');
254
+ });
255
+ it('omits build/test when null', async () => {
256
+ setupConfigMock();
257
+ const knowledge = makeKnowledge({ buildCommand: null, testCommand: null });
258
+ const result = await generateSkill('claude-code', 'workflow', 'Workflow', 'Desc', knowledge);
259
+ expect(result.files[0].content).not.toContain('Build:');
260
+ expect(result.files[0].content).not.toContain('Test:');
261
+ });
262
+ it('uses skills/ fallback path when no skillsDir', async () => {
263
+ setupConfigMock();
264
+ const knowledge = makeKnowledge();
265
+ const result = await generateSkill('cursor', 'workflow', 'My Workflow', 'Desc', knowledge);
266
+ expect(result.files[0].path).toBe('skills/my-workflow.md');
267
+ });
268
+ });
269
+ describe('convention skill type', () => {
270
+ it('generates convention skill', async () => {
271
+ setupConfigMock();
272
+ const knowledge = makeKnowledge();
273
+ const result = await generateSkill('claude-code', 'convention', 'Code Conventions', 'Team coding conventions', knowledge);
274
+ expect(result.files).toHaveLength(1);
275
+ expect(result.files[0].content).toContain('Code Conventions');
276
+ expect(result.files[0].content).toContain('naming-files');
277
+ expect(result.files[0].content).toContain('kebab-case');
278
+ expect(result.files[0].content).toContain('Strictness: strict');
279
+ expect(result.files[0].content).toContain('SOLID');
280
+ expect(result.files[0].content).toContain('solid');
281
+ expect(result.files[0].content).toContain('clean-code');
282
+ });
283
+ it('uses skills/ fallback path when no skillsDir', async () => {
284
+ setupConfigMock();
285
+ const knowledge = makeKnowledge();
286
+ const result = await generateSkill('cursor', 'convention', 'My Conventions', 'Desc', knowledge);
287
+ expect(result.files[0].path).toBe('skills/my-conventions.md');
288
+ });
289
+ });
290
+ describe('install instructions', () => {
291
+ it('adds claude-code specific instructions', async () => {
292
+ setupConfigMock();
293
+ const knowledge = makeKnowledge();
294
+ const result = await generateSkill('claude-code', 'project-rules', 'Rules', 'Desc', knowledge);
295
+ expect(result.installInstructions).toContain('Claude Code');
296
+ expect(result.installInstructions).toContain('CLAUDE.md');
297
+ });
298
+ it('adds cursor specific instructions', async () => {
299
+ setupConfigMock();
300
+ const knowledge = makeKnowledge();
301
+ const result = await generateSkill('cursor', 'project-rules', 'Rules', 'Desc', knowledge);
302
+ expect(result.installInstructions).toContain('Cursor');
303
+ expect(result.installInstructions).toContain('.cursorrules');
304
+ });
305
+ it('adds windsurf specific instructions', async () => {
306
+ setupConfigMock();
307
+ const knowledge = makeKnowledge();
308
+ const result = await generateSkill('windsurf', 'project-rules', 'Rules', 'Desc', knowledge);
309
+ expect(result.installInstructions).toContain('Windsurf');
310
+ expect(result.installInstructions).toContain('.windsurfrules');
311
+ });
312
+ it('generates generic instructions for other platforms', async () => {
313
+ setupConfigMock();
314
+ const knowledge = makeKnowledge();
315
+ const result = await generateSkill('cline', 'project-rules', 'Rules', 'Desc', knowledge);
316
+ expect(result.installInstructions).toContain('Cline');
317
+ expect(result.installInstructions).not.toContain('automatically detect');
318
+ });
319
+ });
320
+ describe('all platforms', () => {
321
+ const platforms = ['claude-code', 'cursor', 'windsurf', 'aider', 'copilot', 'cline', 'continue'];
322
+ for (const platform of platforms) {
323
+ it(`generates skill for ${platform}`, async () => {
324
+ setupConfigMock();
325
+ const knowledge = makeKnowledge();
326
+ const result = await generateSkill(platform, 'project-rules', 'Rules', 'Description', knowledge);
327
+ expect(result.platform).toBe(platform);
328
+ expect(result.files).toHaveLength(1);
329
+ expect(result.message).toContain('project-rules');
330
+ });
331
+ }
332
+ });
333
+ });
334
+ // === Tests for generateRules ===
335
+ describe('generateRules', () => {
336
+ describe('unsupported platform', () => {
337
+ it('returns empty rules for unknown platform', async () => {
338
+ setupConfigMock();
339
+ const knowledge = makeKnowledge();
340
+ const result = await generateRules('nonexistent', knowledge);
341
+ expect(result.rulesFile.path).toBe('');
342
+ expect(result.rulesFile.content).toBe('Platform not supported');
343
+ expect(result.rulesCount).toBe(0);
344
+ expect(result.categories).toEqual([]);
345
+ });
346
+ });
347
+ describe('markdown format platforms', () => {
348
+ it('generates full rules for claude-code with all sections', async () => {
349
+ setupConfigMock();
350
+ const knowledge = makeKnowledge();
351
+ const result = await generateRules('claude-code', knowledge);
352
+ expect(result.platform).toBe('claude-code');
353
+ expect(result.rulesFile.path).toBe('CLAUDE.md');
354
+ expect(result.rulesFile.format).toBe('markdown');
355
+ expect(result.rulesCount).toBeGreaterThan(0);
356
+ expect(result.categories).toContain('project');
357
+ expect(result.categories).toContain('architecture');
358
+ expect(result.categories).toContain('conventions');
359
+ expect(result.categories).toContain('quality');
360
+ expect(result.categories).toContain('workflow');
361
+ });
362
+ it('includes project overview rules', async () => {
363
+ setupConfigMock();
364
+ const knowledge = makeKnowledge();
365
+ const result = await generateRules('claude-code', knowledge);
366
+ expect(result.rulesFile.content).toContain('typescript');
367
+ expect(result.rulesFile.content).toContain('express');
368
+ expect(result.rulesFile.content).toContain('postgresql');
369
+ expect(result.rulesFile.content).toContain('npm');
370
+ expect(result.rulesFile.content).toContain('typescript, express, postgresql');
371
+ });
372
+ it('omits database rule when unknown', async () => {
373
+ setupConfigMock();
374
+ const knowledge = makeKnowledge({ database: 'unknown' });
375
+ const result = await generateRules('claude-code', knowledge);
376
+ expect(result.rulesFile.content).not.toContain('Database: unknown');
377
+ });
378
+ it('omits package manager rule when null', async () => {
379
+ setupConfigMock();
380
+ const knowledge = makeKnowledge({ packageManager: null });
381
+ const result = await generateRules('claude-code', knowledge);
382
+ expect(result.rulesFile.content).not.toContain('Package manager:');
383
+ });
384
+ it('omits stack rule when empty', async () => {
385
+ setupConfigMock();
386
+ const knowledge = makeKnowledge({ stack: [] });
387
+ const result = await generateRules('claude-code', knowledge);
388
+ expect(result.rulesFile.content).not.toContain('Tech stack:');
389
+ });
390
+ it('omits framework in project overview when null', async () => {
391
+ setupConfigMock();
392
+ const knowledge = makeKnowledge({ framework: null });
393
+ const result = await generateRules('claude-code', knowledge);
394
+ expect(result.rulesFile.content).toContain('typescript project');
395
+ expect(result.rulesFile.content).not.toContain('using express');
396
+ });
397
+ it('includes architecture rules with layers and boundaries', async () => {
398
+ setupConfigMock();
399
+ const knowledge = makeKnowledge();
400
+ const result = await generateRules('claude-code', knowledge);
401
+ expect(result.rulesFile.content).toContain('Architecture');
402
+ expect(result.rulesFile.content).toContain('layered');
403
+ expect(result.rulesFile.content).toContain('API');
404
+ expect(result.rulesFile.content).toContain('NEVER import from');
405
+ expect(result.rulesFile.content).toContain('Auth');
406
+ });
407
+ it('omits neverDependsOn when empty', async () => {
408
+ setupConfigMock();
409
+ const knowledge = makeKnowledge();
410
+ // Service layer has no neverDependsOn
411
+ const result = await generateRules('claude-code', knowledge);
412
+ // Only API has neverDependsOn Database
413
+ expect(result.rulesFile.content).toContain('NEVER import from Database');
414
+ });
415
+ it('includes conventions', async () => {
416
+ setupConfigMock();
417
+ const knowledge = makeKnowledge();
418
+ const result = await generateRules('claude-code', knowledge);
419
+ expect(result.rulesFile.content).toContain('naming-files');
420
+ expect(result.rulesFile.content).toContain('kebab-case');
421
+ expect(result.rulesFile.content).toContain('descriptive');
422
+ expect(result.rulesFile.content).toContain('pure functions');
423
+ });
424
+ it('includes quality rules', async () => {
425
+ setupConfigMock();
426
+ const knowledge = makeKnowledge();
427
+ const result = await generateRules('claude-code', knowledge);
428
+ expect(result.rulesFile.content).toContain('Quality');
429
+ expect(result.rulesFile.content).toContain('SOLID');
430
+ expect(result.rulesFile.content).toContain('security issues');
431
+ expect(result.rulesFile.content).toContain('Handle all errors');
432
+ });
433
+ it('uses strict enforcement for strict quality profile', async () => {
434
+ setupConfigMock();
435
+ const knowledge = makeKnowledge();
436
+ const result = await generateRules('claude-code', knowledge);
437
+ expect(result.rulesFile.content).toContain('[STRICT]');
438
+ });
439
+ it('uses recommended enforcement for non-strict profile', async () => {
440
+ setupConfigMock();
441
+ const knowledge = makeKnowledge({
442
+ qualityProfile: {
443
+ enabledCategories: [],
444
+ customRules: [],
445
+ principles: ['KISS'],
446
+ strictness: 'relaxed',
447
+ },
448
+ });
449
+ const result = await generateRules('claude-code', knowledge);
450
+ // KISS should be recommended, not strict
451
+ const kissLine = result.rulesFile.content.split('\n').find((l) => l.includes('KISS'));
452
+ expect(kissLine).toBeDefined();
453
+ expect(kissLine).not.toContain('[STRICT]');
454
+ });
455
+ it('includes workflow rules with build/test commands', async () => {
456
+ setupConfigMock();
457
+ const knowledge = makeKnowledge();
458
+ const result = await generateRules('claude-code', knowledge);
459
+ expect(result.rulesFile.content).toContain('Workflow');
460
+ expect(result.rulesFile.content).toContain('docs/sdd/specs');
461
+ expect(result.rulesFile.content).toContain('npm run build');
462
+ expect(result.rulesFile.content).toContain('npm test');
463
+ });
464
+ it('omits build/test rules when null', async () => {
465
+ setupConfigMock();
466
+ const knowledge = makeKnowledge({ buildCommand: null, testCommand: null });
467
+ const result = await generateRules('claude-code', knowledge);
468
+ expect(result.rulesFile.content).not.toContain('Build command:');
469
+ expect(result.rulesFile.content).not.toContain('Test command:');
470
+ });
471
+ it('includes constitution when provided', async () => {
472
+ setupConfigMock();
473
+ const knowledge = makeKnowledge();
474
+ const constitution = makeConstitution();
475
+ const result = await generateRules('claude-code', knowledge, constitution);
476
+ expect(result.categories).toContain('constitution');
477
+ expect(result.rulesFile.content).toContain('Project Constitution');
478
+ expect(result.rulesFile.content).toContain('Use layered architecture');
479
+ expect(result.rulesFile.content).toContain('Separation of concerns');
480
+ });
481
+ it('includes custom rules when provided', async () => {
482
+ setupConfigMock();
483
+ const knowledge = makeKnowledge();
484
+ const result = await generateRules('claude-code', knowledge, undefined, {
485
+ customRules: ['Always use TypeScript strict mode', 'No any types'],
486
+ });
487
+ expect(result.categories).toContain('custom');
488
+ expect(result.rulesFile.content).toContain('Custom Rules');
489
+ expect(result.rulesFile.content).toContain('Always use TypeScript strict mode');
490
+ expect(result.rulesFile.content).toContain('No any types');
491
+ });
492
+ it('respects include flags - exclude architecture', async () => {
493
+ setupConfigMock();
494
+ const knowledge = makeKnowledge();
495
+ const result = await generateRules('claude-code', knowledge, undefined, {
496
+ includeArchitecture: false,
497
+ });
498
+ expect(result.categories).not.toContain('architecture');
499
+ });
500
+ it('respects include flags - exclude conventions', async () => {
501
+ setupConfigMock();
502
+ const knowledge = makeKnowledge();
503
+ const result = await generateRules('claude-code', knowledge, undefined, {
504
+ includeConventions: false,
505
+ });
506
+ expect(result.categories).not.toContain('conventions');
507
+ });
508
+ it('respects include flags - exclude quality', async () => {
509
+ setupConfigMock();
510
+ const knowledge = makeKnowledge();
511
+ const result = await generateRules('claude-code', knowledge, undefined, {
512
+ includeQuality: false,
513
+ });
514
+ expect(result.categories).not.toContain('quality');
515
+ });
516
+ it('respects include flags - exclude workflow', async () => {
517
+ setupConfigMock();
518
+ const knowledge = makeKnowledge();
519
+ const result = await generateRules('claude-code', knowledge, undefined, {
520
+ includeWorkflow: false,
521
+ });
522
+ expect(result.categories).not.toContain('workflow');
523
+ });
524
+ it('excludes all optional sections', async () => {
525
+ setupConfigMock();
526
+ const knowledge = makeKnowledge();
527
+ const result = await generateRules('claude-code', knowledge, undefined, {
528
+ includeArchitecture: false,
529
+ includeConventions: false,
530
+ includeQuality: false,
531
+ includeWorkflow: false,
532
+ });
533
+ expect(result.categories).toEqual(['project']);
534
+ });
535
+ it('generates additional SDD workflow file for claude-code', async () => {
536
+ setupConfigMock();
537
+ const knowledge = makeKnowledge();
538
+ const result = await generateRules('claude-code', knowledge);
539
+ expect(result.additionalFiles).toHaveLength(1);
540
+ expect(result.additionalFiles[0].path).toContain('.claude/skills/sdd-workflow.md');
541
+ expect(result.additionalFiles[0].content).toContain('SDD Workflow Skill');
542
+ expect(result.additionalFiles[0].content).toContain('typescript');
543
+ expect(result.additionalFiles[0].content).toContain('layered');
544
+ expect(result.additionalFiles[0].content).toContain('docs/sdd/specs');
545
+ });
546
+ it('handles null framework in additional file', async () => {
547
+ setupConfigMock();
548
+ const knowledge = makeKnowledge({ framework: null });
549
+ const result = await generateRules('claude-code', knowledge);
550
+ expect(result.additionalFiles[0].content).toContain('Framework: none');
551
+ });
552
+ it('does not generate additional files for non-claude-code platforms', async () => {
553
+ setupConfigMock();
554
+ const knowledge = makeKnowledge();
555
+ const result = await generateRules('cursor', knowledge);
556
+ expect(result.additionalFiles).toHaveLength(0);
557
+ });
558
+ it('includes platform title in markdown header', async () => {
559
+ setupConfigMock();
560
+ const knowledge = makeKnowledge();
561
+ const result = await generateRules('claude-code', knowledge);
562
+ expect(result.rulesFile.content).toContain('# Claude Code Rules');
563
+ });
564
+ it('uses correct platform title for cursor', async () => {
565
+ setupConfigMock();
566
+ const knowledge = makeKnowledge();
567
+ const result = await generateRules('cursor', knowledge);
568
+ expect(result.rulesFile.content).toContain('# Cursor Rules');
569
+ });
570
+ it('uses correct platform title for windsurf', async () => {
571
+ setupConfigMock();
572
+ const knowledge = makeKnowledge();
573
+ const result = await generateRules('windsurf', knowledge);
574
+ expect(result.rulesFile.content).toContain('# Windsurf Rules');
575
+ });
576
+ it('uses correct platform title for copilot', async () => {
577
+ setupConfigMock();
578
+ const knowledge = makeKnowledge();
579
+ const result = await generateRules('copilot', knowledge);
580
+ expect(result.rulesFile.content).toContain('# GitHub Copilot Rules');
581
+ });
582
+ it('uses correct platform title for cline', async () => {
583
+ setupConfigMock();
584
+ const knowledge = makeKnowledge();
585
+ const result = await generateRules('cline', knowledge);
586
+ expect(result.rulesFile.content).toContain('# Cline Rules');
587
+ });
588
+ it('uses correct platform title for continue', async () => {
589
+ setupConfigMock();
590
+ const knowledge = makeKnowledge();
591
+ // Continue uses JSON format, so the title won't be in markdown
592
+ // but the JSON output should be valid
593
+ const result = await generateRules('continue', knowledge);
594
+ // JSON format doesn't have platform title in same way
595
+ expect(result.rulesFile.format).toBe('json');
596
+ });
597
+ it('uses correct platform title for aider', async () => {
598
+ setupConfigMock();
599
+ const knowledge = makeKnowledge();
600
+ const result = await generateRules('aider', knowledge);
601
+ // aider uses yaml format, check that it is YAML and has auto-generated header
602
+ expect(result.rulesFile.content).toContain('# Auto-generated by SpecForge');
603
+ });
604
+ });
605
+ describe('json format platforms', () => {
606
+ it('generates rules in JSON format for continue', async () => {
607
+ setupConfigMock();
608
+ const knowledge = makeKnowledge();
609
+ const result = await generateRules('continue', knowledge);
610
+ expect(result.rulesFile.format).toBe('json');
611
+ const parsed = JSON.parse(result.rulesFile.content);
612
+ expect(parsed).toBeDefined();
613
+ expect(parsed.project_overview).toBeDefined();
614
+ expect(parsed.architecture).toBeDefined();
615
+ expect(parsed.conventions).toBeDefined();
616
+ expect(parsed.quality).toBeDefined();
617
+ expect(parsed.workflow).toBeDefined();
618
+ });
619
+ it('JSON format includes enforcement level', async () => {
620
+ setupConfigMock();
621
+ const knowledge = makeKnowledge();
622
+ const result = await generateRules('continue', knowledge);
623
+ const parsed = JSON.parse(result.rulesFile.content);
624
+ expect(parsed.project_overview[0]).toHaveProperty('rule');
625
+ expect(parsed.project_overview[0]).toHaveProperty('enforcement');
626
+ });
627
+ });
628
+ describe('yaml format platforms', () => {
629
+ it('generates rules in YAML format for aider', async () => {
630
+ setupConfigMock();
631
+ const knowledge = makeKnowledge();
632
+ const result = await generateRules('aider', knowledge);
633
+ expect(result.rulesFile.format).toBe('yaml');
634
+ expect(result.rulesFile.content).toContain('# Auto-generated by SpecForge');
635
+ expect(result.rulesFile.content).toContain('project_overview:');
636
+ expect(result.rulesFile.content).toContain('enforcement: strict');
637
+ });
638
+ it('escapes double quotes in YAML', async () => {
639
+ setupConfigMock();
640
+ const knowledge = makeKnowledge({
641
+ conventions: { 'quote-test': 'Use "double" quotes carefully' },
642
+ });
643
+ const result = await generateRules('aider', knowledge);
644
+ expect(result.rulesFile.content).toContain('\\"double\\"');
645
+ });
646
+ });
647
+ describe('all platforms generate rules', () => {
648
+ const platforms = ['claude-code', 'cursor', 'windsurf', 'aider', 'copilot', 'cline', 'continue'];
649
+ for (const platform of platforms) {
650
+ it(`generates rules for ${platform}`, async () => {
651
+ setupConfigMock();
652
+ const knowledge = makeKnowledge();
653
+ const result = await generateRules(platform, knowledge);
654
+ expect(result.platform).toBe(platform);
655
+ expect(result.rulesCount).toBeGreaterThan(0);
656
+ expect(result.categories).toContain('project');
657
+ });
658
+ }
659
+ });
660
+ });
661
+ // === Tests for getAgentConfig ===
662
+ describe('getAgentConfig', () => {
663
+ it('returns config for known platform', async () => {
664
+ setupConfigMock();
665
+ const config = await getAgentConfig('claude-code');
666
+ expect(config.platform).toBe('claude-code');
667
+ expect(config.rulesFile).toBe('CLAUDE.md');
668
+ expect(config.skillsDir).toBe('.claude/skills/');
669
+ expect(config.format).toBe('markdown');
670
+ expect(config.capabilities).toContain('mcp');
671
+ expect(config.mcpSupport).toBe(true);
672
+ });
673
+ it('returns empty config for unknown platform', async () => {
674
+ setupConfigMock();
675
+ const config = await getAgentConfig('nonexistent');
676
+ expect(config.platform).toBe('nonexistent');
677
+ expect(config.rulesFile).toBe('');
678
+ expect(config.format).toBe('markdown');
679
+ expect(config.capabilities).toEqual([]);
680
+ expect(config.mcpSupport).toBe(false);
681
+ });
682
+ it('returns undefined skillsDir when platform has null', async () => {
683
+ setupConfigMock();
684
+ const config = await getAgentConfig('cursor');
685
+ expect(config.skillsDir).toBeUndefined();
686
+ });
687
+ });
688
+ // === Tests for detectAgentPlatform ===
689
+ describe('detectAgentPlatform', () => {
690
+ it('detects claude-code from CLAUDE.md', async () => {
691
+ setupConfigMock();
692
+ const result = await detectAgentPlatform({ 'ai-rules': 'CLAUDE.md' });
693
+ expect(result.platform).toBe('claude-code');
694
+ expect(result.confidence).toBeGreaterThan(0);
695
+ expect(result.detectedFrom.length).toBeGreaterThan(0);
696
+ });
697
+ it('detects cursor from cursor-rules', async () => {
698
+ setupConfigMock();
699
+ const result = await detectAgentPlatform({ 'cursor-rules': '.cursorrules' });
700
+ expect(result.platform).toBe('cursor');
701
+ expect(result.confidence).toBeGreaterThan(0);
702
+ expect(result.detectedFrom).toContain('.cursorrules detected');
703
+ });
704
+ it('detects from rules file convention', async () => {
705
+ setupConfigMock();
706
+ const result = await detectAgentPlatform({ 'ai-rules': '.clinerules' });
707
+ expect(result.platform).toBe('cline');
708
+ expect(result.detectedFrom).toContain('Rules file: .clinerules');
709
+ });
710
+ it('defaults to claude-code with 0 confidence when no match', async () => {
711
+ setupConfigMock();
712
+ const result = await detectAgentPlatform({});
713
+ expect(result.platform).toBe('claude-code');
714
+ expect(result.confidence).toBe(0);
715
+ expect(result.detectedFrom).toEqual([]);
716
+ });
717
+ it('prefers higher score platform', async () => {
718
+ setupConfigMock();
719
+ // CLAUDE.md gives score 8 (3+5), so it should win
720
+ const result = await detectAgentPlatform({
721
+ 'ai-rules': 'CLAUDE.md',
722
+ 'cursor-rules': '.cursorrules',
723
+ });
724
+ expect(result.platform).toBe('claude-code');
725
+ });
726
+ it('caps confidence at 1', async () => {
727
+ setupConfigMock();
728
+ const result = await detectAgentPlatform({ 'ai-rules': 'CLAUDE.md' });
729
+ expect(result.confidence).toBeLessThanOrEqual(1);
730
+ });
731
+ });
732
+ // === Tests for loadPlatforms cache and error handling ===
733
+ describe('loadPlatforms', () => {
734
+ it('uses cache on subsequent calls', async () => {
735
+ setupConfigMock();
736
+ await generateSkill('claude-code', 'project-rules', 'R', 'D', makeKnowledge());
737
+ await generateSkill('cursor', 'project-rules', 'R', 'D', makeKnowledge());
738
+ // readFile should have been called at most once due to caching
739
+ expect(mockedReadFile.mock.calls.length).toBeLessThanOrEqual(1);
740
+ });
741
+ });
742
+ //# sourceMappingURL=skill-generator.test.js.map