@hivehub/rulebook 1.2.0

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 (379) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +539 -0
  3. package/dist/agents/claude-code.d.ts +69 -0
  4. package/dist/agents/claude-code.d.ts.map +1 -0
  5. package/dist/agents/claude-code.js +180 -0
  6. package/dist/agents/claude-code.js.map +1 -0
  7. package/dist/agents/cursor-agent.d.ts +184 -0
  8. package/dist/agents/cursor-agent.d.ts.map +1 -0
  9. package/dist/agents/cursor-agent.js +299 -0
  10. package/dist/agents/cursor-agent.js.map +1 -0
  11. package/dist/agents/gemini-cli.d.ts +69 -0
  12. package/dist/agents/gemini-cli.d.ts.map +1 -0
  13. package/dist/agents/gemini-cli.js +180 -0
  14. package/dist/agents/gemini-cli.js.map +1 -0
  15. package/dist/cli/commands.d.ts +57 -0
  16. package/dist/cli/commands.d.ts.map +1 -0
  17. package/dist/cli/commands.js +1370 -0
  18. package/dist/cli/commands.js.map +1 -0
  19. package/dist/cli/docs-prompts.d.ts +3 -0
  20. package/dist/cli/docs-prompts.d.ts.map +1 -0
  21. package/dist/cli/docs-prompts.js +45 -0
  22. package/dist/cli/docs-prompts.js.map +1 -0
  23. package/dist/cli/prompts.d.ts +6 -0
  24. package/dist/cli/prompts.d.ts.map +1 -0
  25. package/dist/cli/prompts.js +376 -0
  26. package/dist/cli/prompts.js.map +1 -0
  27. package/dist/core/agent-manager.d.ts +89 -0
  28. package/dist/core/agent-manager.d.ts.map +1 -0
  29. package/dist/core/agent-manager.js +546 -0
  30. package/dist/core/agent-manager.js.map +1 -0
  31. package/dist/core/auto-fixer.d.ts +14 -0
  32. package/dist/core/auto-fixer.d.ts.map +1 -0
  33. package/dist/core/auto-fixer.js +207 -0
  34. package/dist/core/auto-fixer.js.map +1 -0
  35. package/dist/core/changelog-generator.d.ts +44 -0
  36. package/dist/core/changelog-generator.d.ts.map +1 -0
  37. package/dist/core/changelog-generator.js +222 -0
  38. package/dist/core/changelog-generator.js.map +1 -0
  39. package/dist/core/cli-bridge.d.ts +113 -0
  40. package/dist/core/cli-bridge.d.ts.map +1 -0
  41. package/dist/core/cli-bridge.js +1094 -0
  42. package/dist/core/cli-bridge.js.map +1 -0
  43. package/dist/core/config-manager.d.ts +65 -0
  44. package/dist/core/config-manager.d.ts.map +1 -0
  45. package/dist/core/config-manager.js +266 -0
  46. package/dist/core/config-manager.js.map +1 -0
  47. package/dist/core/coverage-checker.d.ts +14 -0
  48. package/dist/core/coverage-checker.d.ts.map +1 -0
  49. package/dist/core/coverage-checker.js +176 -0
  50. package/dist/core/coverage-checker.js.map +1 -0
  51. package/dist/core/custom-templates.d.ts +27 -0
  52. package/dist/core/custom-templates.d.ts.map +1 -0
  53. package/dist/core/custom-templates.js +122 -0
  54. package/dist/core/custom-templates.js.map +1 -0
  55. package/dist/core/dependency-checker.d.ts +21 -0
  56. package/dist/core/dependency-checker.d.ts.map +1 -0
  57. package/dist/core/dependency-checker.js +247 -0
  58. package/dist/core/dependency-checker.js.map +1 -0
  59. package/dist/core/detector.d.ts +3 -0
  60. package/dist/core/detector.d.ts.map +1 -0
  61. package/dist/core/detector.js +1443 -0
  62. package/dist/core/detector.js.map +1 -0
  63. package/dist/core/docs-generator.d.ts +9 -0
  64. package/dist/core/docs-generator.d.ts.map +1 -0
  65. package/dist/core/docs-generator.js +531 -0
  66. package/dist/core/docs-generator.js.map +1 -0
  67. package/dist/core/generator.d.ts +16 -0
  68. package/dist/core/generator.d.ts.map +1 -0
  69. package/dist/core/generator.js +561 -0
  70. package/dist/core/generator.js.map +1 -0
  71. package/dist/core/gitignore-generator.d.ts +13 -0
  72. package/dist/core/gitignore-generator.d.ts.map +1 -0
  73. package/dist/core/gitignore-generator.js +307 -0
  74. package/dist/core/gitignore-generator.js.map +1 -0
  75. package/dist/core/health-scorer.d.ts +22 -0
  76. package/dist/core/health-scorer.d.ts.map +1 -0
  77. package/dist/core/health-scorer.js +395 -0
  78. package/dist/core/health-scorer.js.map +1 -0
  79. package/dist/core/logger.d.ts +116 -0
  80. package/dist/core/logger.d.ts.map +1 -0
  81. package/dist/core/logger.js +289 -0
  82. package/dist/core/logger.js.map +1 -0
  83. package/dist/core/merger.d.ts +6 -0
  84. package/dist/core/merger.d.ts.map +1 -0
  85. package/dist/core/merger.js +131 -0
  86. package/dist/core/merger.js.map +1 -0
  87. package/dist/core/migrator.d.ts +19 -0
  88. package/dist/core/migrator.d.ts.map +1 -0
  89. package/dist/core/migrator.js +102 -0
  90. package/dist/core/migrator.js.map +1 -0
  91. package/dist/core/minimal-scaffolder.d.ts +8 -0
  92. package/dist/core/minimal-scaffolder.d.ts.map +1 -0
  93. package/dist/core/minimal-scaffolder.js +51 -0
  94. package/dist/core/minimal-scaffolder.js.map +1 -0
  95. package/dist/core/modern-console-new.d.ts +81 -0
  96. package/dist/core/modern-console-new.d.ts.map +1 -0
  97. package/dist/core/modern-console-new.js +340 -0
  98. package/dist/core/modern-console-new.js.map +1 -0
  99. package/dist/core/modern-console.d.ts +99 -0
  100. package/dist/core/modern-console.d.ts.map +1 -0
  101. package/dist/core/modern-console.js +568 -0
  102. package/dist/core/modern-console.js.map +1 -0
  103. package/dist/core/openspec-manager.d.ts +133 -0
  104. package/dist/core/openspec-manager.d.ts.map +1 -0
  105. package/dist/core/openspec-manager.js +605 -0
  106. package/dist/core/openspec-manager.js.map +1 -0
  107. package/dist/core/openspec-migrator.d.ts +27 -0
  108. package/dist/core/openspec-migrator.d.ts.map +1 -0
  109. package/dist/core/openspec-migrator.js +255 -0
  110. package/dist/core/openspec-migrator.js.map +1 -0
  111. package/dist/core/task-manager.d.ts +65 -0
  112. package/dist/core/task-manager.d.ts.map +1 -0
  113. package/dist/core/task-manager.js +318 -0
  114. package/dist/core/task-manager.js.map +1 -0
  115. package/dist/core/test-task-manager.d.ts +49 -0
  116. package/dist/core/test-task-manager.d.ts.map +1 -0
  117. package/dist/core/test-task-manager.js +121 -0
  118. package/dist/core/test-task-manager.js.map +1 -0
  119. package/dist/core/validator.d.ts +21 -0
  120. package/dist/core/validator.d.ts.map +1 -0
  121. package/dist/core/validator.js +177 -0
  122. package/dist/core/validator.js.map +1 -0
  123. package/dist/core/version-bumper.d.ts +19 -0
  124. package/dist/core/version-bumper.d.ts.map +1 -0
  125. package/dist/core/version-bumper.js +180 -0
  126. package/dist/core/version-bumper.js.map +1 -0
  127. package/dist/core/watcher.d.ts +9 -0
  128. package/dist/core/watcher.d.ts.map +1 -0
  129. package/dist/core/watcher.js +22 -0
  130. package/dist/core/watcher.js.map +1 -0
  131. package/dist/core/workflow-generator.d.ts +10 -0
  132. package/dist/core/workflow-generator.d.ts.map +1 -0
  133. package/dist/core/workflow-generator.js +279 -0
  134. package/dist/core/workflow-generator.js.map +1 -0
  135. package/dist/index.d.ts +3 -0
  136. package/dist/index.d.ts.map +1 -0
  137. package/dist/index.js +159 -0
  138. package/dist/index.js.map +1 -0
  139. package/dist/mcp/handlers/archive-task.d.ts +17 -0
  140. package/dist/mcp/handlers/archive-task.d.ts.map +1 -0
  141. package/dist/mcp/handlers/archive-task.js +36 -0
  142. package/dist/mcp/handlers/archive-task.js.map +1 -0
  143. package/dist/mcp/handlers/create-task.d.ts +17 -0
  144. package/dist/mcp/handlers/create-task.d.ts.map +1 -0
  145. package/dist/mcp/handlers/create-task.js +56 -0
  146. package/dist/mcp/handlers/create-task.js.map +1 -0
  147. package/dist/mcp/handlers/list-tasks.d.ts +22 -0
  148. package/dist/mcp/handlers/list-tasks.d.ts.map +1 -0
  149. package/dist/mcp/handlers/list-tasks.js +42 -0
  150. package/dist/mcp/handlers/list-tasks.js.map +1 -0
  151. package/dist/mcp/handlers/show-task.d.ts +25 -0
  152. package/dist/mcp/handlers/show-task.d.ts.map +1 -0
  153. package/dist/mcp/handlers/show-task.js +43 -0
  154. package/dist/mcp/handlers/show-task.js.map +1 -0
  155. package/dist/mcp/handlers/update-task.d.ts +17 -0
  156. package/dist/mcp/handlers/update-task.d.ts.map +1 -0
  157. package/dist/mcp/handlers/update-task.js +35 -0
  158. package/dist/mcp/handlers/update-task.js.map +1 -0
  159. package/dist/mcp/handlers/validate-task.d.ts +15 -0
  160. package/dist/mcp/handlers/validate-task.d.ts.map +1 -0
  161. package/dist/mcp/handlers/validate-task.js +27 -0
  162. package/dist/mcp/handlers/validate-task.js.map +1 -0
  163. package/dist/mcp/rulebook-config.d.ts +22 -0
  164. package/dist/mcp/rulebook-config.d.ts.map +1 -0
  165. package/dist/mcp/rulebook-config.js +65 -0
  166. package/dist/mcp/rulebook-config.js.map +1 -0
  167. package/dist/mcp/rulebook-server.d.ts +4 -0
  168. package/dist/mcp/rulebook-server.d.ts.map +1 -0
  169. package/dist/mcp/rulebook-server.js +246 -0
  170. package/dist/mcp/rulebook-server.js.map +1 -0
  171. package/dist/types.d.ts +190 -0
  172. package/dist/types.d.ts.map +1 -0
  173. package/dist/types.js +2 -0
  174. package/dist/types.js.map +1 -0
  175. package/dist/utils/file-system.d.ts +9 -0
  176. package/dist/utils/file-system.d.ts.map +1 -0
  177. package/dist/utils/file-system.js +51 -0
  178. package/dist/utils/file-system.js.map +1 -0
  179. package/dist/utils/git-hooks.d.ts +8 -0
  180. package/dist/utils/git-hooks.d.ts.map +1 -0
  181. package/dist/utils/git-hooks.js +440 -0
  182. package/dist/utils/git-hooks.js.map +1 -0
  183. package/dist/utils/rulesignore.d.ts +9 -0
  184. package/dist/utils/rulesignore.d.ts.map +1 -0
  185. package/dist/utils/rulesignore.js +42 -0
  186. package/dist/utils/rulesignore.js.map +1 -0
  187. package/package.json +106 -0
  188. package/templates/cli/AIDER.md +49 -0
  189. package/templates/cli/AMAZON_Q.md +25 -0
  190. package/templates/cli/AUGGIE.md +32 -0
  191. package/templates/cli/CLAUDE.md +32 -0
  192. package/templates/cli/CLAUDE_CODE.md +35 -0
  193. package/templates/cli/CLINE.md +32 -0
  194. package/templates/cli/CODEBUDDY.md +20 -0
  195. package/templates/cli/CODEIUM.md +20 -0
  196. package/templates/cli/CODEX.md +21 -0
  197. package/templates/cli/CONTINUE.md +34 -0
  198. package/templates/cli/CURSOR_CLI.md +28 -0
  199. package/templates/cli/FACTORY.md +18 -0
  200. package/templates/cli/GEMINI.md +35 -0
  201. package/templates/cli/KILOCODE.md +18 -0
  202. package/templates/cli/OPENCODE.md +18 -0
  203. package/templates/cli/_GENERIC_TEMPLATE.md +29 -0
  204. package/templates/commands/rulebook-task-apply.md +67 -0
  205. package/templates/commands/rulebook-task-archive.md +70 -0
  206. package/templates/commands/rulebook-task-create.md +93 -0
  207. package/templates/commands/rulebook-task-list.md +42 -0
  208. package/templates/commands/rulebook-task-show.md +52 -0
  209. package/templates/commands/rulebook-task-validate.md +53 -0
  210. package/templates/core/AGENT_AUTOMATION.md +184 -0
  211. package/templates/core/DAG.md +304 -0
  212. package/templates/core/DOCUMENTATION_RULES.md +37 -0
  213. package/templates/core/QUALITY_ENFORCEMENT.md +68 -0
  214. package/templates/core/RULEBOOK.md +1874 -0
  215. package/templates/frameworks/ANGULAR.md +36 -0
  216. package/templates/frameworks/DJANGO.md +83 -0
  217. package/templates/frameworks/ELECTRON.md +147 -0
  218. package/templates/frameworks/FLASK.md +38 -0
  219. package/templates/frameworks/FLUTTER.md +55 -0
  220. package/templates/frameworks/JQUERY.md +32 -0
  221. package/templates/frameworks/LARAVEL.md +38 -0
  222. package/templates/frameworks/NESTJS.md +43 -0
  223. package/templates/frameworks/NEXTJS.md +127 -0
  224. package/templates/frameworks/NUXT.md +40 -0
  225. package/templates/frameworks/RAILS.md +66 -0
  226. package/templates/frameworks/REACT.md +38 -0
  227. package/templates/frameworks/REACT_NATIVE.md +47 -0
  228. package/templates/frameworks/SPRING.md +39 -0
  229. package/templates/frameworks/SYMFONY.md +36 -0
  230. package/templates/frameworks/VUE.md +36 -0
  231. package/templates/frameworks/ZEND.md +35 -0
  232. package/templates/git/CI_CD_PATTERNS.md +661 -0
  233. package/templates/git/GITHUB_ACTIONS.md +728 -0
  234. package/templates/git/GITLAB_CI.md +730 -0
  235. package/templates/git/GIT_WORKFLOW.md +1157 -0
  236. package/templates/git/SECRETS_MANAGEMENT.md +585 -0
  237. package/templates/hooks/COMMIT_MSG.md +530 -0
  238. package/templates/hooks/POST_CHECKOUT.md +546 -0
  239. package/templates/hooks/PREPARE_COMMIT_MSG.md +619 -0
  240. package/templates/hooks/PRE_COMMIT.md +414 -0
  241. package/templates/hooks/PRE_PUSH.md +601 -0
  242. package/templates/hooks/csharp-pre-commit.sh +23 -0
  243. package/templates/hooks/csharp-pre-push.sh +23 -0
  244. package/templates/hooks/dart-pre-commit.sh +30 -0
  245. package/templates/hooks/dart-pre-push.sh +25 -0
  246. package/templates/hooks/elixir-pre-commit.sh +32 -0
  247. package/templates/hooks/elixir-pre-push.sh +31 -0
  248. package/templates/hooks/erlang-pre-commit.sh +30 -0
  249. package/templates/hooks/erlang-pre-push.sh +37 -0
  250. package/templates/hooks/go-pre-commit.sh +40 -0
  251. package/templates/hooks/go-pre-push.sh +31 -0
  252. package/templates/hooks/haskell-pre-commit.sh +41 -0
  253. package/templates/hooks/haskell-pre-push.sh +37 -0
  254. package/templates/hooks/java-pre-commit.sh +34 -0
  255. package/templates/hooks/java-pre-push.sh +24 -0
  256. package/templates/hooks/kotlin-pre-commit.sh +32 -0
  257. package/templates/hooks/kotlin-pre-push.sh +16 -0
  258. package/templates/hooks/php-pre-commit.sh +36 -0
  259. package/templates/hooks/php-pre-push.sh +26 -0
  260. package/templates/hooks/python-pre-commit.sh +51 -0
  261. package/templates/hooks/python-pre-push.sh +25 -0
  262. package/templates/hooks/ruby-pre-commit.sh +33 -0
  263. package/templates/hooks/ruby-pre-push.sh +32 -0
  264. package/templates/hooks/rust-pre-commit.sh +30 -0
  265. package/templates/hooks/rust-pre-push.sh +30 -0
  266. package/templates/hooks/scala-pre-commit.sh +32 -0
  267. package/templates/hooks/scala-pre-push.sh +24 -0
  268. package/templates/hooks/swift-pre-commit.sh +25 -0
  269. package/templates/hooks/swift-pre-push.sh +23 -0
  270. package/templates/hooks/typescript-pre-commit.sh +37 -0
  271. package/templates/hooks/typescript-pre-push.sh +36 -0
  272. package/templates/ides/COPILOT.md +37 -0
  273. package/templates/ides/CURSOR.md +43 -0
  274. package/templates/ides/JETBRAINS_AI.md +35 -0
  275. package/templates/ides/REPLIT.md +36 -0
  276. package/templates/ides/TABNINE.md +29 -0
  277. package/templates/ides/VSCODE.md +40 -0
  278. package/templates/ides/WINDSURF.md +36 -0
  279. package/templates/ides/ZED.md +32 -0
  280. package/templates/languages/ADA.md +58 -0
  281. package/templates/languages/C.md +333 -0
  282. package/templates/languages/CPP.md +743 -0
  283. package/templates/languages/CSHARP.md +417 -0
  284. package/templates/languages/DART.md +332 -0
  285. package/templates/languages/ELIXIR.md +454 -0
  286. package/templates/languages/ERLANG.md +361 -0
  287. package/templates/languages/GO.md +645 -0
  288. package/templates/languages/HASKELL.md +177 -0
  289. package/templates/languages/JAVA.md +607 -0
  290. package/templates/languages/JAVASCRIPT.md +631 -0
  291. package/templates/languages/JULIA.md +97 -0
  292. package/templates/languages/KOTLIN.md +511 -0
  293. package/templates/languages/LISP.md +100 -0
  294. package/templates/languages/LUA.md +74 -0
  295. package/templates/languages/OBJECTIVEC.md +90 -0
  296. package/templates/languages/PHP.md +416 -0
  297. package/templates/languages/PYTHON.md +682 -0
  298. package/templates/languages/R.md +350 -0
  299. package/templates/languages/RUBY.md +421 -0
  300. package/templates/languages/RUST.md +477 -0
  301. package/templates/languages/SAS.md +73 -0
  302. package/templates/languages/SCALA.md +348 -0
  303. package/templates/languages/SOLIDITY.md +580 -0
  304. package/templates/languages/SQL.md +137 -0
  305. package/templates/languages/SWIFT.md +466 -0
  306. package/templates/languages/TYPESCRIPT.md +591 -0
  307. package/templates/languages/ZIG.md +265 -0
  308. package/templates/modules/ATLASSIAN.md +255 -0
  309. package/templates/modules/CONTEXT7.md +54 -0
  310. package/templates/modules/FIGMA.md +267 -0
  311. package/templates/modules/GITHUB_MCP.md +64 -0
  312. package/templates/modules/GRAFANA.md +328 -0
  313. package/templates/modules/NOTION.md +247 -0
  314. package/templates/modules/PLAYWRIGHT.md +90 -0
  315. package/templates/modules/RULEBOOK_MCP.md +156 -0
  316. package/templates/modules/SERENA.md +337 -0
  317. package/templates/modules/SUPABASE.md +223 -0
  318. package/templates/modules/SYNAP.md +69 -0
  319. package/templates/modules/VECTORIZER.md +63 -0
  320. package/templates/services/AZURE_BLOB.md +184 -0
  321. package/templates/services/CASSANDRA.md +239 -0
  322. package/templates/services/DYNAMODB.md +308 -0
  323. package/templates/services/ELASTICSEARCH.md +347 -0
  324. package/templates/services/GCS.md +178 -0
  325. package/templates/services/INFLUXDB.md +265 -0
  326. package/templates/services/KAFKA.md +341 -0
  327. package/templates/services/MARIADB.md +183 -0
  328. package/templates/services/MEMCACHED.md +242 -0
  329. package/templates/services/MINIO.md +201 -0
  330. package/templates/services/MONGODB.md +268 -0
  331. package/templates/services/MYSQL.md +358 -0
  332. package/templates/services/NEO4J.md +247 -0
  333. package/templates/services/ORACLE.md +290 -0
  334. package/templates/services/POSTGRESQL.md +326 -0
  335. package/templates/services/RABBITMQ.md +286 -0
  336. package/templates/services/REDIS.md +292 -0
  337. package/templates/services/S3.md +298 -0
  338. package/templates/services/SQLITE.md +294 -0
  339. package/templates/services/SQLSERVER.md +294 -0
  340. package/templates/workflows/codespell.yml +31 -0
  341. package/templates/workflows/cpp-lint.yml +47 -0
  342. package/templates/workflows/cpp-publish.yml +119 -0
  343. package/templates/workflows/cpp-test.yml +77 -0
  344. package/templates/workflows/dotnet-lint.yml +29 -0
  345. package/templates/workflows/dotnet-publish.yml +40 -0
  346. package/templates/workflows/dotnet-test.yml +41 -0
  347. package/templates/workflows/elixir-lint.yml +45 -0
  348. package/templates/workflows/elixir-publish.yml +49 -0
  349. package/templates/workflows/elixir-test.yml +54 -0
  350. package/templates/workflows/erlang-lint.yml +47 -0
  351. package/templates/workflows/erlang-test.yml +62 -0
  352. package/templates/workflows/go-lint.yml +39 -0
  353. package/templates/workflows/go-publish.yml +95 -0
  354. package/templates/workflows/go-test.yml +59 -0
  355. package/templates/workflows/java-lint.yml +60 -0
  356. package/templates/workflows/java-publish.yml +120 -0
  357. package/templates/workflows/java-test.yml +85 -0
  358. package/templates/workflows/kotlin-lint.yml +34 -0
  359. package/templates/workflows/kotlin-publish.yml +56 -0
  360. package/templates/workflows/kotlin-test.yml +48 -0
  361. package/templates/workflows/php-lint.yml +39 -0
  362. package/templates/workflows/php-publish.yml +50 -0
  363. package/templates/workflows/php-test.yml +54 -0
  364. package/templates/workflows/python-lint.yml +47 -0
  365. package/templates/workflows/python-publish.yml +91 -0
  366. package/templates/workflows/python-test.yml +59 -0
  367. package/templates/workflows/rust-lint.yml +54 -0
  368. package/templates/workflows/rust-publish.yml +66 -0
  369. package/templates/workflows/rust-test.yml +75 -0
  370. package/templates/workflows/solidity-lint.yml +41 -0
  371. package/templates/workflows/solidity-test.yml +47 -0
  372. package/templates/workflows/swift-lint.yml +32 -0
  373. package/templates/workflows/swift-publish.yml +58 -0
  374. package/templates/workflows/swift-test.yml +44 -0
  375. package/templates/workflows/typescript-lint.yml +61 -0
  376. package/templates/workflows/typescript-publish.yml +60 -0
  377. package/templates/workflows/typescript-test.yml +73 -0
  378. package/templates/workflows/zig-lint.yml +27 -0
  379. package/templates/workflows/zig-test.yml +40 -0
@@ -0,0 +1,1370 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import { detectProject } from '../core/detector.js';
4
+ import { promptProjectConfig, promptMergeStrategy } from './prompts.js';
5
+ import { generateFullAgents } from '../core/generator.js';
6
+ import { mergeFullAgents } from '../core/merger.js';
7
+ import { generateWorkflows, generateIDEFiles } from '../core/workflow-generator.js';
8
+ import { writeFile, createBackup, readFile, fileExists } from '../utils/file-system.js';
9
+ import { existsSync } from 'fs';
10
+ import { parseRulesIgnore } from '../utils/rulesignore.js';
11
+ import { installGitHooks } from '../utils/git-hooks.js';
12
+ import { scaffoldMinimalProject } from '../core/minimal-scaffolder.js';
13
+ import path from 'path';
14
+ import { readFileSync } from 'fs';
15
+ import { fileURLToPath } from 'url';
16
+ const FRAMEWORK_LABELS = {
17
+ nestjs: 'NestJS',
18
+ spring: 'Spring Boot',
19
+ laravel: 'Laravel',
20
+ angular: 'Angular',
21
+ react: 'React',
22
+ vue: 'Vue.js',
23
+ nuxt: 'Nuxt',
24
+ nextjs: 'Next.js',
25
+ django: 'Django',
26
+ rails: 'Ruby on Rails',
27
+ flask: 'Flask',
28
+ symfony: 'Symfony',
29
+ zend: 'Zend Framework',
30
+ jquery: 'jQuery',
31
+ reactnative: 'React Native',
32
+ flutter: 'Flutter',
33
+ electron: 'Electron',
34
+ };
35
+ // Get version from package.json
36
+ function getRulebookVersion() {
37
+ try {
38
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
39
+ const packagePath = path.join(__dirname, '..', '..', 'package.json');
40
+ const packageJson = JSON.parse(readFileSync(packagePath, 'utf-8'));
41
+ return packageJson.version;
42
+ }
43
+ catch {
44
+ return '0.12.1'; // Fallback version
45
+ }
46
+ }
47
+ export async function initCommand(options) {
48
+ try {
49
+ const cwd = process.cwd();
50
+ console.log(chalk.bold.blue('\n🔍 Rulebook Project Initializer\n'));
51
+ // Detect project
52
+ const spinner = ora('Detecting project structure...').start();
53
+ const detection = await detectProject(cwd);
54
+ spinner.succeed('Project detection complete');
55
+ // Show detection results
56
+ if (detection.languages.length > 0) {
57
+ console.log(chalk.green('\n✓ Detected languages:'));
58
+ for (const lang of detection.languages) {
59
+ console.log(` - ${lang.language} (${(lang.confidence * 100).toFixed(0)}% confidence)`);
60
+ console.log(` Indicators: ${lang.indicators.join(', ')}`);
61
+ }
62
+ }
63
+ if (detection.modules.filter((m) => m.detected).length > 0) {
64
+ console.log(chalk.green('\n✓ Detected modules:'));
65
+ for (const module of detection.modules.filter((m) => m.detected)) {
66
+ console.log(` - ${module.module} (${module.source})`);
67
+ }
68
+ }
69
+ const detectedFrameworks = detection.frameworks.filter((f) => f.detected);
70
+ if (detectedFrameworks.length > 0) {
71
+ console.log(chalk.green('\n✓ Detected frameworks:'));
72
+ for (const framework of detectedFrameworks) {
73
+ const languagesLabel = framework.languages.map((lang) => lang.toUpperCase()).join(', ');
74
+ const indicators = framework.indicators.join(', ');
75
+ const label = FRAMEWORK_LABELS[framework.framework] || framework.framework;
76
+ console.log(` - ${label} (${languagesLabel})${indicators ? ` [${indicators}]` : ''}`);
77
+ }
78
+ }
79
+ if (detection.existingAgents) {
80
+ console.log(chalk.yellow(`\n⚠ Found existing AGENTS.md with ${detection.existingAgents.blocks.length} blocks`));
81
+ for (const block of detection.existingAgents.blocks) {
82
+ console.log(` - ${block.name}`);
83
+ }
84
+ }
85
+ // Get project configuration
86
+ let config;
87
+ const cliMinimal = Boolean(options.minimal);
88
+ const cliLight = Boolean(options.light);
89
+ if (options.yes) {
90
+ config = {
91
+ languages: detection.languages.map((l) => l.language),
92
+ modules: cliMinimal ? [] : detection.modules.filter((m) => m.detected).map((m) => m.module),
93
+ frameworks: detection.frameworks.filter((f) => f.detected).map((f) => f.framework),
94
+ ides: cliMinimal ? [] : ['cursor'],
95
+ projectType: 'application',
96
+ coverageThreshold: 95,
97
+ strictDocs: true,
98
+ generateWorkflows: true,
99
+ includeGitWorkflow: true,
100
+ gitPushMode: 'manual',
101
+ installGitHooks: false,
102
+ minimal: cliMinimal,
103
+ lightMode: cliLight,
104
+ modular: true, // Enable modular /rulebook directory by default
105
+ };
106
+ console.log(chalk.blue('\nUsing detected defaults...'));
107
+ }
108
+ else {
109
+ console.log('');
110
+ config = await promptProjectConfig(detection, {
111
+ defaultMode: cliMinimal ? 'minimal' : 'full',
112
+ });
113
+ config.lightMode = cliLight;
114
+ }
115
+ const minimalMode = config.minimal ?? cliMinimal;
116
+ config.minimal = minimalMode;
117
+ config.modules = minimalMode ? [] : config.modules || [];
118
+ config.frameworks = config.frameworks || [];
119
+ config.ides = minimalMode ? [] : config.ides || ['cursor'];
120
+ config.includeGitWorkflow = config.includeGitWorkflow ?? true;
121
+ config.generateWorkflows = config.generateWorkflows ?? true;
122
+ config.modular = config.modular ?? true; // Enable modular by default
123
+ let minimalArtifacts = [];
124
+ if (minimalMode) {
125
+ minimalArtifacts = await scaffoldMinimalProject(cwd, {
126
+ projectName: path.basename(cwd),
127
+ description: 'Essential project scaffolding generated by Rulebook minimal mode.',
128
+ license: 'MIT',
129
+ });
130
+ }
131
+ const detectedHookStatus = {
132
+ preCommit: detection.gitHooks?.preCommitExists ?? false,
133
+ prePush: detection.gitHooks?.prePushExists ?? false,
134
+ };
135
+ const hookLanguages = detection.languages.length > 0
136
+ ? detection.languages
137
+ : config.languages.map((language) => ({
138
+ language: language,
139
+ confidence: 1,
140
+ indicators: [],
141
+ }));
142
+ let hooksInstalled = false;
143
+ if (config.installGitHooks) {
144
+ const hookSpinner = ora('Installing Git hooks (pre-commit & pre-push)...').start();
145
+ try {
146
+ await installGitHooks({ languages: hookLanguages, cwd });
147
+ hookSpinner.succeed('Git hooks installed successfully');
148
+ hooksInstalled = true;
149
+ }
150
+ catch (error) {
151
+ hookSpinner.fail('Failed to install Git hooks');
152
+ console.error(chalk.red(' ➤'), error instanceof Error ? error.message : error);
153
+ console.log(chalk.yellow(' ⚠ Skipping automatic hook installation. You can rerun "rulebook init" later to retry or install manually.'));
154
+ }
155
+ }
156
+ else if (!detectedHookStatus.preCommit || !detectedHookStatus.prePush) {
157
+ console.log(chalk.gray('\nℹ Git hooks were not installed automatically. Run "rulebook init" again if you want to add them later.'));
158
+ }
159
+ const gitHooksActive = hooksInstalled || (detectedHookStatus.preCommit && detectedHookStatus.prePush);
160
+ config.installGitHooks = gitHooksActive;
161
+ // Check .rulesignore
162
+ const rulesIgnore = await parseRulesIgnore(cwd);
163
+ if (rulesIgnore.patterns.length > 0) {
164
+ console.log(chalk.yellow('\n📋 Found .rulesignore with patterns:'));
165
+ for (const pattern of rulesIgnore.patterns) {
166
+ console.log(` - ${pattern}`);
167
+ }
168
+ }
169
+ // Save project configuration to .rulebook
170
+ const { createConfigManager } = await import('../core/config-manager.js');
171
+ const configManager = createConfigManager(cwd);
172
+ await configManager.updateConfig({
173
+ languages: config.languages,
174
+ frameworks: config.frameworks,
175
+ modules: config.modules,
176
+ services: config.services,
177
+ modular: config.modular ?? true,
178
+ rulebookDir: config.rulebookDir || 'rulebook',
179
+ });
180
+ // Generate or merge AGENTS.md
181
+ const agentsPath = path.join(cwd, 'AGENTS.md');
182
+ let finalContent;
183
+ if (detection.existingAgents) {
184
+ const strategy = options.yes ? 'merge' : await promptMergeStrategy();
185
+ if (strategy === 'merge') {
186
+ const mergeSpinner = ora('Merging with existing AGENTS.md...').start();
187
+ finalContent = await mergeFullAgents(detection.existingAgents, config, cwd);
188
+ // Create backup
189
+ const backupPath = await createBackup(agentsPath);
190
+ mergeSpinner.succeed(`Backup created: ${path.basename(backupPath)}`);
191
+ }
192
+ else {
193
+ const backupSpinner = ora('Creating backup...').start();
194
+ const backupPath = await createBackup(agentsPath);
195
+ backupSpinner.succeed(`Backup created: ${path.basename(backupPath)}`);
196
+ const genSpinner = ora('Generating new AGENTS.md...').start();
197
+ finalContent = await generateFullAgents(config, cwd);
198
+ genSpinner.succeed('AGENTS.md generated');
199
+ }
200
+ }
201
+ else {
202
+ const genSpinner = ora('Generating AGENTS.md...').start();
203
+ finalContent = await generateFullAgents(config, cwd);
204
+ genSpinner.succeed('AGENTS.md generated');
205
+ }
206
+ // Write AGENTS.md
207
+ await writeFile(agentsPath, finalContent);
208
+ console.log(chalk.green(`\n✅ AGENTS.md written to ${agentsPath}`));
209
+ // Generate workflows if requested
210
+ if (config.generateWorkflows) {
211
+ const workflowSpinner = ora('Generating GitHub Actions workflows...').start();
212
+ const workflows = await generateWorkflows(config, cwd, {
213
+ mode: minimalMode ? 'minimal' : 'full',
214
+ });
215
+ workflowSpinner.succeed(`Generated ${workflows.length} workflow files`);
216
+ for (const workflow of workflows) {
217
+ console.log(chalk.gray(` - ${path.relative(cwd, workflow)}`));
218
+ }
219
+ }
220
+ // Generate or update .gitignore
221
+ const gitignoreSpinner = ora('Generating/updating .gitignore...').start();
222
+ const { generateGitignore } = await import('../core/gitignore-generator.js');
223
+ const gitignoreResult = await generateGitignore(cwd, detection.languages);
224
+ if (gitignoreResult.created) {
225
+ gitignoreSpinner.succeed('.gitignore created');
226
+ }
227
+ else if (gitignoreResult.updated) {
228
+ gitignoreSpinner.succeed('.gitignore updated with missing patterns');
229
+ }
230
+ else {
231
+ gitignoreSpinner.info('.gitignore already contains all necessary patterns');
232
+ }
233
+ // Generate IDE-specific files
234
+ if (!minimalMode && config.ides.length > 0) {
235
+ const ideSpinner = ora('Generating IDE-specific files...').start();
236
+ const ideFiles = await generateIDEFiles(config, cwd);
237
+ if (ideFiles.length > 0) {
238
+ ideSpinner.succeed(`Generated ${ideFiles.length} IDE configuration files`);
239
+ for (const file of ideFiles) {
240
+ console.log(chalk.gray(` - ${path.relative(cwd, file)}`));
241
+ }
242
+ }
243
+ else {
244
+ ideSpinner.info('IDE files already exist (skipped)');
245
+ }
246
+ }
247
+ if (minimalMode && minimalArtifacts.length > 0) {
248
+ console.log(chalk.green('\n✅ Essentials created:'));
249
+ for (const artifact of minimalArtifacts) {
250
+ console.log(chalk.gray(` - ${path.relative(cwd, artifact)}`));
251
+ }
252
+ }
253
+ console.log(chalk.bold.green('\n✨ Rulebook initialization complete!\n'));
254
+ console.log(chalk.gray('Next steps:'));
255
+ console.log(chalk.gray(' 1. Review AGENTS.md'));
256
+ console.log(chalk.gray(' 2. Review generated workflows in .github/workflows/'));
257
+ console.log(chalk.gray(' 3. Create .rulesignore if needed'));
258
+ console.log(chalk.gray(' 4. Set up /docs structure'));
259
+ console.log(chalk.gray(' 5. Run your AI assistant with the new rules\n'));
260
+ }
261
+ catch (error) {
262
+ console.error(chalk.red('\n❌ Error:'), error);
263
+ process.exit(1);
264
+ }
265
+ }
266
+ export async function validateCommand() {
267
+ try {
268
+ const cwd = process.cwd();
269
+ console.log(chalk.bold.blue('\n🔍 Rulebook Project Validator\n'));
270
+ const spinner = ora('Validating project structure...').start();
271
+ // Import validator
272
+ const { validateProject } = await import('../core/validator.js');
273
+ const result = await validateProject(cwd);
274
+ spinner.stop();
275
+ // Display results
276
+ if (result.valid) {
277
+ console.log(chalk.green(`\n✅ Validation passed! Score: ${result.score}/100\n`));
278
+ }
279
+ else {
280
+ console.log(chalk.red(`\n❌ Validation failed. Score: ${result.score}/100\n`));
281
+ }
282
+ // Show errors
283
+ if (result.errors.length > 0) {
284
+ console.log(chalk.red.bold('Errors:'));
285
+ for (const error of result.errors) {
286
+ console.log(chalk.red(` ❌ ${error.category}: ${error.message}`));
287
+ if (error.file) {
288
+ console.log(chalk.gray(` File: ${error.file}`));
289
+ }
290
+ }
291
+ console.log('');
292
+ }
293
+ // Show warnings
294
+ if (result.warnings.length > 0) {
295
+ console.log(chalk.yellow.bold('Warnings:'));
296
+ for (const warning of result.warnings) {
297
+ console.log(chalk.yellow(` ⚠️ ${warning.category}: ${warning.message}`));
298
+ if (warning.file) {
299
+ console.log(chalk.gray(` File: ${warning.file}`));
300
+ }
301
+ }
302
+ console.log('');
303
+ }
304
+ // Summary
305
+ if (result.valid && result.warnings.length === 0) {
306
+ console.log(chalk.green('🎉 Perfect! Your project follows all rulebook standards.\n'));
307
+ }
308
+ else if (result.valid) {
309
+ console.log(chalk.yellow(`✅ Project is valid but has ${result.warnings.length} warnings to address.\n`));
310
+ }
311
+ else {
312
+ console.log(chalk.red(`❌ Project has ${result.errors.length} errors that must be fixed.\n`));
313
+ console.log(chalk.gray('Run "rulebook init" to set up missing standards.\n'));
314
+ }
315
+ process.exit(result.valid ? 0 : 1);
316
+ }
317
+ catch (error) {
318
+ console.error(chalk.red('\n❌ Validation error:'), error);
319
+ process.exit(1);
320
+ }
321
+ }
322
+ export async function workflowsCommand() {
323
+ try {
324
+ const cwd = process.cwd();
325
+ console.log(chalk.bold.blue('\n📦 Generating GitHub Actions Workflows\n'));
326
+ // Detect project
327
+ const spinner = ora('Detecting project languages...').start();
328
+ const detection = await detectProject(cwd);
329
+ spinner.succeed('Detection complete');
330
+ if (detection.languages.length === 0) {
331
+ console.log(chalk.yellow('\n⚠ No languages detected. Cannot generate workflows.'));
332
+ console.log(chalk.gray('Ensure you have project files (Cargo.toml, package.json, etc.)\n'));
333
+ return;
334
+ }
335
+ const config = {
336
+ languages: detection.languages.map((l) => l.language),
337
+ modules: [],
338
+ ides: [],
339
+ projectType: 'application',
340
+ coverageThreshold: 95,
341
+ strictDocs: true,
342
+ generateWorkflows: true,
343
+ };
344
+ const workflowSpinner = ora('Generating workflows...').start();
345
+ const workflows = await generateWorkflows(config, cwd);
346
+ workflowSpinner.succeed(`Generated ${workflows.length} workflow files`);
347
+ console.log(chalk.green('\n✅ Workflows generated:\n'));
348
+ for (const workflow of workflows) {
349
+ console.log(chalk.gray(` - ${path.relative(cwd, workflow)}`));
350
+ }
351
+ console.log(chalk.bold.green('\n✨ Done!\n'));
352
+ }
353
+ catch (error) {
354
+ console.error(chalk.red('\n❌ Error:'), error);
355
+ process.exit(1);
356
+ }
357
+ }
358
+ export async function checkDepsCommand() {
359
+ try {
360
+ const cwd = process.cwd();
361
+ console.log(chalk.bold.blue('\n🔍 Checking Dependencies\n'));
362
+ const spinner = ora('Analyzing dependencies...').start();
363
+ const { checkDependencies } = await import('../core/dependency-checker.js');
364
+ const result = await checkDependencies(cwd);
365
+ spinner.succeed('Analysis complete');
366
+ console.log('');
367
+ console.log(chalk.bold('Dependency Summary:'));
368
+ console.log(chalk.gray(` Total: ${result.total}`));
369
+ console.log(chalk.green(` Up-to-date: ${result.upToDate}`));
370
+ if (result.outdated.length > 0) {
371
+ console.log(chalk.yellow(` Outdated: ${result.outdated.length}`));
372
+ }
373
+ if (result.vulnerable.length > 0) {
374
+ console.log(chalk.red(` Vulnerable: ${result.vulnerable.length}`));
375
+ }
376
+ // Show outdated
377
+ if (result.outdated.length > 0) {
378
+ console.log('');
379
+ console.log(chalk.yellow.bold('Outdated Dependencies:'));
380
+ for (const dep of result.outdated.slice(0, 10)) {
381
+ console.log(chalk.yellow(` ↑ ${dep.name}: ${dep.current} → ${dep.latest}`));
382
+ }
383
+ if (result.outdated.length > 10) {
384
+ console.log(chalk.gray(` ... and ${result.outdated.length - 10} more`));
385
+ }
386
+ }
387
+ // Show vulnerable
388
+ if (result.vulnerable.length > 0) {
389
+ console.log('');
390
+ console.log(chalk.red.bold('Vulnerable Dependencies:'));
391
+ for (const vuln of result.vulnerable.slice(0, 5)) {
392
+ console.log(chalk.red(` ❌ ${vuln.name} (${vuln.severity}): ${vuln.title}`));
393
+ }
394
+ if (result.vulnerable.length > 5) {
395
+ console.log(chalk.gray(` ... and ${result.vulnerable.length - 5} more`));
396
+ }
397
+ console.log('');
398
+ console.log(chalk.red.bold('⚠️ SECURITY: Update vulnerable dependencies immediately!'));
399
+ }
400
+ console.log('');
401
+ if (result.outdated.length === 0 && result.vulnerable.length === 0) {
402
+ console.log(chalk.green('✅ All dependencies are up-to-date and secure!\n'));
403
+ }
404
+ }
405
+ catch (error) {
406
+ console.error(chalk.red('\n❌ Error checking dependencies:'), error);
407
+ process.exit(1);
408
+ }
409
+ }
410
+ export async function checkCoverageCommand(options) {
411
+ try {
412
+ const cwd = process.cwd();
413
+ const threshold = options.threshold || 95;
414
+ console.log(chalk.bold.blue('\n📊 Checking Test Coverage\n'));
415
+ const spinner = ora('Running coverage analysis...').start();
416
+ const { checkCoverage } = await import('../core/coverage-checker.js');
417
+ const result = await checkCoverage(cwd, threshold);
418
+ spinner.succeed('Coverage analysis complete');
419
+ console.log('');
420
+ console.log(chalk.bold('Coverage Summary:'));
421
+ console.log(chalk.gray(` Threshold: ${threshold}%`));
422
+ console.log(result.meetsThreshold
423
+ ? chalk.green(` Actual: ${result.percentage.toFixed(2)}% ✅`)
424
+ : chalk.red(` Actual: ${result.percentage.toFixed(2)}% ❌`));
425
+ if (result.details) {
426
+ console.log('');
427
+ console.log(chalk.bold('Details:'));
428
+ if (result.details.lines) {
429
+ console.log(chalk.gray(` Lines: ${result.details.lines.toFixed(2)}%`));
430
+ }
431
+ if (result.details.statements) {
432
+ console.log(chalk.gray(` Statements: ${result.details.statements.toFixed(2)}%`));
433
+ }
434
+ if (result.details.functions) {
435
+ console.log(chalk.gray(` Functions: ${result.details.functions.toFixed(2)}%`));
436
+ }
437
+ if (result.details.branches) {
438
+ console.log(chalk.gray(` Branches: ${result.details.branches.toFixed(2)}%`));
439
+ }
440
+ }
441
+ console.log('');
442
+ if (result.meetsThreshold) {
443
+ console.log(chalk.green(`✅ Coverage meets threshold of ${threshold}%!\n`));
444
+ }
445
+ else {
446
+ console.log(chalk.red(`❌ Coverage ${result.percentage.toFixed(2)}% is below threshold ${threshold}%\n`));
447
+ console.log(chalk.gray('Add more tests to increase coverage.\n'));
448
+ process.exit(1);
449
+ }
450
+ }
451
+ catch (error) {
452
+ console.error(chalk.red('\n❌ Error checking coverage:'), error);
453
+ process.exit(1);
454
+ }
455
+ }
456
+ export async function generateDocsCommand(options) {
457
+ try {
458
+ const cwd = process.cwd();
459
+ console.log(chalk.bold.blue('\n📚 Generate Documentation Structure\n'));
460
+ let config;
461
+ if (options.yes) {
462
+ config = {
463
+ projectName: path.basename(cwd),
464
+ description: 'A modern software project',
465
+ author: 'Your Name',
466
+ email: '',
467
+ license: 'MIT',
468
+ };
469
+ console.log(chalk.blue('Using defaults...\n'));
470
+ }
471
+ else {
472
+ const { promptDocsConfig } = await import('./docs-prompts.js');
473
+ config = await promptDocsConfig();
474
+ }
475
+ const spinner = ora('Generating documentation structure...').start();
476
+ const { generateDocsStructure } = await import('../core/docs-generator.js');
477
+ const generatedFiles = await generateDocsStructure(config, cwd);
478
+ spinner.succeed(`Generated ${generatedFiles.length} files`);
479
+ console.log('');
480
+ console.log(chalk.green('✅ Files created:\n'));
481
+ for (const file of generatedFiles) {
482
+ console.log(chalk.gray(` - ${path.relative(cwd, file)}`));
483
+ }
484
+ console.log('');
485
+ console.log(chalk.bold.green('✨ Documentation structure ready!\n'));
486
+ console.log(chalk.gray('Next steps:'));
487
+ console.log(chalk.gray(' 1. Review and customize generated files'));
488
+ console.log(chalk.gray(' 2. Add your project-specific content'));
489
+ console.log(chalk.gray(' 3. Update ROADMAP.md with your milestones'));
490
+ console.log(chalk.gray(' 4. Document architecture in ARCHITECTURE.md\n'));
491
+ }
492
+ catch (error) {
493
+ console.error(chalk.red('\n❌ Error generating docs:'), error);
494
+ process.exit(1);
495
+ }
496
+ }
497
+ export async function versionCommand(options) {
498
+ try {
499
+ const cwd = process.cwd();
500
+ console.log(chalk.bold.blue('\n📦 Version Bump\n'));
501
+ const { bumpProjectVersion } = await import('../core/version-bumper.js');
502
+ const spinner = ora('Bumping version...').start();
503
+ const result = await bumpProjectVersion(cwd, options.type);
504
+ spinner.succeed(`Version bumped: ${result.oldVersion} → ${result.newVersion}`);
505
+ console.log('');
506
+ console.log(chalk.green('✅ Files updated:\n'));
507
+ result.filesUpdated.forEach((file) => {
508
+ console.log(chalk.gray(` - ${file}`));
509
+ });
510
+ console.log('');
511
+ console.log(chalk.yellow('Next steps:'));
512
+ console.log(chalk.gray(' 1. Review changes'));
513
+ console.log(chalk.gray(' 2. Update CHANGELOG.md (or run: rulebook changelog)'));
514
+ console.log(chalk.gray(` 3. Commit: git add . && git commit -m "chore: Release version ${result.newVersion}"`));
515
+ console.log(chalk.gray(` 4. Tag: git tag -a v${result.newVersion} -m "Release v${result.newVersion}"`));
516
+ }
517
+ catch (error) {
518
+ console.error(chalk.red('\n❌ Error:'), error.message);
519
+ process.exit(1);
520
+ }
521
+ }
522
+ export async function changelogCommand(options) {
523
+ try {
524
+ const cwd = process.cwd();
525
+ console.log(chalk.bold.blue('\n📝 Changelog Generation\n'));
526
+ const { generateChangelog, getCurrentVersion } = await import('../core/changelog-generator.js');
527
+ const version = options.version || (await getCurrentVersion(cwd));
528
+ if (!version) {
529
+ console.error(chalk.red('❌ Could not determine version'));
530
+ console.log(chalk.gray(' Specify version with --version flag'));
531
+ process.exit(1);
532
+ }
533
+ const spinner = ora('Generating changelog from commits...').start();
534
+ const section = await generateChangelog(cwd, version);
535
+ spinner.succeed(`Changelog generated for version ${version}`);
536
+ console.log('');
537
+ console.log(chalk.green('✅ Changelog sections:\n'));
538
+ if (section.breaking.length > 0) {
539
+ console.log(chalk.red(' Breaking Changes: ') + section.breaking.length);
540
+ }
541
+ if (section.added.length > 0) {
542
+ console.log(chalk.green(' Added: ') + section.added.length);
543
+ }
544
+ if (section.changed.length > 0) {
545
+ console.log(chalk.blue(' Changed: ') + section.changed.length);
546
+ }
547
+ if (section.fixed.length > 0) {
548
+ console.log(chalk.yellow(' Fixed: ') + section.fixed.length);
549
+ }
550
+ console.log('');
551
+ console.log(chalk.gray('CHANGELOG.md has been updated'));
552
+ console.log(chalk.gray('Review and edit as needed before committing'));
553
+ }
554
+ catch (error) {
555
+ console.error(chalk.red('\n❌ Error:'), error.message);
556
+ process.exit(1);
557
+ }
558
+ }
559
+ export async function healthCommand() {
560
+ try {
561
+ const cwd = process.cwd();
562
+ console.log(chalk.bold.blue('\n🏥 Project Health Check\n'));
563
+ const { calculateHealthScore, getHealthGrade } = await import('../core/health-scorer.js');
564
+ const spinner = ora('Analyzing project health...').start();
565
+ const health = await calculateHealthScore(cwd);
566
+ spinner.succeed('Health analysis complete');
567
+ console.log('');
568
+ const grade = getHealthGrade(health.overall);
569
+ console.log(chalk.bold(`Overall Health Score: ${health.overall}/100 (${grade})`));
570
+ console.log('');
571
+ console.log(chalk.bold('Category Scores:\n'));
572
+ console.log(` 📝 Documentation: ${health.categories.documentation}/100`);
573
+ console.log(` 🧪 Testing: ${health.categories.testing}/100`);
574
+ console.log(` 🎨 Code Quality: ${health.categories.quality}/100`);
575
+ console.log(` 🔒 Security: ${health.categories.security}/100`);
576
+ console.log(` 🔄 CI/CD: ${health.categories.cicd}/100`);
577
+ console.log(` 📦 Dependencies: ${health.categories.dependencies}/100`);
578
+ console.log('');
579
+ if (health.recommendations.length > 0) {
580
+ console.log(chalk.bold.yellow('Recommendations:\n'));
581
+ health.recommendations.forEach((rec) => {
582
+ console.log(chalk.yellow(` ${rec}`));
583
+ });
584
+ console.log('');
585
+ }
586
+ if (health.overall >= 90) {
587
+ console.log(chalk.green('🌟 Excellent! Your project is in great shape!'));
588
+ }
589
+ else if (health.overall >= 70) {
590
+ console.log(chalk.blue('👍 Good project health. A few improvements suggested.'));
591
+ }
592
+ else {
593
+ console.log(chalk.yellow('⚠️ Project health needs improvement.'));
594
+ console.log(chalk.gray(' Run: rulebook fix'));
595
+ console.log(chalk.gray(' To auto-fix common issues.'));
596
+ }
597
+ }
598
+ catch (error) {
599
+ console.error(chalk.red('\n❌ Error:'), error.message);
600
+ process.exit(1);
601
+ }
602
+ }
603
+ export async function fixCommand() {
604
+ try {
605
+ const cwd = process.cwd();
606
+ console.log(chalk.bold.blue('\n🔧 Auto-Fix Common Issues\n'));
607
+ const { autoFixProject, autoFixLint } = await import('../core/auto-fixer.js');
608
+ const spinner = ora('Analyzing and fixing issues...').start();
609
+ const result = await autoFixProject(cwd);
610
+ spinner.succeed('Auto-fix complete');
611
+ console.log('');
612
+ if (result.applied.length > 0) {
613
+ console.log(chalk.green('✅ Fixed:\n'));
614
+ result.applied.forEach((fix) => {
615
+ console.log(chalk.gray(` - ${fix}`));
616
+ });
617
+ console.log('');
618
+ }
619
+ if (result.skipped.length > 0) {
620
+ console.log(chalk.blue('ℹ️ Skipped:\n'));
621
+ result.skipped.forEach((skip) => {
622
+ console.log(chalk.gray(` - ${skip}`));
623
+ });
624
+ console.log('');
625
+ }
626
+ if (result.failed.length > 0) {
627
+ console.log(chalk.yellow('⚠️ Failed:\n'));
628
+ result.failed.forEach((fail) => {
629
+ console.log(chalk.gray(` - ${fail}`));
630
+ });
631
+ console.log('');
632
+ }
633
+ // Try to fix lint errors
634
+ console.log(chalk.blue('🎨 Attempting to fix lint errors...\n'));
635
+ const lintFixed = await autoFixLint(cwd);
636
+ if (lintFixed) {
637
+ console.log(chalk.green('✅ Lint errors fixed'));
638
+ }
639
+ else {
640
+ console.log(chalk.gray('ℹ️ No lint auto-fix available'));
641
+ }
642
+ console.log('');
643
+ console.log(chalk.bold.green('✨ Auto-fix complete!\n'));
644
+ console.log(chalk.gray('Run: rulebook health'));
645
+ console.log(chalk.gray('To check updated health score.'));
646
+ }
647
+ catch (error) {
648
+ console.error(chalk.red('\n❌ Error:'), error.message);
649
+ process.exit(1);
650
+ }
651
+ }
652
+ export async function watcherCommand() {
653
+ try {
654
+ const cwd = process.cwd();
655
+ const { startWatcher } = await import('../core/watcher.js');
656
+ console.log(chalk.bold.blue('\n🚀 Starting Modern Console Watcher\n'));
657
+ console.log(chalk.gray('Full-screen interface with system monitoring'));
658
+ console.log(chalk.gray('Press Ctrl+C or F10 to exit\n'));
659
+ await startWatcher(cwd);
660
+ }
661
+ catch (error) {
662
+ console.error(chalk.red('\n❌ Watcher error:'), error);
663
+ process.exit(1);
664
+ }
665
+ }
666
+ export async function agentCommand(options) {
667
+ try {
668
+ const cwd = process.cwd();
669
+ const { startAgent } = await import('../core/agent-manager.js');
670
+ console.log(chalk.bold.blue('\n🤖 Starting Rulebook Agent\n'));
671
+ const agentOptions = {
672
+ dryRun: options.dryRun || false,
673
+ tool: options.tool,
674
+ maxIterations: options.iterations || 10,
675
+ watchMode: options.watch || false,
676
+ };
677
+ if (agentOptions.dryRun) {
678
+ console.log(chalk.yellow('🔍 DRY RUN MODE - No actual changes will be made\n'));
679
+ }
680
+ await startAgent(cwd, agentOptions);
681
+ }
682
+ catch (error) {
683
+ console.error(chalk.red('\n❌ Agent error:'), error);
684
+ process.exit(1);
685
+ }
686
+ }
687
+ export async function configCommand(options) {
688
+ try {
689
+ const cwd = process.cwd();
690
+ const { createConfigManager } = await import('../core/config-manager.js');
691
+ const configManager = createConfigManager(cwd);
692
+ if (options.show) {
693
+ const summary = await configManager.getConfigSummary();
694
+ console.log(chalk.bold.blue('\n⚙️ Rulebook Configuration\n'));
695
+ console.log(chalk.white(`Version: ${summary.version}`));
696
+ console.log(chalk.white(`Project ID: ${summary.projectId}`));
697
+ console.log(chalk.white(`Coverage Threshold: ${summary.coverageThreshold}%`));
698
+ console.log(chalk.white(`CLI Tools: ${summary.cliTools.join(', ') || 'None detected'}`));
699
+ console.log(chalk.white(`Enabled Features: ${summary.enabledFeatures.join(', ')}`));
700
+ }
701
+ else if (options.feature && typeof options.enable === 'boolean') {
702
+ await configManager.toggleFeature(options.feature, options.enable);
703
+ console.log(chalk.green(`✅ Feature '${options.feature}' ${options.enable ? 'enabled' : 'disabled'}`));
704
+ }
705
+ else if (options.set) {
706
+ const [key, value] = options.set.split('=');
707
+ if (!key || !value) {
708
+ console.error(chalk.red('Invalid set format. Use: --set key=value'));
709
+ process.exit(1);
710
+ }
711
+ // Handle different value types
712
+ let parsedValue = value;
713
+ if (value === 'true')
714
+ parsedValue = true;
715
+ else if (value === 'false')
716
+ parsedValue = false;
717
+ else if (!isNaN(Number(value)))
718
+ parsedValue = Number(value);
719
+ await configManager.updateConfig({ [key]: parsedValue });
720
+ console.log(chalk.green(`✅ Configuration updated: ${key} = ${value}`));
721
+ }
722
+ else {
723
+ console.log(chalk.bold.blue('\n⚙️ Rulebook Configuration\n'));
724
+ console.log(chalk.gray('Available commands:'));
725
+ console.log(chalk.gray(' --show Show current configuration'));
726
+ console.log(chalk.gray(' --set key=value Set configuration value'));
727
+ console.log(chalk.gray(' --feature name --enable Enable a feature'));
728
+ console.log(chalk.gray(' --feature name --disable Disable a feature'));
729
+ }
730
+ }
731
+ catch (error) {
732
+ console.error(chalk.red('\n❌ Config error:'), error);
733
+ process.exit(1);
734
+ }
735
+ }
736
+ // Task management commands using Rulebook task system
737
+ export async function taskCreateCommand(taskId) {
738
+ try {
739
+ const cwd = process.cwd();
740
+ const { createTaskManager } = await import('../core/task-manager.js');
741
+ const { createConfigManager } = await import('../core/config-manager.js');
742
+ const configManager = createConfigManager(cwd);
743
+ const config = await configManager.loadConfig();
744
+ const rulebookDir = config.rulebookDir || 'rulebook';
745
+ const taskManager = createTaskManager(cwd, rulebookDir);
746
+ await taskManager.createTask(taskId);
747
+ console.log(chalk.green(`✅ Task ${taskId} created successfully`));
748
+ console.log(chalk.gray(`Location: ${rulebookDir}/tasks/${taskId}/`));
749
+ console.log(chalk.yellow('\n⚠️ Remember to:'));
750
+ console.log(chalk.gray(' 1. Check Context7 MCP for OpenSpec format requirements'));
751
+ console.log(chalk.gray(' 2. Fill in proposal.md (minimum 20 characters in "Why" section)'));
752
+ console.log(chalk.gray(' 3. Add tasks to tasks.md'));
753
+ console.log(chalk.gray(' 4. Create spec deltas in specs/*/spec.md'));
754
+ console.log(chalk.gray(' 5. Validate with: rulebook task validate ' + taskId));
755
+ }
756
+ catch (error) {
757
+ console.error(chalk.red(`❌ Failed to create task: ${error.message}`));
758
+ process.exit(1);
759
+ }
760
+ }
761
+ export async function taskListCommand(includeArchived = false) {
762
+ try {
763
+ const cwd = process.cwd();
764
+ const { createTaskManager } = await import('../core/task-manager.js');
765
+ const { createConfigManager } = await import('../core/config-manager.js');
766
+ const configManager = createConfigManager(cwd);
767
+ const config = await configManager.loadConfig();
768
+ const rulebookDir = config.rulebookDir || 'rulebook';
769
+ const taskManager = createTaskManager(cwd, rulebookDir);
770
+ const tasks = await taskManager.listTasks(includeArchived);
771
+ if (tasks.length === 0) {
772
+ console.log(chalk.gray('No tasks found'));
773
+ return;
774
+ }
775
+ console.log(chalk.bold.blue('\n📋 Rulebook Tasks\n'));
776
+ const activeTasks = tasks.filter((t) => !t.archivedAt);
777
+ const archivedTasks = tasks.filter((t) => t.archivedAt);
778
+ if (activeTasks.length > 0) {
779
+ console.log(chalk.bold('Active Tasks:'));
780
+ for (const task of activeTasks) {
781
+ const statusColor = task.status === 'completed'
782
+ ? chalk.green
783
+ : task.status === 'in-progress'
784
+ ? chalk.yellow
785
+ : task.status === 'blocked'
786
+ ? chalk.red
787
+ : chalk.gray;
788
+ console.log(` ${statusColor(task.status.padEnd(12))} ${chalk.white(task.id)} - ${chalk.gray(task.title)}`);
789
+ }
790
+ console.log('');
791
+ }
792
+ if (includeArchived && archivedTasks.length > 0) {
793
+ console.log(chalk.bold('Archived Tasks:'));
794
+ for (const task of archivedTasks) {
795
+ console.log(` ${chalk.gray('archived'.padEnd(12))} ${chalk.white(task.id)} - ${chalk.gray(task.title)} ${chalk.dim(`(${task.archivedAt})`)}`);
796
+ }
797
+ console.log('');
798
+ }
799
+ }
800
+ catch (error) {
801
+ console.error(chalk.red(`❌ Failed to list tasks: ${error.message}`));
802
+ process.exit(1);
803
+ }
804
+ }
805
+ export async function taskShowCommand(taskId) {
806
+ try {
807
+ const cwd = process.cwd();
808
+ const { createTaskManager } = await import('../core/task-manager.js');
809
+ const { createConfigManager } = await import('../core/config-manager.js');
810
+ const configManager = createConfigManager(cwd);
811
+ const config = await configManager.loadConfig();
812
+ const rulebookDir = config.rulebookDir || 'rulebook';
813
+ const taskManager = createTaskManager(cwd, rulebookDir);
814
+ const task = await taskManager.showTask(taskId);
815
+ if (!task) {
816
+ console.error(chalk.red(`❌ Task ${taskId} not found`));
817
+ process.exit(1);
818
+ return;
819
+ }
820
+ console.log(chalk.bold.blue(`\n📋 Task: ${task.id}\n`));
821
+ console.log(chalk.white(`Title: ${task.title}`));
822
+ console.log(chalk.gray(`Status: ${task.status}`));
823
+ console.log(chalk.gray(`Created: ${task.createdAt}`));
824
+ console.log(chalk.gray(`Updated: ${task.updatedAt}`));
825
+ if (task.archivedAt) {
826
+ console.log(chalk.gray(`Archived: ${task.archivedAt}`));
827
+ }
828
+ console.log('');
829
+ if (task.proposal) {
830
+ console.log(chalk.bold('Proposal:'));
831
+ console.log(chalk.gray(task.proposal.substring(0, 500) + (task.proposal.length > 500 ? '...' : '')));
832
+ console.log('');
833
+ }
834
+ if (task.specs && Object.keys(task.specs).length > 0) {
835
+ console.log(chalk.bold('Specs:'));
836
+ for (const [module, spec] of Object.entries(task.specs)) {
837
+ console.log(chalk.gray(` ${module}/spec.md (${spec.length} chars)`));
838
+ }
839
+ console.log('');
840
+ }
841
+ }
842
+ catch (error) {
843
+ console.error(chalk.red(`❌ Failed to show task: ${error.message}`));
844
+ process.exit(1);
845
+ }
846
+ }
847
+ export async function taskValidateCommand(taskId) {
848
+ try {
849
+ const cwd = process.cwd();
850
+ const { createTaskManager } = await import('../core/task-manager.js');
851
+ const { createConfigManager } = await import('../core/config-manager.js');
852
+ const configManager = createConfigManager(cwd);
853
+ const config = await configManager.loadConfig();
854
+ const rulebookDir = config.rulebookDir || 'rulebook';
855
+ const taskManager = createTaskManager(cwd, rulebookDir);
856
+ const validation = await taskManager.validateTask(taskId);
857
+ if (validation.valid) {
858
+ console.log(chalk.green(`✅ Task ${taskId} is valid`));
859
+ if (validation.warnings.length > 0) {
860
+ console.log(chalk.yellow('\n⚠️ Warnings:'));
861
+ for (const warning of validation.warnings) {
862
+ console.log(chalk.yellow(` - ${warning}`));
863
+ }
864
+ }
865
+ }
866
+ else {
867
+ console.log(chalk.red(`❌ Task ${taskId} validation failed\n`));
868
+ console.log(chalk.red('Errors:'));
869
+ for (const error of validation.errors) {
870
+ console.log(chalk.red(` - ${error}`));
871
+ }
872
+ if (validation.warnings.length > 0) {
873
+ console.log(chalk.yellow('\n⚠️ Warnings:'));
874
+ for (const warning of validation.warnings) {
875
+ console.log(chalk.yellow(` - ${warning}`));
876
+ }
877
+ }
878
+ process.exit(1);
879
+ }
880
+ }
881
+ catch (error) {
882
+ console.error(chalk.red(`❌ Failed to validate task: ${error.message}`));
883
+ process.exit(1);
884
+ }
885
+ }
886
+ export async function taskArchiveCommand(taskId, skipValidation = false) {
887
+ try {
888
+ const cwd = process.cwd();
889
+ const { createTaskManager } = await import('../core/task-manager.js');
890
+ const { createConfigManager } = await import('../core/config-manager.js');
891
+ const configManager = createConfigManager(cwd);
892
+ const config = await configManager.loadConfig();
893
+ const rulebookDir = config.rulebookDir || 'rulebook';
894
+ const taskManager = createTaskManager(cwd, rulebookDir);
895
+ await taskManager.archiveTask(taskId, skipValidation);
896
+ console.log(chalk.green(`✅ Task ${taskId} archived successfully`));
897
+ }
898
+ catch (error) {
899
+ console.error(chalk.red(`❌ Failed to archive task: ${error.message}`));
900
+ process.exit(1);
901
+ }
902
+ }
903
+ /**
904
+ * Initialize MCP configuration in .rulebook file
905
+ * Adds mcp block to .rulebook and creates/updates .cursor/mcp.json
906
+ */
907
+ export async function mcpInitCommand() {
908
+ const { findRulebookFile } = await import('../mcp/rulebook-server.js');
909
+ const { existsSync, readFileSync, writeFileSync } = await import('fs');
910
+ const { join, dirname } = await import('path');
911
+ const { createConfigManager } = await import('../core/config-manager.js');
912
+ try {
913
+ // Find or create .rulebook file
914
+ const cwd = process.cwd();
915
+ let rulebookPath = findRulebookFile(cwd);
916
+ if (!rulebookPath) {
917
+ // Create new .rulebook file
918
+ rulebookPath = join(cwd, '.rulebook');
919
+ const configManager = createConfigManager(cwd);
920
+ await configManager.initializeConfig();
921
+ }
922
+ const projectRoot = dirname(rulebookPath);
923
+ // Load existing config
924
+ let config = {};
925
+ if (existsSync(rulebookPath)) {
926
+ const raw = readFileSync(rulebookPath, 'utf8');
927
+ config = JSON.parse(raw);
928
+ }
929
+ // Add/update mcp block
930
+ config.mcp = config.mcp ?? {};
931
+ if (config.mcp.enabled === undefined)
932
+ config.mcp.enabled = true;
933
+ if (!config.mcp.tasksDir)
934
+ config.mcp.tasksDir = 'rulebook/tasks';
935
+ if (!config.mcp.archiveDir)
936
+ config.mcp.archiveDir = 'rulebook/archive';
937
+ // Save updated config
938
+ writeFileSync(rulebookPath, JSON.stringify(config, null, 2) + '\n');
939
+ // Create/update .cursor/mcp.json if .cursor directory exists
940
+ const cursorDir = join(projectRoot, '.cursor');
941
+ if (existsSync(cursorDir)) {
942
+ const mcpJsonPath = join(cursorDir, 'mcp.json');
943
+ let mcpConfig = { mcpServers: {} };
944
+ if (existsSync(mcpJsonPath)) {
945
+ const raw = readFileSync(mcpJsonPath, 'utf8');
946
+ mcpConfig = JSON.parse(raw);
947
+ }
948
+ mcpConfig.mcpServers = mcpConfig.mcpServers ?? {};
949
+ mcpConfig.mcpServers.rulebook = {
950
+ command: 'npx',
951
+ args: ['-y', '@hivehub/rulebook@latest', 'mcp-server'],
952
+ };
953
+ writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + '\n');
954
+ console.log(chalk.green('✓ Rulebook MCP initialized'));
955
+ console.log(chalk.gray(` • Updated .rulebook with MCP configuration`));
956
+ console.log(chalk.gray(` • Updated .cursor/mcp.json`));
957
+ console.log(chalk.gray(` • MCP server will find .rulebook automatically`));
958
+ }
959
+ else {
960
+ console.log(chalk.green('✓ Rulebook MCP initialized'));
961
+ console.log(chalk.gray(` • Updated .rulebook with MCP configuration`));
962
+ console.log(chalk.gray(` • To use with Cursor, create .cursor/mcp.json manually`));
963
+ }
964
+ }
965
+ catch (error) {
966
+ console.error(chalk.red(`\n❌ Failed to initialize MCP: ${error.message}`));
967
+ console.error(error.stack);
968
+ process.exit(1);
969
+ }
970
+ }
971
+ export async function mcpServerCommand() {
972
+ try {
973
+ const { startRulebookMcpServer } = await import('../mcp/rulebook-server.js');
974
+ // CRITICAL: In stdio mode, stdout MUST contain ONLY JSON-RPC 2.0 messages
975
+ // stdout must contain ONLY JSON-RPC 2.0 messages for MCP protocol
976
+ // All logs must go to stderr
977
+ // Use environment variable for debug: RULEBOOK_MCP_DEBUG=1
978
+ if (process.env.RULEBOOK_MCP_DEBUG === '1') {
979
+ console.error(chalk.gray('[rulebook-mcp] Starting MCP server with stdio transport'));
980
+ console.error(chalk.gray('[rulebook-mcp] Server will find .rulebook automatically'));
981
+ }
982
+ await startRulebookMcpServer();
983
+ }
984
+ catch (error) {
985
+ // Errors always go to stderr
986
+ console.error(chalk.red(`\n❌ Failed to start MCP server: ${error.message}`));
987
+ console.error(error.stack);
988
+ process.exit(1);
989
+ }
990
+ }
991
+ // Legacy tasks command (deprecated - use task commands instead)
992
+ export async function tasksCommand(options) {
993
+ console.log(chalk.yellow('⚠️ The `tasks` command is deprecated. Use `rulebook task` commands instead.'));
994
+ console.log(chalk.gray(' - rulebook task list'));
995
+ console.log(chalk.gray(' - rulebook task show <task-id>'));
996
+ console.log(chalk.gray(' - rulebook task create <task-id>'));
997
+ console.log(chalk.gray(' - rulebook task validate <task-id>'));
998
+ console.log(chalk.gray(' - rulebook task archive <task-id>'));
999
+ if (options.tree || options.current || options.status) {
1000
+ console.log(chalk.red('\n❌ Legacy OpenSpec commands are no longer supported.'));
1001
+ console.log(chalk.yellow('Please migrate to the new Rulebook task system.'));
1002
+ process.exit(1);
1003
+ }
1004
+ // Fallback to list tasks
1005
+ await taskListCommand(false);
1006
+ }
1007
+ export async function updateCommand(options) {
1008
+ try {
1009
+ const cwd = process.cwd();
1010
+ console.log(chalk.bold.blue('\n🔄 Rulebook Update Tool\n'));
1011
+ console.log(chalk.gray('This will update your AGENTS.md and .rulebook to the latest version\n'));
1012
+ // Detect project
1013
+ const spinner = ora('Detecting project structure...').start();
1014
+ const detection = await detectProject(cwd);
1015
+ spinner.succeed('Project detection complete');
1016
+ // Show detected languages
1017
+ if (detection.languages.length > 0) {
1018
+ console.log(chalk.green('\n✓ Detected languages:'));
1019
+ for (const lang of detection.languages) {
1020
+ console.log(` - ${lang.language} (${(lang.confidence * 100).toFixed(0)}% confidence)`);
1021
+ }
1022
+ }
1023
+ // Check for existing AGENTS.md
1024
+ if (!detection.existingAgents) {
1025
+ console.log(chalk.yellow('\n⚠ No AGENTS.md found. Use "rulebook init" instead.'));
1026
+ process.exit(0);
1027
+ }
1028
+ console.log(chalk.green(`\n✓ Found existing AGENTS.md with ${detection.existingAgents.blocks.length} blocks`));
1029
+ // Get existing blocks to preserve user customizations
1030
+ const existingBlocks = detection.existingAgents.blocks.map((b) => b.name);
1031
+ console.log(chalk.gray(` Existing blocks: ${existingBlocks.join(', ')}`));
1032
+ let inquirerModule = null;
1033
+ if (!options.yes) {
1034
+ inquirerModule = (await import('inquirer')).default;
1035
+ const { confirm } = await inquirerModule.prompt([
1036
+ {
1037
+ type: 'confirm',
1038
+ name: 'confirm',
1039
+ message: 'Update AGENTS.md and .rulebook with latest templates?',
1040
+ default: true,
1041
+ },
1042
+ ]);
1043
+ if (!confirm) {
1044
+ console.log(chalk.yellow('\nUpdate cancelled'));
1045
+ process.exit(0);
1046
+ }
1047
+ }
1048
+ const hasPreCommit = detection.gitHooks?.preCommitExists ?? false;
1049
+ const hasPrePush = detection.gitHooks?.prePushExists ?? false;
1050
+ const missingHooks = !hasPreCommit || !hasPrePush;
1051
+ let installHooksOnUpdate = false;
1052
+ let hooksInstalledOnUpdate = false;
1053
+ if (missingHooks) {
1054
+ if (options.yes) {
1055
+ console.log(chalk.yellow('\n⚠ Git hooks are missing. Re-run "rulebook update" without --yes to install automated hooks or install them manually.'));
1056
+ }
1057
+ else {
1058
+ if (!inquirerModule) {
1059
+ inquirerModule = (await import('inquirer')).default;
1060
+ }
1061
+ const { installHooks } = await inquirerModule.prompt([
1062
+ {
1063
+ type: 'confirm',
1064
+ name: 'installHooks',
1065
+ message: `Install Git hooks for automated quality checks? Missing: ${hasPreCommit ? '' : 'pre-commit '}${hasPrePush ? '' : 'pre-push'}`.trim(),
1066
+ default: true,
1067
+ },
1068
+ ]);
1069
+ installHooksOnUpdate = installHooks;
1070
+ }
1071
+ }
1072
+ if (missingHooks && !installHooksOnUpdate && !options.yes) {
1073
+ console.log(chalk.gray('\nℹ Git hooks were not installed during update. Re-run "rulebook update" later or install them manually if you change your mind.'));
1074
+ }
1075
+ const agentsPath = path.join(cwd, 'AGENTS.md');
1076
+ const rulebookPath = path.join(cwd, '.rulebook');
1077
+ let existingMode;
1078
+ let existingLightMode;
1079
+ if (await fileExists(rulebookPath)) {
1080
+ try {
1081
+ const currentConfig = JSON.parse(await readFile(rulebookPath));
1082
+ if (currentConfig && (currentConfig.mode === 'minimal' || currentConfig.mode === 'full')) {
1083
+ existingMode = currentConfig.mode;
1084
+ }
1085
+ if (currentConfig && currentConfig.lightMode !== undefined) {
1086
+ existingLightMode = currentConfig.lightMode;
1087
+ }
1088
+ }
1089
+ catch {
1090
+ existingMode = undefined;
1091
+ existingLightMode = undefined;
1092
+ }
1093
+ }
1094
+ const minimalMode = options.minimal ?? existingMode === 'minimal';
1095
+ const lightMode = options.light !== undefined ? options.light : (existingLightMode ?? false);
1096
+ // Build config from detected project
1097
+ const config = {
1098
+ languages: detection.languages.map((l) => l.language),
1099
+ modules: minimalMode ? [] : detection.modules.filter((m) => m.detected).map((m) => m.module),
1100
+ frameworks: detection.frameworks.filter((f) => f.detected).map((f) => f.framework),
1101
+ ides: [], // Preserve existing IDE choices
1102
+ projectType: 'application',
1103
+ coverageThreshold: 95,
1104
+ strictDocs: true,
1105
+ generateWorkflows: false, // Don't regenerate workflows on update
1106
+ includeGitWorkflow: true,
1107
+ gitPushMode: 'manual',
1108
+ installGitHooks: installHooksOnUpdate,
1109
+ minimal: minimalMode,
1110
+ lightMode: lightMode,
1111
+ };
1112
+ if (minimalMode) {
1113
+ config.ides = [];
1114
+ config.generateWorkflows = true;
1115
+ }
1116
+ let minimalArtifacts = [];
1117
+ if (minimalMode) {
1118
+ minimalArtifacts = await scaffoldMinimalProject(cwd, {
1119
+ projectName: path.basename(cwd),
1120
+ description: 'Essential project scaffolding refreshed via Rulebook minimal mode.',
1121
+ license: 'MIT',
1122
+ });
1123
+ }
1124
+ // Migrate OpenSpec tasks to Rulebook format (if OpenSpec exists)
1125
+ const openspecChangesPath = path.join(cwd, 'openspec', 'changes');
1126
+ if (existsSync(openspecChangesPath)) {
1127
+ const migrationSpinner = ora('Migrating OpenSpec tasks to Rulebook format...').start();
1128
+ const { migrateOpenSpecToRulebook, migrateOpenSpecArchives, removeOpenSpecRulebookFile } = await import('../core/openspec-migrator.js');
1129
+ const rulebookDir = config.rulebookDir || 'rulebook';
1130
+ const migrationResult = await migrateOpenSpecToRulebook(cwd, rulebookDir);
1131
+ const archiveMigrationResult = await migrateOpenSpecArchives(cwd, rulebookDir);
1132
+ if (migrationResult.migrated > 0 || archiveMigrationResult.migrated > 0) {
1133
+ const totalMigrated = migrationResult.migrated + archiveMigrationResult.migrated;
1134
+ migrationSpinner.succeed(`Migrated ${totalMigrated} OpenSpec task(s) to Rulebook format`);
1135
+ if (migrationResult.migratedTasks.length > 0) {
1136
+ console.log(chalk.gray(` Active tasks: ${migrationResult.migratedTasks.join(', ')}`));
1137
+ }
1138
+ if (archiveMigrationResult.migratedTasks.length > 0) {
1139
+ console.log(chalk.gray(` Archived tasks: ${archiveMigrationResult.migratedTasks.join(', ')}`));
1140
+ }
1141
+ }
1142
+ else if (migrationResult.skipped > 0 || archiveMigrationResult.skipped > 0) {
1143
+ migrationSpinner.info('No OpenSpec tasks to migrate (already migrated or none found)');
1144
+ }
1145
+ else {
1146
+ migrationSpinner.info('No OpenSpec tasks found');
1147
+ }
1148
+ const allErrors = [...migrationResult.errors, ...archiveMigrationResult.errors];
1149
+ if (allErrors.length > 0) {
1150
+ console.log(chalk.yellow('\n⚠️ Migration warnings:'));
1151
+ for (const error of allErrors) {
1152
+ console.log(chalk.yellow(` - ${error}`));
1153
+ }
1154
+ }
1155
+ // Remove /rulebook/OPENSPEC.md if exists
1156
+ const removed = await removeOpenSpecRulebookFile(cwd, rulebookDir);
1157
+ if (removed) {
1158
+ console.log(chalk.gray(' Removed /rulebook/OPENSPEC.md'));
1159
+ }
1160
+ // Remove OpenSpec commands from .cursor/commands/
1161
+ const { removeOpenSpecCommands } = await import('../core/openspec-migrator.js');
1162
+ const removedCommands = await removeOpenSpecCommands(cwd);
1163
+ if (removedCommands > 0) {
1164
+ console.log(chalk.gray(` Removed ${removedCommands} OpenSpec command(s) from .cursor/commands/`));
1165
+ }
1166
+ // Generate Rulebook commands if Cursor is detected or if OpenSpec was used (likely Cursor project)
1167
+ const cursorRulesPath = path.join(cwd, '.cursorrules');
1168
+ const cursorCommandsDir = path.join(cwd, '.cursor', 'commands');
1169
+ const usesCursor = existsSync(cursorRulesPath) || existsSync(cursorCommandsDir);
1170
+ // Always generate commands if OpenSpec exists (OpenSpec was primarily used with Cursor)
1171
+ // or if Cursor is explicitly detected
1172
+ if (usesCursor || removedCommands > 0) {
1173
+ const { generateCursorCommands } = await import('../core/workflow-generator.js');
1174
+ const generatedCommands = await generateCursorCommands(cwd);
1175
+ if (generatedCommands.length > 0) {
1176
+ console.log(chalk.green(` Generated ${generatedCommands.length} Rulebook command(s) in .cursor/commands/`));
1177
+ }
1178
+ else if (usesCursor || removedCommands > 0) {
1179
+ // Commands already exist, just inform user
1180
+ console.log(chalk.gray(' Rulebook commands already exist in .cursor/commands/'));
1181
+ }
1182
+ }
1183
+ // Remove OpenSpec directory after successful migration
1184
+ const openspecPath = path.join(cwd, 'openspec');
1185
+ if (existsSync(openspecPath)) {
1186
+ const hasErrors = migrationResult.errors.length > 0 || archiveMigrationResult.errors.length > 0;
1187
+ // Remove directory if no errors occurred (migration was successful)
1188
+ // Even if no tasks were migrated (already migrated or empty), remove the directory
1189
+ if (!hasErrors) {
1190
+ try {
1191
+ const { rmSync } = await import('fs');
1192
+ rmSync(openspecPath, { recursive: true, force: true });
1193
+ console.log(chalk.gray(' Removed /openspec directory'));
1194
+ }
1195
+ catch (error) {
1196
+ console.log(chalk.yellow(` ⚠️ Could not remove /openspec directory: ${error.message}`));
1197
+ }
1198
+ }
1199
+ else {
1200
+ console.log(chalk.yellow(' ⚠️ /openspec directory kept due to migration errors (review and remove manually)'));
1201
+ }
1202
+ }
1203
+ }
1204
+ else {
1205
+ // Check if /openspec directory exists (even without /openspec/changes)
1206
+ const openspecPath = path.join(cwd, 'openspec');
1207
+ if (existsSync(openspecPath)) {
1208
+ // Remove OpenSpec commands and generate Rulebook commands
1209
+ const { removeOpenSpecCommands } = await import('../core/openspec-migrator.js');
1210
+ const removedCommands = await removeOpenSpecCommands(cwd);
1211
+ if (removedCommands > 0) {
1212
+ console.log(chalk.gray(` Removed ${removedCommands} OpenSpec command(s) from .cursor/commands/`));
1213
+ }
1214
+ // Generate Rulebook commands if Cursor is detected or if OpenSpec was used
1215
+ const cursorRulesPath = path.join(cwd, '.cursorrules');
1216
+ const cursorCommandsDir = path.join(cwd, '.cursor', 'commands');
1217
+ const usesCursor = existsSync(cursorRulesPath) || existsSync(cursorCommandsDir);
1218
+ // Always generate commands if OpenSpec exists (OpenSpec was primarily used with Cursor)
1219
+ // or if Cursor is explicitly detected
1220
+ if (usesCursor || removedCommands > 0) {
1221
+ const { generateCursorCommands } = await import('../core/workflow-generator.js');
1222
+ const generatedCommands = await generateCursorCommands(cwd);
1223
+ if (generatedCommands.length > 0) {
1224
+ console.log(chalk.green(` Generated ${generatedCommands.length} Rulebook command(s) in .cursor/commands/`));
1225
+ }
1226
+ else if (usesCursor || removedCommands > 0) {
1227
+ // Commands already exist, just inform user
1228
+ console.log(chalk.gray(' Rulebook commands already exist in .cursor/commands/'));
1229
+ }
1230
+ }
1231
+ }
1232
+ }
1233
+ // Always generate Rulebook commands if Cursor is detected (even without OpenSpec)
1234
+ // This ensures commands are available for all Cursor projects
1235
+ const cursorRulesPath = path.join(cwd, '.cursorrules');
1236
+ const cursorCommandsDir = path.join(cwd, '.cursor', 'commands');
1237
+ const usesCursor = existsSync(cursorRulesPath) || existsSync(cursorCommandsDir);
1238
+ if (usesCursor) {
1239
+ // Check if commands already exist to avoid duplicate generation
1240
+ const existingCommandsDir = path.join(cwd, '.cursor', 'commands');
1241
+ if (existsSync(existingCommandsDir)) {
1242
+ const { readdir } = await import('fs/promises');
1243
+ const existingFiles = await readdir(existingCommandsDir);
1244
+ const hasRulebookCommands = existingFiles.some((file) => file.startsWith('rulebook-task-'));
1245
+ if (!hasRulebookCommands) {
1246
+ const { generateCursorCommands } = await import('../core/workflow-generator.js');
1247
+ const generatedCommands = await generateCursorCommands(cwd);
1248
+ if (generatedCommands.length > 0) {
1249
+ console.log(chalk.green(` Generated ${generatedCommands.length} Rulebook command(s) in .cursor/commands/`));
1250
+ }
1251
+ }
1252
+ }
1253
+ else {
1254
+ // Directory doesn't exist, create it and generate commands
1255
+ const { generateCursorCommands } = await import('../core/workflow-generator.js');
1256
+ const generatedCommands = await generateCursorCommands(cwd);
1257
+ if (generatedCommands.length > 0) {
1258
+ console.log(chalk.green(` Generated ${generatedCommands.length} Rulebook command(s) in .cursor/commands/`));
1259
+ }
1260
+ }
1261
+ }
1262
+ // Save project configuration to .rulebook
1263
+ const { createConfigManager } = await import('../core/config-manager.js');
1264
+ const configManager = createConfigManager(cwd);
1265
+ await configManager.updateConfig({
1266
+ languages: config.languages,
1267
+ frameworks: config.frameworks,
1268
+ modules: config.modules,
1269
+ services: config.services,
1270
+ modular: config.modular ?? true,
1271
+ rulebookDir: config.rulebookDir || 'rulebook',
1272
+ });
1273
+ // Merge with existing AGENTS.md (with migration support)
1274
+ const mergeSpinner = ora('Updating AGENTS.md with latest templates...').start();
1275
+ config.modular = config.modular ?? true; // Enable modular by default
1276
+ const mergedContent = await mergeFullAgents(detection.existingAgents, config, cwd);
1277
+ await writeFile(agentsPath, mergedContent);
1278
+ mergeSpinner.succeed('AGENTS.md updated');
1279
+ if (installHooksOnUpdate) {
1280
+ const hookLanguages = detection.languages.length > 0
1281
+ ? detection.languages
1282
+ : config.languages.map((language) => ({
1283
+ language: language,
1284
+ confidence: 1,
1285
+ indicators: [],
1286
+ }));
1287
+ const hookSpinner = ora('Installing Git hooks (pre-commit & pre-push)...').start();
1288
+ try {
1289
+ await installGitHooks({ languages: hookLanguages, cwd });
1290
+ hookSpinner.succeed('Git hooks installed successfully');
1291
+ hooksInstalledOnUpdate = true;
1292
+ }
1293
+ catch (error) {
1294
+ hookSpinner.fail('Failed to install Git hooks');
1295
+ console.error(chalk.red(' ➤'), error instanceof Error ? error.message : error);
1296
+ console.log(chalk.yellow(' ⚠ Skipping automatic hook installation. You can rerun "rulebook update" later to retry or install manually.'));
1297
+ }
1298
+ }
1299
+ const gitHooksActiveAfterUpdate = hooksInstalledOnUpdate || (hasPreCommit && hasPrePush);
1300
+ config.installGitHooks = gitHooksActiveAfterUpdate;
1301
+ // Update .rulebook config
1302
+ const configSpinner = ora('Updating .rulebook configuration...').start();
1303
+ const rulebookFeatures = {
1304
+ watcher: false,
1305
+ agent: false,
1306
+ logging: true,
1307
+ telemetry: false,
1308
+ notifications: false,
1309
+ dryRun: false,
1310
+ gitHooks: gitHooksActiveAfterUpdate,
1311
+ repl: false,
1312
+ templates: true,
1313
+ context: minimalMode ? false : true,
1314
+ health: true,
1315
+ plugins: false,
1316
+ parallel: minimalMode ? false : true,
1317
+ smartContinue: minimalMode ? false : true,
1318
+ };
1319
+ const rulebookConfig = {
1320
+ version: getRulebookVersion(),
1321
+ installedAt: detection.existingAgents.content?.match(/Generated at: (.+)/)?.[1] ||
1322
+ new Date().toISOString(),
1323
+ updatedAt: new Date().toISOString(),
1324
+ projectId: path.basename(cwd),
1325
+ mode: minimalMode ? 'minimal' : 'full',
1326
+ features: rulebookFeatures,
1327
+ coverageThreshold: 95,
1328
+ language: 'en',
1329
+ outputLanguage: 'en',
1330
+ cliTools: [],
1331
+ maxParallelTasks: 5,
1332
+ timeouts: {
1333
+ taskExecution: 3600000,
1334
+ cliResponse: 180000,
1335
+ testRun: 600000,
1336
+ },
1337
+ };
1338
+ await writeFile(rulebookPath, JSON.stringify(rulebookConfig, null, 2));
1339
+ configSpinner.succeed('.rulebook configuration updated');
1340
+ // Success message
1341
+ console.log(chalk.bold.green('\n✅ Update complete!\n'));
1342
+ console.log(chalk.white('Updated components:'));
1343
+ console.log(chalk.green(' ✓ AGENTS.md - Merged with latest templates'));
1344
+ console.log(chalk.green(` ✓ .rulebook - Updated to v${getRulebookVersion()}`));
1345
+ console.log(chalk.white('\nWhat was updated:'));
1346
+ console.log(chalk.gray(` - ${detection.languages.length} language templates`));
1347
+ console.log(chalk.gray(` - ${detection.modules.filter((m) => m.detected).length} MCP modules`));
1348
+ console.log(chalk.gray(' - Git workflow rules'));
1349
+ console.log(chalk.gray(' - Rulebook task management'));
1350
+ console.log(chalk.gray(' - Pre-commit command standardization'));
1351
+ console.log(chalk.yellow('\n⚠ Review the updated AGENTS.md to ensure your custom rules are preserved'));
1352
+ console.log(chalk.white('\nNext steps:'));
1353
+ console.log(chalk.gray(' 1. Review AGENTS.md changes'));
1354
+ console.log(chalk.gray(' 2. Test that your project still builds'));
1355
+ console.log(chalk.gray(' 3. Run quality checks (lint, test, build)'));
1356
+ console.log(chalk.gray(' 4. Commit the updated files\n'));
1357
+ if (minimalMode && minimalArtifacts.length > 0) {
1358
+ console.log(chalk.green('Essentials ensured:'));
1359
+ for (const artifact of minimalArtifacts) {
1360
+ console.log(chalk.gray(` - ${path.relative(cwd, artifact)}`));
1361
+ }
1362
+ console.log('');
1363
+ }
1364
+ }
1365
+ catch (error) {
1366
+ console.error(chalk.red('\n❌ Update failed:'), error);
1367
+ process.exit(1);
1368
+ }
1369
+ }
1370
+ //# sourceMappingURL=commands.js.map