@hivehub/rulebook 4.0.0 → 4.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 (348) hide show
  1. package/.claude/commands/continue.md +33 -33
  2. package/.claude/commands/ralph-config.md +112 -112
  3. package/.claude/commands/ralph-history.md +110 -110
  4. package/.claude/commands/ralph-init.md +72 -72
  5. package/.claude/commands/ralph-pause-resume.md +105 -105
  6. package/.claude/commands/ralph-run.md +101 -101
  7. package/.claude/commands/ralph-status.md +76 -76
  8. package/.claude/commands/rulebook-memory-save.md +48 -48
  9. package/.claude/commands/rulebook-memory-search.md +47 -47
  10. package/.claude/commands/rulebook-task-apply.md +67 -67
  11. package/.claude/commands/rulebook-task-archive.md +70 -70
  12. package/.claude/commands/rulebook-task-create.md +93 -93
  13. package/.claude/commands/rulebook-task-list.md +42 -42
  14. package/.claude/commands/rulebook-task-show.md +52 -52
  15. package/.claude/commands/rulebook-task-validate.md +53 -53
  16. package/.claude-plugin/marketplace.json +28 -28
  17. package/.claude-plugin/plugin.json +8 -8
  18. package/README.md +34 -0
  19. package/dist/cli/commands.d.ts +18 -6
  20. package/dist/cli/commands.d.ts.map +1 -1
  21. package/dist/cli/commands.js +717 -406
  22. package/dist/cli/commands.js.map +1 -1
  23. package/dist/core/claude-mcp.d.ts +4 -2
  24. package/dist/core/claude-mcp.d.ts.map +1 -1
  25. package/dist/core/claude-mcp.js +14 -9
  26. package/dist/core/claude-mcp.js.map +1 -1
  27. package/dist/core/generator.d.ts.map +1 -1
  28. package/dist/core/generator.js +13 -0
  29. package/dist/core/generator.js.map +1 -1
  30. package/dist/core/indexer/background-indexer.d.ts +27 -0
  31. package/dist/core/indexer/background-indexer.d.ts.map +1 -0
  32. package/dist/core/indexer/background-indexer.js +156 -0
  33. package/dist/core/indexer/background-indexer.js.map +1 -0
  34. package/dist/core/indexer/file-parser.d.ts +28 -0
  35. package/dist/core/indexer/file-parser.d.ts.map +1 -0
  36. package/dist/core/indexer/file-parser.js +171 -0
  37. package/dist/core/indexer/file-parser.js.map +1 -0
  38. package/dist/core/indexer/indexer-types.d.ts +35 -0
  39. package/dist/core/indexer/indexer-types.d.ts.map +1 -0
  40. package/dist/core/indexer/indexer-types.js +8 -0
  41. package/dist/core/indexer/indexer-types.js.map +1 -0
  42. package/dist/core/workspace/legacy-migrator.d.ts +29 -0
  43. package/dist/core/workspace/legacy-migrator.d.ts.map +1 -0
  44. package/dist/core/workspace/legacy-migrator.js +142 -0
  45. package/dist/core/workspace/legacy-migrator.js.map +1 -0
  46. package/dist/core/workspace/project-worker.d.ts +49 -0
  47. package/dist/core/workspace/project-worker.d.ts.map +1 -0
  48. package/dist/core/workspace/project-worker.js +108 -0
  49. package/dist/core/workspace/project-worker.js.map +1 -0
  50. package/dist/core/workspace/workspace-manager.d.ts +90 -0
  51. package/dist/core/workspace/workspace-manager.d.ts.map +1 -0
  52. package/dist/core/workspace/workspace-manager.js +337 -0
  53. package/dist/core/workspace/workspace-manager.js.map +1 -0
  54. package/dist/core/workspace/workspace-types.d.ts +37 -0
  55. package/dist/core/workspace/workspace-types.d.ts.map +1 -0
  56. package/dist/core/workspace/workspace-types.js +8 -0
  57. package/dist/core/workspace/workspace-types.js.map +1 -0
  58. package/dist/index.js +43 -7
  59. package/dist/index.js.map +1 -1
  60. package/dist/mcp/rulebook-server.d.ts.map +1 -1
  61. package/dist/mcp/rulebook-server.js +450 -81
  62. package/dist/mcp/rulebook-server.js.map +1 -1
  63. package/dist/memory/memory-manager.d.ts +4 -1
  64. package/dist/memory/memory-manager.d.ts.map +1 -1
  65. package/dist/memory/memory-manager.js +33 -4
  66. package/dist/memory/memory-manager.js.map +1 -1
  67. package/dist/memory/memory-search.d.ts +2 -2
  68. package/dist/memory/memory-search.d.ts.map +1 -1
  69. package/dist/memory/memory-search.js +19 -0
  70. package/dist/memory/memory-search.js.map +1 -1
  71. package/dist/memory/memory-store.d.ts +13 -0
  72. package/dist/memory/memory-store.d.ts.map +1 -1
  73. package/dist/memory/memory-store.js +88 -1
  74. package/dist/memory/memory-store.js.map +1 -1
  75. package/dist/memory/memory-types.d.ts +15 -0
  76. package/dist/memory/memory-types.d.ts.map +1 -1
  77. package/dist/types.d.ts +1 -0
  78. package/dist/types.d.ts.map +1 -1
  79. package/package.json +22 -21
  80. package/templates/agents/implementer.md +35 -35
  81. package/templates/agents/researcher.md +34 -34
  82. package/templates/agents/team-lead.md +34 -34
  83. package/templates/agents/tester.md +42 -42
  84. package/templates/ci/rulebook-review.yml +26 -26
  85. package/templates/cli/AIDER.md +49 -49
  86. package/templates/cli/AMAZON_Q.md +25 -25
  87. package/templates/cli/AUGGIE.md +32 -32
  88. package/templates/cli/CLAUDE.md +117 -117
  89. package/templates/cli/CLINE.md +99 -99
  90. package/templates/cli/CODEBUDDY.md +20 -20
  91. package/templates/cli/CODEIUM.md +20 -20
  92. package/templates/cli/CODEX.md +21 -21
  93. package/templates/cli/CONTINUE.md +34 -34
  94. package/templates/cli/CURSOR_CLI.md +62 -62
  95. package/templates/cli/FACTORY.md +18 -18
  96. package/templates/cli/GEMINI.md +35 -35
  97. package/templates/cli/KILOCODE.md +18 -18
  98. package/templates/cli/OPENCODE.md +18 -18
  99. package/templates/cli/_GENERIC_TEMPLATE.md +29 -29
  100. package/templates/commands/rulebook-memory-save.md +48 -48
  101. package/templates/commands/rulebook-memory-search.md +47 -47
  102. package/templates/commands/rulebook-task-apply.md +67 -67
  103. package/templates/commands/rulebook-task-archive.md +94 -70
  104. package/templates/commands/rulebook-task-create.md +93 -93
  105. package/templates/commands/rulebook-task-list.md +42 -42
  106. package/templates/commands/rulebook-task-show.md +52 -52
  107. package/templates/commands/rulebook-task-validate.md +53 -53
  108. package/templates/core/AGENTS_LEAN.md +25 -25
  109. package/templates/core/AGENTS_OVERRIDE.md +16 -16
  110. package/templates/core/AGENT_AUTOMATION.md +288 -288
  111. package/templates/core/DAG.md +304 -304
  112. package/templates/core/DOCUMENTATION_RULES.md +36 -36
  113. package/templates/core/MULTI_AGENT.md +74 -74
  114. package/templates/core/PLANS.md +28 -28
  115. package/templates/core/QUALITY_ENFORCEMENT.md +68 -68
  116. package/templates/core/RALPH.md +471 -471
  117. package/templates/core/RULEBOOK.md +1935 -1893
  118. package/templates/core/WORKSPACE.md +69 -0
  119. package/templates/frameworks/ANGULAR.md +36 -36
  120. package/templates/frameworks/DJANGO.md +83 -83
  121. package/templates/frameworks/ELECTRON.md +147 -147
  122. package/templates/frameworks/FLASK.md +38 -38
  123. package/templates/frameworks/FLUTTER.md +55 -55
  124. package/templates/frameworks/JQUERY.md +32 -32
  125. package/templates/frameworks/LARAVEL.md +38 -38
  126. package/templates/frameworks/NESTJS.md +43 -43
  127. package/templates/frameworks/NEXTJS.md +127 -127
  128. package/templates/frameworks/NUXT.md +40 -40
  129. package/templates/frameworks/RAILS.md +66 -66
  130. package/templates/frameworks/REACT.md +38 -38
  131. package/templates/frameworks/REACT_NATIVE.md +47 -47
  132. package/templates/frameworks/SPRING.md +39 -39
  133. package/templates/frameworks/SYMFONY.md +36 -36
  134. package/templates/frameworks/VUE.md +36 -36
  135. package/templates/frameworks/ZEND.md +35 -35
  136. package/templates/git/CI_CD_PATTERNS.md +661 -661
  137. package/templates/git/GITHUB_ACTIONS.md +728 -728
  138. package/templates/git/GITLAB_CI.md +730 -730
  139. package/templates/git/GIT_WORKFLOW.md +1157 -1157
  140. package/templates/git/SECRETS_MANAGEMENT.md +585 -585
  141. package/templates/hooks/COMMIT_MSG.md +530 -530
  142. package/templates/hooks/POST_CHECKOUT.md +546 -546
  143. package/templates/hooks/PREPARE_COMMIT_MSG.md +619 -619
  144. package/templates/hooks/PRE_COMMIT.md +414 -414
  145. package/templates/hooks/PRE_PUSH.md +601 -601
  146. package/templates/ides/CONTINUE_RULES.md +16 -16
  147. package/templates/ides/COPILOT.md +37 -37
  148. package/templates/ides/COPILOT_INSTRUCTIONS.md +23 -23
  149. package/templates/ides/CURSOR.md +43 -43
  150. package/templates/ides/GEMINI_RULES.md +17 -17
  151. package/templates/ides/JETBRAINS_AI.md +35 -35
  152. package/templates/ides/REPLIT.md +36 -36
  153. package/templates/ides/TABNINE.md +29 -29
  154. package/templates/ides/VSCODE.md +40 -40
  155. package/templates/ides/WINDSURF.md +36 -36
  156. package/templates/ides/WINDSURF_RULES.md +14 -14
  157. package/templates/ides/ZED.md +32 -32
  158. package/templates/ides/cursor-mdc/go.mdc +24 -24
  159. package/templates/ides/cursor-mdc/python.mdc +24 -24
  160. package/templates/ides/cursor-mdc/quality.mdc +25 -25
  161. package/templates/ides/cursor-mdc/ralph.mdc +39 -39
  162. package/templates/ides/cursor-mdc/rulebook.mdc +38 -38
  163. package/templates/ides/cursor-mdc/rust.mdc +24 -24
  164. package/templates/ides/cursor-mdc/typescript.mdc +25 -25
  165. package/templates/languages/C.md +333 -333
  166. package/templates/languages/CPP.md +743 -743
  167. package/templates/languages/CSHARP.md +417 -417
  168. package/templates/languages/ELIXIR.md +454 -454
  169. package/templates/languages/ERLANG.md +361 -361
  170. package/templates/languages/GO.md +645 -645
  171. package/templates/languages/HASKELL.md +177 -177
  172. package/templates/languages/JAVA.md +607 -607
  173. package/templates/languages/JAVASCRIPT.md +631 -631
  174. package/templates/languages/JULIA.md +97 -97
  175. package/templates/languages/KOTLIN.md +511 -511
  176. package/templates/languages/LISP.md +100 -100
  177. package/templates/languages/LUA.md +74 -74
  178. package/templates/languages/OBJECTIVEC.md +90 -90
  179. package/templates/languages/PHP.md +416 -416
  180. package/templates/languages/PYTHON.md +682 -682
  181. package/templates/languages/RUBY.md +421 -421
  182. package/templates/languages/RUST.md +477 -477
  183. package/templates/languages/SAS.md +73 -73
  184. package/templates/languages/SCALA.md +348 -348
  185. package/templates/languages/SOLIDITY.md +580 -580
  186. package/templates/languages/SQL.md +137 -137
  187. package/templates/languages/SWIFT.md +466 -466
  188. package/templates/languages/TYPESCRIPT.md +591 -591
  189. package/templates/languages/ZIG.md +265 -265
  190. package/templates/modules/ATLASSIAN.md +255 -255
  191. package/templates/modules/CONTEXT7.md +54 -54
  192. package/templates/modules/FIGMA.md +267 -267
  193. package/templates/modules/GITHUB_MCP.md +64 -64
  194. package/templates/modules/GRAFANA.md +328 -328
  195. package/templates/modules/MEMORY.md +126 -126
  196. package/templates/modules/NOTION.md +247 -247
  197. package/templates/modules/PLAYWRIGHT.md +90 -90
  198. package/templates/modules/RULEBOOK_MCP.md +156 -156
  199. package/templates/modules/SERENA.md +337 -337
  200. package/templates/modules/SUPABASE.md +223 -223
  201. package/templates/modules/SYNAP.md +69 -69
  202. package/templates/modules/VECTORIZER.md +63 -63
  203. package/templates/modules/sequential-thinking.md +42 -42
  204. package/templates/ralph/ralph-history.bat +4 -4
  205. package/templates/ralph/ralph-history.sh +5 -5
  206. package/templates/ralph/ralph-init.bat +5 -5
  207. package/templates/ralph/ralph-init.sh +5 -5
  208. package/templates/ralph/ralph-pause.bat +5 -5
  209. package/templates/ralph/ralph-pause.sh +5 -5
  210. package/templates/ralph/ralph-run.bat +5 -5
  211. package/templates/ralph/ralph-run.sh +5 -5
  212. package/templates/ralph/ralph-status.bat +4 -4
  213. package/templates/ralph/ralph-status.sh +5 -5
  214. package/templates/services/AZURE_BLOB.md +184 -184
  215. package/templates/services/CASSANDRA.md +239 -239
  216. package/templates/services/DATADOG.md +26 -26
  217. package/templates/services/DOCKER.md +124 -124
  218. package/templates/services/DOCKER_COMPOSE.md +168 -168
  219. package/templates/services/DYNAMODB.md +308 -308
  220. package/templates/services/ELASTICSEARCH.md +347 -347
  221. package/templates/services/GCS.md +178 -178
  222. package/templates/services/HELM.md +194 -194
  223. package/templates/services/INFLUXDB.md +265 -265
  224. package/templates/services/KAFKA.md +341 -341
  225. package/templates/services/KUBERNETES.md +208 -208
  226. package/templates/services/MARIADB.md +183 -183
  227. package/templates/services/MEMCACHED.md +242 -242
  228. package/templates/services/MINIO.md +201 -201
  229. package/templates/services/MONGODB.md +268 -268
  230. package/templates/services/MYSQL.md +358 -358
  231. package/templates/services/NEO4J.md +247 -247
  232. package/templates/services/OPENTELEMETRY.md +25 -25
  233. package/templates/services/ORACLE.md +290 -290
  234. package/templates/services/PINO.md +24 -24
  235. package/templates/services/POSTGRESQL.md +326 -326
  236. package/templates/services/PROMETHEUS.md +33 -33
  237. package/templates/services/RABBITMQ.md +286 -286
  238. package/templates/services/REDIS.md +292 -292
  239. package/templates/services/S3.md +298 -298
  240. package/templates/services/SENTRY.md +23 -23
  241. package/templates/services/SQLITE.md +294 -294
  242. package/templates/services/SQLSERVER.md +294 -294
  243. package/templates/services/WINSTON.md +30 -30
  244. package/templates/skills/cli/aider/SKILL.md +59 -59
  245. package/templates/skills/cli/amazon-q/SKILL.md +35 -35
  246. package/templates/skills/cli/auggie/SKILL.md +42 -42
  247. package/templates/skills/cli/claude/SKILL.md +42 -42
  248. package/templates/skills/cli/cline/SKILL.md +42 -42
  249. package/templates/skills/cli/codebuddy/SKILL.md +30 -30
  250. package/templates/skills/cli/codeium/SKILL.md +30 -30
  251. package/templates/skills/cli/codex/SKILL.md +31 -31
  252. package/templates/skills/cli/continue/SKILL.md +44 -44
  253. package/templates/skills/cli/cursor-cli/SKILL.md +38 -38
  254. package/templates/skills/cli/factory/SKILL.md +28 -28
  255. package/templates/skills/cli/gemini/SKILL.md +45 -45
  256. package/templates/skills/cli/kilocode/SKILL.md +28 -28
  257. package/templates/skills/cli/opencode/SKILL.md +28 -28
  258. package/templates/skills/core/agent-automation/SKILL.md +194 -194
  259. package/templates/skills/core/dag/SKILL.md +314 -314
  260. package/templates/skills/core/documentation-rules/SKILL.md +46 -46
  261. package/templates/skills/core/quality-enforcement/SKILL.md +78 -78
  262. package/templates/skills/core/rulebook/SKILL.md +176 -176
  263. package/templates/skills/frameworks/angular/SKILL.md +46 -46
  264. package/templates/skills/frameworks/django/SKILL.md +93 -93
  265. package/templates/skills/frameworks/electron/SKILL.md +157 -157
  266. package/templates/skills/frameworks/flask/SKILL.md +48 -48
  267. package/templates/skills/frameworks/flutter/SKILL.md +65 -65
  268. package/templates/skills/frameworks/jquery/SKILL.md +42 -42
  269. package/templates/skills/frameworks/laravel/SKILL.md +48 -48
  270. package/templates/skills/frameworks/nestjs/SKILL.md +53 -53
  271. package/templates/skills/frameworks/nextjs/SKILL.md +137 -137
  272. package/templates/skills/frameworks/nuxt/SKILL.md +50 -50
  273. package/templates/skills/frameworks/rails/SKILL.md +76 -76
  274. package/templates/skills/frameworks/react/SKILL.md +48 -48
  275. package/templates/skills/frameworks/react-native/SKILL.md +57 -57
  276. package/templates/skills/frameworks/spring/SKILL.md +49 -49
  277. package/templates/skills/frameworks/symfony/SKILL.md +46 -46
  278. package/templates/skills/frameworks/vue/SKILL.md +46 -46
  279. package/templates/skills/frameworks/zend/SKILL.md +45 -45
  280. package/templates/skills/ides/copilot/SKILL.md +47 -47
  281. package/templates/skills/ides/cursor/SKILL.md +53 -53
  282. package/templates/skills/ides/jetbrains-ai/SKILL.md +45 -45
  283. package/templates/skills/ides/replit/SKILL.md +46 -46
  284. package/templates/skills/ides/tabnine/SKILL.md +39 -39
  285. package/templates/skills/ides/vscode/SKILL.md +50 -50
  286. package/templates/skills/ides/windsurf/SKILL.md +46 -46
  287. package/templates/skills/ides/zed/SKILL.md +42 -42
  288. package/templates/skills/languages/c/SKILL.md +343 -343
  289. package/templates/skills/languages/cpp/SKILL.md +753 -753
  290. package/templates/skills/languages/csharp/SKILL.md +427 -427
  291. package/templates/skills/languages/elixir/SKILL.md +464 -464
  292. package/templates/skills/languages/erlang/SKILL.md +371 -371
  293. package/templates/skills/languages/go/SKILL.md +655 -655
  294. package/templates/skills/languages/haskell/SKILL.md +187 -187
  295. package/templates/skills/languages/java/SKILL.md +617 -617
  296. package/templates/skills/languages/javascript/SKILL.md +641 -641
  297. package/templates/skills/languages/julia/SKILL.md +107 -107
  298. package/templates/skills/languages/kotlin/SKILL.md +521 -521
  299. package/templates/skills/languages/lisp/SKILL.md +110 -110
  300. package/templates/skills/languages/lua/SKILL.md +84 -84
  301. package/templates/skills/languages/objectivec/SKILL.md +100 -100
  302. package/templates/skills/languages/php/SKILL.md +426 -426
  303. package/templates/skills/languages/python/SKILL.md +692 -692
  304. package/templates/skills/languages/ruby/SKILL.md +431 -431
  305. package/templates/skills/languages/rust/SKILL.md +487 -487
  306. package/templates/skills/languages/sas/SKILL.md +83 -83
  307. package/templates/skills/languages/scala/SKILL.md +358 -358
  308. package/templates/skills/languages/solidity/SKILL.md +590 -590
  309. package/templates/skills/languages/sql/SKILL.md +147 -147
  310. package/templates/skills/languages/swift/SKILL.md +476 -476
  311. package/templates/skills/languages/typescript/SKILL.md +302 -302
  312. package/templates/skills/languages/zig/SKILL.md +275 -275
  313. package/templates/skills/modules/atlassian/SKILL.md +265 -265
  314. package/templates/skills/modules/context7/SKILL.md +64 -64
  315. package/templates/skills/modules/figma/SKILL.md +277 -277
  316. package/templates/skills/modules/github-mcp/SKILL.md +74 -74
  317. package/templates/skills/modules/grafana/SKILL.md +338 -338
  318. package/templates/skills/modules/memory/SKILL.md +73 -73
  319. package/templates/skills/modules/notion/SKILL.md +257 -257
  320. package/templates/skills/modules/playwright/SKILL.md +100 -100
  321. package/templates/skills/modules/rulebook-mcp/SKILL.md +166 -166
  322. package/templates/skills/modules/serena/SKILL.md +347 -347
  323. package/templates/skills/modules/supabase/SKILL.md +233 -233
  324. package/templates/skills/modules/synap/SKILL.md +79 -79
  325. package/templates/skills/modules/vectorizer/SKILL.md +73 -73
  326. package/templates/skills/services/azure-blob/SKILL.md +194 -194
  327. package/templates/skills/services/cassandra/SKILL.md +249 -249
  328. package/templates/skills/services/dynamodb/SKILL.md +318 -318
  329. package/templates/skills/services/elasticsearch/SKILL.md +357 -357
  330. package/templates/skills/services/gcs/SKILL.md +188 -188
  331. package/templates/skills/services/influxdb/SKILL.md +275 -275
  332. package/templates/skills/services/kafka/SKILL.md +351 -351
  333. package/templates/skills/services/mariadb/SKILL.md +193 -193
  334. package/templates/skills/services/memcached/SKILL.md +252 -252
  335. package/templates/skills/services/minio/SKILL.md +211 -211
  336. package/templates/skills/services/mongodb/SKILL.md +278 -278
  337. package/templates/skills/services/mysql/SKILL.md +368 -368
  338. package/templates/skills/services/neo4j/SKILL.md +257 -257
  339. package/templates/skills/services/oracle/SKILL.md +300 -300
  340. package/templates/skills/services/postgresql/SKILL.md +336 -336
  341. package/templates/skills/services/rabbitmq/SKILL.md +296 -296
  342. package/templates/skills/services/redis/SKILL.md +302 -302
  343. package/templates/skills/services/s3/SKILL.md +308 -308
  344. package/templates/skills/services/sqlite/SKILL.md +304 -304
  345. package/templates/skills/services/sqlserver/SKILL.md +304 -304
  346. package/templates/skills/workflows/ralph/SKILL.md +309 -309
  347. package/templates/skills/workflows/ralph/install.sh +87 -87
  348. package/templates/skills/workflows/ralph/manifest.json +158 -158
@@ -10,9 +10,11 @@ import { parseRulesIgnore } from '../utils/rulesignore.js';
10
10
  import { installGitHooks } from '../utils/git-hooks.js';
11
11
  import { scaffoldMinimalProject } from '../core/minimal-scaffolder.js';
12
12
  import path from 'path';
13
- import { readFileSync } from 'fs';
13
+ import { readFileSync, writeFileSync } from 'fs';
14
14
  import { fileURLToPath } from 'url';
15
15
  import { SkillsManager, getDefaultTemplatesPath } from '../core/skills-manager.js';
16
+ import { WorkspaceManager } from '../core/workspace/workspace-manager.js';
17
+ import { migrateLegacyMcpConfigs } from '../core/workspace/legacy-migrator.js';
16
18
  const FRAMEWORK_LABELS = {
17
19
  nestjs: 'NestJS',
18
20
  spring: 'Spring Boot',
@@ -900,19 +902,68 @@ export async function configCommand(options) {
900
902
  process.exit(1);
901
903
  }
902
904
  }
905
+ /**
906
+ * Resolve a TaskManager for a specific project.
907
+ *
908
+ * Resolution order:
909
+ * 1. Explicit --project flag → find workspace config from cwd, route to named project
910
+ * 2. Auto-detect → walk up from cwd to find workspace, match cwd to a project
911
+ * 3. Fallback → single-project from cwd (no workspace)
912
+ */
913
+ async function resolveTaskManager(cwd, options) {
914
+ const { createTaskManager } = await import('../core/task-manager.js');
915
+ const { createConfigManager } = await import('../core/config-manager.js');
916
+ const { isAbsolute, resolve } = await import('path');
917
+ // Helper: build TaskManager from a resolved project root
918
+ const buildFromProjectRoot = async (projectRoot, label) => {
919
+ const configManager = createConfigManager(projectRoot);
920
+ const config = await configManager.loadConfig();
921
+ const rulebookDir = config.rulebookDir || '.rulebook';
922
+ return { taskManager: createTaskManager(projectRoot, rulebookDir), projectLabel: label };
923
+ };
924
+ // 1. Explicit --project flag
925
+ if (options?.project) {
926
+ const ws = WorkspaceManager.findWorkspaceFromCwd(cwd);
927
+ if (!ws) {
928
+ console.error(chalk.red('No workspace found. Run `rulebook workspace init` first.'));
929
+ process.exit(1);
930
+ }
931
+ const project = ws.config.projects.find((p) => p.name === options.project);
932
+ if (!project) {
933
+ console.error(chalk.red(`Project "${options.project}" not found in workspace.`));
934
+ console.error(chalk.gray(`Available: ${ws.config.projects.map((p) => p.name).join(', ')}`));
935
+ process.exit(1);
936
+ }
937
+ const projectRoot = isAbsolute(project.path)
938
+ ? project.path
939
+ : resolve(ws.root, project.path);
940
+ return buildFromProjectRoot(projectRoot, project.name);
941
+ }
942
+ // 2. Auto-detect: walk up from cwd to find workspace and match project
943
+ const resolved = WorkspaceManager.resolveProjectFromCwd(cwd);
944
+ if (resolved) {
945
+ const project = resolved.config.projects.find((p) => p.name === resolved.projectName);
946
+ if (project) {
947
+ const projectRoot = isAbsolute(project.path)
948
+ ? project.path
949
+ : resolve(resolved.root, project.path);
950
+ return buildFromProjectRoot(projectRoot, project.name);
951
+ }
952
+ }
953
+ // 3. Fallback: single-project from cwd
954
+ const configManager = createConfigManager(cwd);
955
+ const config = await configManager.loadConfig();
956
+ const rulebookDir = config.rulebookDir || '.rulebook';
957
+ return { taskManager: createTaskManager(cwd, rulebookDir) };
958
+ }
903
959
  // Task management commands using Rulebook task system
904
- export async function taskCreateCommand(taskId) {
960
+ export async function taskCreateCommand(taskId, wsOptions) {
905
961
  try {
906
962
  const cwd = process.cwd();
907
- const { createTaskManager } = await import('../core/task-manager.js');
908
- const { createConfigManager } = await import('../core/config-manager.js');
909
- const configManager = createConfigManager(cwd);
910
- const config = await configManager.loadConfig();
911
- const rulebookDir = config.rulebookDir || '.rulebook';
912
- const taskManager = createTaskManager(cwd, rulebookDir);
963
+ const { taskManager, projectLabel } = await resolveTaskManager(cwd, wsOptions);
913
964
  await taskManager.createTask(taskId);
914
- console.log(chalk.green(`✅ Task ${taskId} created successfully`));
915
- console.log(chalk.gray(`Location: ${rulebookDir}/tasks/${taskId}/`));
965
+ const prefix = projectLabel ? `[${projectLabel}] ` : '';
966
+ console.log(chalk.green(`✅ ${prefix}Task ${taskId} created successfully`));
916
967
  console.log(chalk.yellow('\n⚠️ Remember to:'));
917
968
  console.log(chalk.gray(' 1. Fill in proposal.md (minimum 20 characters in "Why" section)'));
918
969
  console.log(chalk.gray(' 3. Add tasks to tasks.md'));
@@ -924,21 +975,66 @@ export async function taskCreateCommand(taskId) {
924
975
  process.exit(1);
925
976
  }
926
977
  }
927
- export async function taskListCommand(includeArchived = false) {
978
+ export async function taskListCommand(includeArchived = false, wsOptions) {
928
979
  try {
929
980
  const cwd = process.cwd();
930
- const { createTaskManager } = await import('../core/task-manager.js');
931
- const { createConfigManager } = await import('../core/config-manager.js');
932
- const configManager = createConfigManager(cwd);
933
- const config = await configManager.loadConfig();
934
- const rulebookDir = config.rulebookDir || '.rulebook';
935
- const taskManager = createTaskManager(cwd, rulebookDir);
981
+ // --all-projects: list tasks from every workspace project
982
+ if (wsOptions?.allProjects) {
983
+ const ws = WorkspaceManager.findWorkspaceFromCwd(cwd);
984
+ if (!ws) {
985
+ console.error(chalk.red('No workspace found. Run `rulebook workspace init` first.'));
986
+ process.exit(1);
987
+ return;
988
+ }
989
+ console.log(chalk.bold.blue(`\n📋 Workspace Tasks (${ws.config.name})\n`));
990
+ const { createTaskManager } = await import('../core/task-manager.js');
991
+ const { createConfigManager } = await import('../core/config-manager.js');
992
+ const { isAbsolute, resolve } = await import('path');
993
+ let totalTasks = 0;
994
+ for (const project of ws.config.projects) {
995
+ const projectRoot = isAbsolute(project.path) ? project.path : resolve(ws.root, project.path);
996
+ try {
997
+ const configManager = createConfigManager(projectRoot);
998
+ const config = await configManager.loadConfig();
999
+ const rulebookDir = config.rulebookDir || '.rulebook';
1000
+ const taskManager = createTaskManager(projectRoot, rulebookDir);
1001
+ const tasks = await taskManager.listTasks(includeArchived);
1002
+ if (tasks.length > 0) {
1003
+ console.log(chalk.bold.cyan(` [${project.name}]`));
1004
+ for (const task of tasks) {
1005
+ const statusColor = task.status === 'completed'
1006
+ ? chalk.green
1007
+ : task.status === 'in-progress'
1008
+ ? chalk.yellow
1009
+ : task.status === 'blocked'
1010
+ ? chalk.red
1011
+ : chalk.gray;
1012
+ console.log(` ${statusColor(task.status.padEnd(12))} ${chalk.white(task.id)} - ${chalk.gray(task.title)}`);
1013
+ }
1014
+ console.log('');
1015
+ totalTasks += tasks.length;
1016
+ }
1017
+ }
1018
+ catch {
1019
+ console.log(chalk.bold.cyan(` [${project.name}]`));
1020
+ console.log(chalk.gray(' No tasks or no .rulebook config'));
1021
+ console.log('');
1022
+ }
1023
+ }
1024
+ console.log(chalk.gray(`${totalTasks} task(s) across ${ws.config.projects.length} project(s)`));
1025
+ return;
1026
+ }
1027
+ // Single project (optionally targeted via --project)
1028
+ const { taskManager, projectLabel } = await resolveTaskManager(cwd, wsOptions);
936
1029
  const tasks = await taskManager.listTasks(includeArchived);
937
1030
  if (tasks.length === 0) {
938
1031
  console.log(chalk.gray('No tasks found'));
939
1032
  return;
940
1033
  }
941
- console.log(chalk.bold.blue('\n📋 Rulebook Tasks\n'));
1034
+ const header = projectLabel
1035
+ ? `\n📋 Rulebook Tasks [${projectLabel}]\n`
1036
+ : '\n📋 Rulebook Tasks\n';
1037
+ console.log(chalk.bold.blue(header));
942
1038
  const activeTasks = tasks.filter((t) => !t.archivedAt);
943
1039
  const archivedTasks = tasks.filter((t) => t.archivedAt);
944
1040
  if (activeTasks.length > 0) {
@@ -968,15 +1064,10 @@ export async function taskListCommand(includeArchived = false) {
968
1064
  process.exit(1);
969
1065
  }
970
1066
  }
971
- export async function taskShowCommand(taskId) {
1067
+ export async function taskShowCommand(taskId, wsOptions) {
972
1068
  try {
973
1069
  const cwd = process.cwd();
974
- const { createTaskManager } = await import('../core/task-manager.js');
975
- const { createConfigManager } = await import('../core/config-manager.js');
976
- const configManager = createConfigManager(cwd);
977
- const config = await configManager.loadConfig();
978
- const rulebookDir = config.rulebookDir || '.rulebook';
979
- const taskManager = createTaskManager(cwd, rulebookDir);
1070
+ const { taskManager } = await resolveTaskManager(cwd, wsOptions);
980
1071
  const task = await taskManager.showTask(taskId);
981
1072
  if (!task) {
982
1073
  console.error(chalk.red(`❌ Task ${taskId} not found`));
@@ -1010,15 +1101,10 @@ export async function taskShowCommand(taskId) {
1010
1101
  process.exit(1);
1011
1102
  }
1012
1103
  }
1013
- export async function taskValidateCommand(taskId) {
1104
+ export async function taskValidateCommand(taskId, wsOptions) {
1014
1105
  try {
1015
1106
  const cwd = process.cwd();
1016
- const { createTaskManager } = await import('../core/task-manager.js');
1017
- const { createConfigManager } = await import('../core/config-manager.js');
1018
- const configManager = createConfigManager(cwd);
1019
- const config = await configManager.loadConfig();
1020
- const rulebookDir = config.rulebookDir || '.rulebook';
1021
- const taskManager = createTaskManager(cwd, rulebookDir);
1107
+ const { taskManager } = await resolveTaskManager(cwd, wsOptions);
1022
1108
  const validation = await taskManager.validateTask(taskId);
1023
1109
  if (validation.valid) {
1024
1110
  console.log(chalk.green(`✅ Task ${taskId} is valid`));
@@ -1049,17 +1135,13 @@ export async function taskValidateCommand(taskId) {
1049
1135
  process.exit(1);
1050
1136
  }
1051
1137
  }
1052
- export async function taskArchiveCommand(taskId, skipValidation = false) {
1138
+ export async function taskArchiveCommand(taskId, skipValidation = false, wsOptions) {
1053
1139
  try {
1054
1140
  const cwd = process.cwd();
1055
- const { createTaskManager } = await import('../core/task-manager.js');
1056
- const { createConfigManager } = await import('../core/config-manager.js');
1057
- const configManager = createConfigManager(cwd);
1058
- const config = await configManager.loadConfig();
1059
- const rulebookDir = config.rulebookDir || '.rulebook';
1060
- const taskManager = createTaskManager(cwd, rulebookDir);
1141
+ const { taskManager, projectLabel } = await resolveTaskManager(cwd, wsOptions);
1061
1142
  await taskManager.archiveTask(taskId, skipValidation);
1062
- console.log(chalk.green(`✅ Task ${taskId} archived successfully`));
1143
+ const prefix = projectLabel ? `[${projectLabel}] ` : '';
1144
+ console.log(chalk.green(`✅ ${prefix}Task ${taskId} archived successfully`));
1063
1145
  }
1064
1146
  catch (error) {
1065
1147
  console.error(chalk.red(`❌ Failed to archive task: ${error.message}`));
@@ -1070,23 +1152,59 @@ export async function taskArchiveCommand(taskId, skipValidation = false) {
1070
1152
  * Initialize MCP configuration in .rulebook file
1071
1153
  * Adds mcp block to .rulebook and creates/updates .cursor/mcp.json
1072
1154
  */
1073
- export async function mcpInitCommand() {
1155
+ export async function mcpInitCommand(options) {
1074
1156
  const { findRulebookConfig } = await import('../mcp/rulebook-server.js');
1075
1157
  const { existsSync, readFileSync, writeFileSync, statSync } = await import('fs');
1076
1158
  const { join, dirname } = await import('path');
1077
1159
  const { createConfigManager } = await import('../core/config-manager.js');
1078
1160
  try {
1079
- // Find or create .rulebook file/directory
1080
1161
  const cwd = process.cwd();
1162
+ // --- Workspace mode ---
1163
+ if (options?.workspace) {
1164
+ const wsConfig = WorkspaceManager.findWorkspaceConfig(cwd);
1165
+ if (!wsConfig) {
1166
+ console.error(chalk.red('No workspace found. Run `rulebook workspace init` first.'));
1167
+ process.exit(1);
1168
+ }
1169
+ const mcpArgs = ['-y', '@hivehub/rulebook@latest', 'mcp-server', '--workspace'];
1170
+ const mcpEntry = { command: 'npx', args: mcpArgs };
1171
+ // Write to .cursor/mcp.json if .cursor exists
1172
+ const cursorDir = join(cwd, '.cursor');
1173
+ if (existsSync(cursorDir)) {
1174
+ const mcpJsonPath = join(cursorDir, 'mcp.json');
1175
+ let mcpConfig = { mcpServers: {} };
1176
+ if (existsSync(mcpJsonPath)) {
1177
+ mcpConfig = JSON.parse(readFileSync(mcpJsonPath, 'utf8'));
1178
+ }
1179
+ mcpConfig.mcpServers = mcpConfig.mcpServers ?? {};
1180
+ mcpConfig.mcpServers.rulebook = mcpEntry;
1181
+ writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + '\n');
1182
+ console.log(chalk.green('✓ Workspace MCP initialized'));
1183
+ console.log(chalk.gray(` • Updated .cursor/mcp.json with --workspace flag`));
1184
+ }
1185
+ // Write to .mcp.json (Claude Code format)
1186
+ const mcpJsonPath = join(cwd, '.mcp.json');
1187
+ let mcpConfig = { mcpServers: {} };
1188
+ if (existsSync(mcpJsonPath)) {
1189
+ mcpConfig = JSON.parse(readFileSync(mcpJsonPath, 'utf8'));
1190
+ }
1191
+ mcpConfig.mcpServers = mcpConfig.mcpServers ?? {};
1192
+ mcpConfig.mcpServers.rulebook = mcpEntry;
1193
+ writeFileSync(mcpJsonPath, JSON.stringify(mcpConfig, null, 2) + '\n');
1194
+ console.log(chalk.green('✓ Workspace MCP initialized'));
1195
+ console.log(chalk.gray(` • Workspace: ${wsConfig.name} (${wsConfig.projects.length} projects)`));
1196
+ console.log(chalk.gray(` • Updated .mcp.json with --workspace flag`));
1197
+ console.log(chalk.gray(` • MCP server will manage all projects automatically`));
1198
+ return;
1199
+ }
1200
+ // --- Single-project mode (original behavior) ---
1081
1201
  let rulebookPath = findRulebookConfig(cwd);
1082
1202
  if (!rulebookPath) {
1083
- // Create new .rulebook directory via ConfigManager
1084
1203
  rulebookPath = join(cwd, '.rulebook');
1085
1204
  const configManager = createConfigManager(cwd);
1086
1205
  await configManager.initializeConfig();
1087
1206
  }
1088
1207
  const projectRoot = dirname(rulebookPath);
1089
- // Resolve config file path (handle .rulebook as directory or file)
1090
1208
  let configFilePath = rulebookPath;
1091
1209
  if (existsSync(rulebookPath)) {
1092
1210
  const stats = statSync(rulebookPath);
@@ -1094,13 +1212,11 @@ export async function mcpInitCommand() {
1094
1212
  configFilePath = join(rulebookPath, 'rulebook.json');
1095
1213
  }
1096
1214
  }
1097
- // Load existing config
1098
1215
  let config = {};
1099
1216
  if (existsSync(configFilePath)) {
1100
1217
  const raw = readFileSync(configFilePath, 'utf8');
1101
1218
  config = JSON.parse(raw);
1102
1219
  }
1103
- // Add/update mcp block
1104
1220
  config.mcp = config.mcp ?? {};
1105
1221
  if (config.mcp.enabled === undefined)
1106
1222
  config.mcp.enabled = true;
@@ -1108,9 +1224,7 @@ export async function mcpInitCommand() {
1108
1224
  config.mcp.tasksDir = '.rulebook/tasks';
1109
1225
  if (!config.mcp.archiveDir)
1110
1226
  config.mcp.archiveDir = '.rulebook/archive';
1111
- // Save updated config
1112
1227
  writeFileSync(configFilePath, JSON.stringify(config, null, 2) + '\n');
1113
- // Create/update .cursor/mcp.json if .cursor directory exists
1114
1228
  const cursorDir = join(projectRoot, '.cursor');
1115
1229
  if (existsSync(cursorDir)) {
1116
1230
  const mcpJsonPath = join(cursorDir, 'mcp.json');
@@ -1181,144 +1295,195 @@ export async function tasksCommand(options) {
1181
1295
  export async function updateCommand(options) {
1182
1296
  try {
1183
1297
  const cwd = process.cwd();
1184
- console.log(chalk.bold.blue('\n🔄 Rulebook Update Tool\n'));
1185
- console.log(chalk.gray('This will update your AGENTS.md and .rulebook to the latest version\n'));
1186
- // Detect project
1187
- const spinner = ora('Detecting project structure...').start();
1188
- const detection = await detectProject(cwd);
1189
- spinner.succeed('Project detection complete');
1190
- // Show detected languages
1191
- if (detection.languages.length > 0) {
1192
- console.log(chalk.green('\n✓ Detected languages:'));
1193
- for (const lang of detection.languages) {
1194
- console.log(` - ${lang.language} (${(lang.confidence * 100).toFixed(0)}% confidence)`);
1298
+ // Auto-detect workspace: if inside a workspace, update ALL projects
1299
+ const ws = WorkspaceManager.findWorkspaceFromCwd(cwd);
1300
+ if (ws && ws.config.projects.length > 1) {
1301
+ console.log(chalk.bold.blue('\n🔄 Rulebook Workspace Update\n'));
1302
+ console.log(chalk.gray(`Workspace "${ws.config.name}" detected updating ${ws.config.projects.length} projects\n`));
1303
+ const { isAbsolute, resolve, join } = await import('path');
1304
+ const fsPromises = await import('fs/promises');
1305
+ let updatedCount = 0;
1306
+ // Build workspace project list for the template
1307
+ const projectListMd = ws.config.projects
1308
+ .map((p) => {
1309
+ const root = isAbsolute(p.path) ? p.path : resolve(ws.root, p.path);
1310
+ return `- **${p.name}** → \`${root}\``;
1311
+ })
1312
+ .join('\n');
1313
+ // Load WORKSPACE.md template
1314
+ const { getDefaultTemplatesPath } = await import('../core/skills-manager.js');
1315
+ let workspaceTplContent = '';
1316
+ try {
1317
+ const tplPath = join(getDefaultTemplatesPath(), 'core', 'WORKSPACE.md');
1318
+ workspaceTplContent = await fsPromises.readFile(tplPath, 'utf-8');
1195
1319
  }
1320
+ catch {
1321
+ // Template not available — skip
1322
+ }
1323
+ for (const project of ws.config.projects) {
1324
+ const projectRoot = isAbsolute(project.path)
1325
+ ? project.path
1326
+ : resolve(ws.root, project.path);
1327
+ console.log(chalk.bold.cyan(`\n━━━ [${project.name}] ${projectRoot} ━━━\n`));
1328
+ try {
1329
+ await updateSingleProject(projectRoot, options);
1330
+ // Inject WORKSPACE.md spec into this project's .rulebook/specs/
1331
+ if (workspaceTplContent) {
1332
+ const specsDir = join(projectRoot, '.rulebook', 'specs');
1333
+ await fsPromises.mkdir(specsDir, { recursive: true });
1334
+ const rendered = workspaceTplContent
1335
+ .replace('{{DEFAULT_PROJECT}}', ws.config.defaultProject ?? ws.config.projects[0]?.name ?? '')
1336
+ .replace('{{WORKSPACE_PROJECTS}}', projectListMd);
1337
+ await fsPromises.writeFile(join(specsDir, 'WORKSPACE.md'), rendered, 'utf-8');
1338
+ }
1339
+ updatedCount++;
1340
+ }
1341
+ catch (error) {
1342
+ console.error(chalk.red(` ❌ Failed to update ${project.name}: ${error.message}`));
1343
+ }
1344
+ }
1345
+ console.log(chalk.bold.green(`\n✅ Workspace update complete — ${updatedCount}/${ws.config.projects.length} projects updated\n`));
1346
+ return;
1196
1347
  }
1197
- // Check for existing AGENTS.md
1198
- if (!detection.existingAgents) {
1199
- console.log(chalk.yellow('\n⚠ No AGENTS.md found. Use "rulebook init" instead.'));
1348
+ // Single project update
1349
+ console.log(chalk.bold.blue('\n🔄 Rulebook Update Tool\n'));
1350
+ console.log(chalk.gray('This will update your AGENTS.md and .rulebook to the latest version\n'));
1351
+ await updateSingleProject(cwd, options);
1352
+ }
1353
+ catch (error) {
1354
+ console.error(chalk.red('\n❌ Update failed:'), error);
1355
+ process.exit(1);
1356
+ }
1357
+ }
1358
+ /** Update a single project at the given root directory. */
1359
+ async function updateSingleProject(cwd, options) {
1360
+ // Detect project
1361
+ const spinner = ora('Detecting project structure...').start();
1362
+ const detection = await detectProject(cwd);
1363
+ spinner.succeed('Project detection complete');
1364
+ // Show detected languages
1365
+ if (detection.languages.length > 0) {
1366
+ console.log(chalk.green('\n✓ Detected languages:'));
1367
+ for (const lang of detection.languages) {
1368
+ console.log(` - ${lang.language} (${(lang.confidence * 100).toFixed(0)}% confidence)`);
1369
+ }
1370
+ }
1371
+ // Check for existing AGENTS.md
1372
+ if (!detection.existingAgents) {
1373
+ console.log(chalk.yellow('\n⚠ No AGENTS.md found. Use "rulebook init" instead.'));
1374
+ process.exit(0);
1375
+ }
1376
+ console.log(chalk.green(`\n✓ Found existing AGENTS.md with ${detection.existingAgents.blocks.length} blocks`));
1377
+ // Get existing blocks to preserve user customizations
1378
+ const existingBlocks = detection.existingAgents.blocks.map((b) => b.name);
1379
+ console.log(chalk.gray(` Existing blocks: ${existingBlocks.join(', ')}`));
1380
+ let inquirerModule = null;
1381
+ if (!options.yes) {
1382
+ inquirerModule = (await import('inquirer')).default;
1383
+ const { confirm } = await inquirerModule.prompt([
1384
+ {
1385
+ type: 'confirm',
1386
+ name: 'confirm',
1387
+ message: 'Update AGENTS.md and .rulebook with latest templates?',
1388
+ default: true,
1389
+ },
1390
+ ]);
1391
+ if (!confirm) {
1392
+ console.log(chalk.yellow('\nUpdate cancelled'));
1200
1393
  process.exit(0);
1201
1394
  }
1202
- console.log(chalk.green(`\n✓ Found existing AGENTS.md with ${detection.existingAgents.blocks.length} blocks`));
1203
- // Get existing blocks to preserve user customizations
1204
- const existingBlocks = detection.existingAgents.blocks.map((b) => b.name);
1205
- console.log(chalk.gray(` Existing blocks: ${existingBlocks.join(', ')}`));
1206
- let inquirerModule = null;
1207
- if (!options.yes) {
1208
- inquirerModule = (await import('inquirer')).default;
1209
- const { confirm } = await inquirerModule.prompt([
1395
+ }
1396
+ const hasPreCommit = detection.gitHooks?.preCommitExists ?? false;
1397
+ const hasPrePush = detection.gitHooks?.prePushExists ?? false;
1398
+ const missingHooks = !hasPreCommit || !hasPrePush;
1399
+ let installHooksOnUpdate = false;
1400
+ let hooksInstalledOnUpdate = false;
1401
+ if (missingHooks) {
1402
+ if (options.yes) {
1403
+ console.log(chalk.yellow('\n⚠ Git hooks are missing. Re-run "rulebook update" without --yes to install automated hooks or install them manually.'));
1404
+ }
1405
+ else {
1406
+ if (!inquirerModule) {
1407
+ inquirerModule = (await import('inquirer')).default;
1408
+ }
1409
+ const { installHooks } = await inquirerModule.prompt([
1210
1410
  {
1211
1411
  type: 'confirm',
1212
- name: 'confirm',
1213
- message: 'Update AGENTS.md and .rulebook with latest templates?',
1412
+ name: 'installHooks',
1413
+ message: `Install Git hooks for automated quality checks? Missing: ${hasPreCommit ? '' : 'pre-commit '}${hasPrePush ? '' : 'pre-push'}`.trim(),
1214
1414
  default: true,
1215
1415
  },
1216
1416
  ]);
1217
- if (!confirm) {
1218
- console.log(chalk.yellow('\nUpdate cancelled'));
1219
- process.exit(0);
1220
- }
1221
- }
1222
- const hasPreCommit = detection.gitHooks?.preCommitExists ?? false;
1223
- const hasPrePush = detection.gitHooks?.prePushExists ?? false;
1224
- const missingHooks = !hasPreCommit || !hasPrePush;
1225
- let installHooksOnUpdate = false;
1226
- let hooksInstalledOnUpdate = false;
1227
- if (missingHooks) {
1228
- if (options.yes) {
1229
- console.log(chalk.yellow('\n⚠ Git hooks are missing. Re-run "rulebook update" without --yes to install automated hooks or install them manually.'));
1230
- }
1231
- else {
1232
- if (!inquirerModule) {
1233
- inquirerModule = (await import('inquirer')).default;
1234
- }
1235
- const { installHooks } = await inquirerModule.prompt([
1236
- {
1237
- type: 'confirm',
1238
- name: 'installHooks',
1239
- message: `Install Git hooks for automated quality checks? Missing: ${hasPreCommit ? '' : 'pre-commit '}${hasPrePush ? '' : 'pre-push'}`.trim(),
1240
- default: true,
1241
- },
1242
- ]);
1243
- installHooksOnUpdate = installHooks;
1244
- }
1245
- }
1246
- if (missingHooks && !installHooksOnUpdate && !options.yes) {
1247
- 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.'));
1417
+ installHooksOnUpdate = installHooks;
1248
1418
  }
1249
- const agentsPath = path.join(cwd, 'AGENTS.md');
1250
- // Load existing config using ConfigManager
1251
- const { createConfigManager } = await import('../core/config-manager.js');
1252
- const configManager = createConfigManager(cwd);
1253
- const existingConfig = await configManager.loadConfig();
1254
- let existingMode;
1255
- let existingLightMode;
1256
- if (existingConfig) {
1257
- if (existingConfig && (existingConfig.mode === 'minimal' || existingConfig.mode === 'full')) {
1258
- existingMode = existingConfig.mode;
1259
- }
1260
- if (existingConfig && existingConfig.lightMode !== undefined) {
1261
- existingLightMode = existingConfig.lightMode;
1262
- }
1263
- }
1264
- const minimalMode = options.minimal ?? existingMode === 'minimal';
1265
- const lightMode = options.light !== undefined ? options.light : (existingLightMode ?? false);
1266
- const leanMode = options.lean ?? existingConfig?.agentsMode === 'lean';
1267
- // Build config from detected project
1268
- const config = {
1269
- languages: detection.languages.map((l) => l.language),
1270
- modules: minimalMode ? [] : detection.modules.filter((m) => m.detected).map((m) => m.module),
1271
- frameworks: detection.frameworks.filter((f) => f.detected).map((f) => f.framework),
1272
- ides: [], // Preserve existing IDE choices
1273
- projectType: 'application',
1274
- coverageThreshold: 95,
1275
- strictDocs: true,
1276
- generateWorkflows: false, // Don't regenerate workflows on update
1277
- includeGitWorkflow: true,
1278
- gitPushMode: 'manual',
1279
- installGitHooks: installHooksOnUpdate,
1280
- minimal: minimalMode,
1281
- lightMode: lightMode,
1282
- ...(leanMode ? { agentsMode: 'lean' } : {}),
1283
- };
1284
- if (minimalMode) {
1285
- config.ides = [];
1286
- config.generateWorkflows = true;
1287
- }
1288
- let minimalArtifacts = [];
1289
- if (minimalMode) {
1290
- minimalArtifacts = await scaffoldMinimalProject(cwd, {
1291
- projectName: path.basename(cwd),
1292
- description: 'Essential project scaffolding refreshed via Rulebook minimal mode.',
1293
- license: 'MIT',
1294
- });
1295
- }
1296
- // Generate Rulebook commands if Cursor is detected
1297
- // This ensures commands are available for all Cursor projects
1298
- const cursorRulesPath = path.join(cwd, '.cursorrules');
1299
- const cursorCommandsDir = path.join(cwd, '.cursor', 'commands');
1300
- const usesCursor = existsSync(cursorRulesPath) || existsSync(cursorCommandsDir);
1301
- // Deprecated notice: .cursorrules is superseded by .cursor/rules/*.mdc in Cursor v0.45+
1302
- if (existsSync(cursorRulesPath)) {
1303
- console.log(chalk.yellow(' ⚠ .cursorrules is deprecated as of Cursor v0.45. Use .cursor/rules/*.mdc instead.'));
1304
- }
1305
- if (usesCursor) {
1306
- // Check if commands already exist to avoid duplicate generation
1307
- const existingCommandsDir = path.join(cwd, '.cursor', 'commands');
1308
- if (existsSync(existingCommandsDir)) {
1309
- const { readdir } = await import('fs/promises');
1310
- const existingFiles = await readdir(existingCommandsDir);
1311
- const hasRulebookCommands = existingFiles.some((file) => file.startsWith('rulebook-task-'));
1312
- if (!hasRulebookCommands) {
1313
- const { generateCursorCommands } = await import('../core/workflow-generator.js');
1314
- const generatedCommands = await generateCursorCommands(cwd);
1315
- if (generatedCommands.length > 0) {
1316
- console.log(chalk.green(` Generated ${generatedCommands.length} Rulebook command(s) in .cursor/commands/`));
1317
- }
1318
- }
1319
- }
1320
- else {
1321
- // Directory doesn't exist, create it and generate commands
1419
+ }
1420
+ if (missingHooks && !installHooksOnUpdate && !options.yes) {
1421
+ 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.'));
1422
+ }
1423
+ const agentsPath = path.join(cwd, 'AGENTS.md');
1424
+ // Load existing config using ConfigManager
1425
+ const { createConfigManager } = await import('../core/config-manager.js');
1426
+ const configManager = createConfigManager(cwd);
1427
+ const existingConfig = await configManager.loadConfig();
1428
+ let existingMode;
1429
+ let existingLightMode;
1430
+ if (existingConfig) {
1431
+ if (existingConfig && (existingConfig.mode === 'minimal' || existingConfig.mode === 'full')) {
1432
+ existingMode = existingConfig.mode;
1433
+ }
1434
+ if (existingConfig && existingConfig.lightMode !== undefined) {
1435
+ existingLightMode = existingConfig.lightMode;
1436
+ }
1437
+ }
1438
+ const minimalMode = options.minimal ?? existingMode === 'minimal';
1439
+ const lightMode = options.light !== undefined ? options.light : (existingLightMode ?? false);
1440
+ const leanMode = options.lean ?? existingConfig?.agentsMode === 'lean';
1441
+ // Build config from detected project
1442
+ const config = {
1443
+ languages: detection.languages.map((l) => l.language),
1444
+ modules: minimalMode ? [] : detection.modules.filter((m) => m.detected).map((m) => m.module),
1445
+ frameworks: detection.frameworks.filter((f) => f.detected).map((f) => f.framework),
1446
+ ides: [], // Preserve existing IDE choices
1447
+ projectType: 'application',
1448
+ coverageThreshold: 95,
1449
+ strictDocs: true,
1450
+ generateWorkflows: false, // Don't regenerate workflows on update
1451
+ includeGitWorkflow: true,
1452
+ gitPushMode: 'manual',
1453
+ installGitHooks: installHooksOnUpdate,
1454
+ minimal: minimalMode,
1455
+ lightMode: lightMode,
1456
+ ...(leanMode ? { agentsMode: 'lean' } : {}),
1457
+ };
1458
+ if (minimalMode) {
1459
+ config.ides = [];
1460
+ config.generateWorkflows = true;
1461
+ }
1462
+ let minimalArtifacts = [];
1463
+ if (minimalMode) {
1464
+ minimalArtifacts = await scaffoldMinimalProject(cwd, {
1465
+ projectName: path.basename(cwd),
1466
+ description: 'Essential project scaffolding refreshed via Rulebook minimal mode.',
1467
+ license: 'MIT',
1468
+ });
1469
+ }
1470
+ // Generate Rulebook commands if Cursor is detected
1471
+ // This ensures commands are available for all Cursor projects
1472
+ const cursorRulesPath = path.join(cwd, '.cursorrules');
1473
+ const cursorCommandsDir = path.join(cwd, '.cursor', 'commands');
1474
+ const usesCursor = existsSync(cursorRulesPath) || existsSync(cursorCommandsDir);
1475
+ // Deprecated notice: .cursorrules is superseded by .cursor/rules/*.mdc in Cursor v0.45+
1476
+ if (existsSync(cursorRulesPath)) {
1477
+ console.log(chalk.yellow(' ⚠ .cursorrules is deprecated as of Cursor v0.45. Use .cursor/rules/*.mdc instead.'));
1478
+ }
1479
+ if (usesCursor) {
1480
+ // Check if commands already exist to avoid duplicate generation
1481
+ const existingCommandsDir = path.join(cwd, '.cursor', 'commands');
1482
+ if (existsSync(existingCommandsDir)) {
1483
+ const { readdir } = await import('fs/promises');
1484
+ const existingFiles = await readdir(existingCommandsDir);
1485
+ const hasRulebookCommands = existingFiles.some((file) => file.startsWith('rulebook-task-'));
1486
+ if (!hasRulebookCommands) {
1322
1487
  const { generateCursorCommands } = await import('../core/workflow-generator.js');
1323
1488
  const generatedCommands = await generateCursorCommands(cwd);
1324
1489
  if (generatedCommands.length > 0) {
@@ -1326,256 +1491,260 @@ export async function updateCommand(options) {
1326
1491
  }
1327
1492
  }
1328
1493
  }
1329
- // Migration already done via configManager.loadConfig() -> migrateConfig() -> migrateDirectoryStructure()
1330
- // No need to call it again here
1331
- // Load existing config to preserve skills and ralph settings (already loaded above)
1332
- const existingSkills = existingConfig.skills?.enabled || [];
1333
- const existingRalph = existingConfig.ralph;
1334
- // Auto-detect skills based on project detection (v2.0)
1335
- let detectedSkills = [];
1336
- try {
1337
- const { SkillsManager, getDefaultTemplatesPath } = await import('../core/skills-manager.js');
1338
- const skillsManager = new SkillsManager(getDefaultTemplatesPath(), cwd);
1339
- // Build a RulebookConfig-like object for skill detection
1340
- const rulebookConfigForSkills = {
1341
- languages: config.languages,
1342
- frameworks: config.frameworks,
1343
- modules: config.modules,
1344
- services: config.services,
1345
- };
1346
- detectedSkills = await skillsManager.autoDetectSkills(rulebookConfigForSkills);
1347
- // Merge with existing skills (keep existing, add new detected)
1348
- const mergedSkills = [...new Set([...existingSkills, ...detectedSkills])];
1349
- if (detectedSkills.length > existingSkills.length) {
1350
- const newSkills = detectedSkills.filter((s) => !existingSkills.includes(s));
1351
- if (newSkills.length > 0) {
1352
- console.log(chalk.green('\n✓ New skills detected:'));
1353
- for (const skillId of newSkills) {
1354
- console.log(chalk.gray(` - ${skillId}`));
1355
- }
1356
- }
1494
+ else {
1495
+ // Directory doesn't exist, create it and generate commands
1496
+ const { generateCursorCommands } = await import('../core/workflow-generator.js');
1497
+ const generatedCommands = await generateCursorCommands(cwd);
1498
+ if (generatedCommands.length > 0) {
1499
+ console.log(chalk.green(` Generated ${generatedCommands.length} Rulebook command(s) in .cursor/commands/`));
1357
1500
  }
1358
- detectedSkills = mergedSkills;
1359
- }
1360
- catch {
1361
- // Skills system not available or error - preserve existing skills
1362
- detectedSkills = existingSkills;
1363
1501
  }
1364
- await configManager.updateConfig({
1502
+ }
1503
+ // Migration already done via configManager.loadConfig() -> migrateConfig() -> migrateDirectoryStructure()
1504
+ // No need to call it again here
1505
+ // Load existing config to preserve skills and ralph settings (already loaded above)
1506
+ const existingSkills = existingConfig.skills?.enabled || [];
1507
+ const existingRalph = existingConfig.ralph;
1508
+ // Auto-detect skills based on project detection (v2.0)
1509
+ let detectedSkills = [];
1510
+ try {
1511
+ const { SkillsManager, getDefaultTemplatesPath } = await import('../core/skills-manager.js');
1512
+ const skillsManager = new SkillsManager(getDefaultTemplatesPath(), cwd);
1513
+ // Build a RulebookConfig-like object for skill detection
1514
+ const rulebookConfigForSkills = {
1365
1515
  languages: config.languages,
1366
1516
  frameworks: config.frameworks,
1367
1517
  modules: config.modules,
1368
1518
  services: config.services,
1369
- modular: config.modular ?? true,
1370
- rulebookDir: config.rulebookDir || '.rulebook',
1371
- skills: detectedSkills.length > 0 ? { enabled: detectedSkills } : undefined,
1372
- ralph: existingRalph,
1373
- memory: existingConfig.memory,
1374
- });
1375
- // Ensure .rulebook is in .gitignore with exceptions for specs/tasks
1376
- await configManager.ensureGitignore();
1377
- // Migrate flat layout to specs/ subdirectory if needed
1378
- {
1379
- const { hasFlatLayout, migrateFlatToSpecs } = await import('../core/migrator.js');
1380
- const rulebookDirForMigration = config.rulebookDir || '.rulebook';
1381
- if (await hasFlatLayout(cwd, rulebookDirForMigration)) {
1382
- const migrationSpinner = ora('Migrating rulebook files to specs/ subdirectory...').start();
1383
- const { migratedFiles } = await migrateFlatToSpecs(cwd, rulebookDirForMigration);
1384
- if (migratedFiles.length > 0) {
1385
- migrationSpinner.succeed(`Migrated ${migratedFiles.length} file(s) to /${rulebookDirForMigration}/specs/`);
1386
- }
1387
- else {
1388
- migrationSpinner.info('No files to migrate');
1519
+ };
1520
+ detectedSkills = await skillsManager.autoDetectSkills(rulebookConfigForSkills);
1521
+ // Merge with existing skills (keep existing, add new detected)
1522
+ const mergedSkills = [...new Set([...existingSkills, ...detectedSkills])];
1523
+ if (detectedSkills.length > existingSkills.length) {
1524
+ const newSkills = detectedSkills.filter((s) => !existingSkills.includes(s));
1525
+ if (newSkills.length > 0) {
1526
+ console.log(chalk.green('\n✓ New skills detected:'));
1527
+ for (const skillId of newSkills) {
1528
+ console.log(chalk.gray(` - ${skillId}`));
1389
1529
  }
1390
1530
  }
1391
1531
  }
1392
- // Merge with existing AGENTS.md (with migration support)
1393
- const mergeSpinner = ora('Updating AGENTS.md with latest templates...').start();
1394
- config.modular = config.modular ?? true; // Enable modular by default
1395
- const mergedContent = await mergeFullAgents(detection.existingAgents, config, cwd);
1396
- await writeFile(agentsPath, mergedContent);
1397
- mergeSpinner.succeed('AGENTS.md updated');
1398
- // Show multi-tool config feedback (update command)
1399
- if (detection.geminiCli?.detected) {
1400
- console.log(chalk.gray(' • Gemini CLI config updated: GEMINI.md'));
1401
- }
1402
- if (detection.continueDev?.detected) {
1403
- console.log(chalk.gray(' • Continue.dev rules updated in .continue/rules/'));
1404
- }
1405
- if (detection.windsurf?.detected) {
1406
- console.log(chalk.gray(' • Windsurf rules updated: .windsurfrules'));
1407
- }
1408
- if (detection.githubCopilot?.detected) {
1409
- console.log(chalk.gray(' • GitHub Copilot instructions updated in .github/'));
1410
- }
1411
- if (installHooksOnUpdate) {
1412
- const hookLanguages = detection.languages.length > 0
1413
- ? detection.languages
1414
- : config.languages.map((language) => ({
1415
- language: language,
1416
- confidence: 1,
1417
- indicators: [],
1418
- }));
1419
- const hookSpinner = ora('Installing Git hooks (pre-commit & pre-push)...').start();
1420
- try {
1421
- await installGitHooks({ languages: hookLanguages, cwd });
1422
- hookSpinner.succeed('Git hooks installed successfully');
1423
- hooksInstalledOnUpdate = true;
1424
- }
1425
- catch (error) {
1426
- hookSpinner.fail('Failed to install Git hooks');
1427
- console.error(chalk.red(' ➤'), error instanceof Error ? error.message : error);
1428
- console.log(chalk.yellow(' ⚠ Skipping automatic hook installation. You can rerun "rulebook update" later to retry or install manually.'));
1429
- }
1430
- }
1431
- const gitHooksActiveAfterUpdate = hooksInstalledOnUpdate || (hasPreCommit && hasPrePush);
1432
- config.installGitHooks = gitHooksActiveAfterUpdate;
1433
- // Update .rulebook config
1434
- const configSpinner = ora('Updating .rulebook configuration...').start();
1435
- const rulebookFeatures = {
1436
- watcher: false,
1437
- agent: false,
1438
- logging: true,
1439
- telemetry: false,
1440
- notifications: false,
1441
- dryRun: false,
1442
- gitHooks: gitHooksActiveAfterUpdate,
1443
- repl: false,
1444
- templates: true,
1445
- context: minimalMode ? false : true,
1446
- health: true,
1447
- plugins: false,
1448
- parallel: minimalMode ? false : true,
1449
- smartContinue: minimalMode ? false : true,
1450
- };
1451
- const rulebookConfig = {
1452
- version: getRulebookVersion(),
1453
- installedAt: detection.existingAgents.content?.match(/Generated at: (.+)/)?.[1] ||
1454
- new Date().toISOString(),
1455
- updatedAt: new Date().toISOString(),
1456
- projectId: path.basename(cwd),
1457
- mode: minimalMode ? 'minimal' : 'full',
1458
- features: rulebookFeatures,
1459
- coverageThreshold: existingConfig.coverageThreshold ?? 95,
1460
- language: existingConfig.language ?? 'en',
1461
- outputLanguage: existingConfig.outputLanguage ?? 'en',
1462
- cliTools: existingConfig.cliTools ?? [],
1463
- maxParallelTasks: existingConfig.maxParallelTasks ?? 5,
1464
- timeouts: existingConfig.timeouts ?? {
1465
- taskExecution: 3600000,
1466
- cliResponse: 180000,
1467
- testRun: 600000,
1468
- },
1469
- ...(existingConfig.memory ? { memory: existingConfig.memory } : {}),
1470
- ...(existingConfig.ralph ? { ralph: existingConfig.ralph } : {}),
1471
- ...(existingConfig.skills ? { skills: existingConfig.skills } : {}),
1472
- ...(leanMode
1473
- ? { agentsMode: 'lean' }
1474
- : existingConfig.agentsMode
1475
- ? { agentsMode: existingConfig.agentsMode }
1476
- : {}),
1477
- };
1478
- await configManager.saveConfig(rulebookConfig);
1479
- configSpinner.succeed('.rulebook configuration updated');
1480
- // Auto-setup Claude Code integration (MCP + skills)
1481
- const claudeSpinner = ora('Checking Claude Code integration...').start();
1482
- try {
1483
- const { setupClaudeCodeIntegration } = await import('../core/claude-mcp.js');
1484
- const result = await setupClaudeCodeIntegration(cwd);
1485
- if (result.detected) {
1486
- claudeSpinner.succeed('Claude Code integration updated');
1487
- if (result.mcpConfigured) {
1488
- console.log(chalk.gray(' • MCP server added to .mcp.json'));
1489
- }
1490
- if (result.skillsInstalled.length > 0) {
1491
- console.log(chalk.gray(` • ${result.skillsInstalled.length} skills updated in .claude/commands/`));
1492
- }
1493
- if (result.agentTeamsEnabled) {
1494
- console.log(chalk.gray(' • Multi-agent teams enabled in .claude/settings.json'));
1495
- }
1496
- if (result.agentDefinitionsInstalled.length > 0) {
1497
- console.log(chalk.gray(` • ${result.agentDefinitionsInstalled.length} agent definitions updated in .claude/agents/`));
1498
- }
1532
+ detectedSkills = mergedSkills;
1533
+ }
1534
+ catch {
1535
+ // Skills system not available or error - preserve existing skills
1536
+ detectedSkills = existingSkills;
1537
+ }
1538
+ await configManager.updateConfig({
1539
+ languages: config.languages,
1540
+ frameworks: config.frameworks,
1541
+ modules: config.modules,
1542
+ services: config.services,
1543
+ modular: config.modular ?? true,
1544
+ rulebookDir: config.rulebookDir || '.rulebook',
1545
+ skills: detectedSkills.length > 0 ? { enabled: detectedSkills } : undefined,
1546
+ ralph: existingRalph,
1547
+ memory: existingConfig.memory,
1548
+ });
1549
+ // Ensure .rulebook is in .gitignore with exceptions for specs/tasks
1550
+ await configManager.ensureGitignore();
1551
+ // Migrate flat layout to specs/ subdirectory if needed
1552
+ {
1553
+ const { hasFlatLayout, migrateFlatToSpecs } = await import('../core/migrator.js');
1554
+ const rulebookDirForMigration = config.rulebookDir || '.rulebook';
1555
+ if (await hasFlatLayout(cwd, rulebookDirForMigration)) {
1556
+ const migrationSpinner = ora('Migrating rulebook files to specs/ subdirectory...').start();
1557
+ const { migratedFiles } = await migrateFlatToSpecs(cwd, rulebookDirForMigration);
1558
+ if (migratedFiles.length > 0) {
1559
+ migrationSpinner.succeed(`Migrated ${migratedFiles.length} file(s) to /${rulebookDirForMigration}/specs/`);
1499
1560
  }
1500
1561
  else {
1501
- claudeSpinner.info('Claude Code not detected (skipped)');
1562
+ migrationSpinner.info('No files to migrate');
1502
1563
  }
1503
1564
  }
1504
- catch {
1505
- claudeSpinner.info('Claude Code integration skipped');
1506
- }
1507
- // Install/update Ralph shell scripts
1565
+ }
1566
+ // Merge with existing AGENTS.md (with migration support)
1567
+ const mergeSpinner = ora('Updating AGENTS.md with latest templates...').start();
1568
+ config.modular = config.modular ?? true; // Enable modular by default
1569
+ const mergedContent = await mergeFullAgents(detection.existingAgents, config, cwd);
1570
+ await writeFile(agentsPath, mergedContent);
1571
+ mergeSpinner.succeed('AGENTS.md updated');
1572
+ // Show multi-tool config feedback (update command)
1573
+ if (detection.geminiCli?.detected) {
1574
+ console.log(chalk.gray(' • Gemini CLI config updated: GEMINI.md'));
1575
+ }
1576
+ if (detection.continueDev?.detected) {
1577
+ console.log(chalk.gray(' • Continue.dev rules updated in .continue/rules/'));
1578
+ }
1579
+ if (detection.windsurf?.detected) {
1580
+ console.log(chalk.gray(' • Windsurf rules updated: .windsurfrules'));
1581
+ }
1582
+ if (detection.githubCopilot?.detected) {
1583
+ console.log(chalk.gray(' • GitHub Copilot instructions updated in .github/'));
1584
+ }
1585
+ if (installHooksOnUpdate) {
1586
+ const hookLanguages = detection.languages.length > 0
1587
+ ? detection.languages
1588
+ : config.languages.map((language) => ({
1589
+ language: language,
1590
+ confidence: 1,
1591
+ indicators: [],
1592
+ }));
1593
+ const hookSpinner = ora('Installing Git hooks (pre-commit & pre-push)...').start();
1508
1594
  try {
1509
- const { installRalphScripts } = await import('../core/ralph-scripts.js');
1510
- const scripts = await installRalphScripts(cwd);
1511
- if (scripts.length > 0) {
1512
- console.log(chalk.gray(` • ${scripts.length} Ralph scripts updated in .rulebook/scripts/`));
1595
+ await installGitHooks({ languages: hookLanguages, cwd });
1596
+ hookSpinner.succeed('Git hooks installed successfully');
1597
+ hooksInstalledOnUpdate = true;
1598
+ }
1599
+ catch (error) {
1600
+ hookSpinner.fail('Failed to install Git hooks');
1601
+ console.error(chalk.red(' ➤'), error instanceof Error ? error.message : error);
1602
+ console.log(chalk.yellow(' ⚠ Skipping automatic hook installation. You can rerun "rulebook update" later to retry or install manually.'));
1603
+ }
1604
+ }
1605
+ const gitHooksActiveAfterUpdate = hooksInstalledOnUpdate || (hasPreCommit && hasPrePush);
1606
+ config.installGitHooks = gitHooksActiveAfterUpdate;
1607
+ // Update .rulebook config
1608
+ const configSpinner = ora('Updating .rulebook configuration...').start();
1609
+ const rulebookFeatures = {
1610
+ watcher: false,
1611
+ agent: false,
1612
+ logging: true,
1613
+ telemetry: false,
1614
+ notifications: false,
1615
+ dryRun: false,
1616
+ gitHooks: gitHooksActiveAfterUpdate,
1617
+ repl: false,
1618
+ templates: true,
1619
+ context: minimalMode ? false : true,
1620
+ health: true,
1621
+ plugins: false,
1622
+ parallel: minimalMode ? false : true,
1623
+ smartContinue: minimalMode ? false : true,
1624
+ };
1625
+ const rulebookConfig = {
1626
+ version: getRulebookVersion(),
1627
+ installedAt: detection.existingAgents.content?.match(/Generated at: (.+)/)?.[1] ||
1628
+ new Date().toISOString(),
1629
+ updatedAt: new Date().toISOString(),
1630
+ projectId: path.basename(cwd),
1631
+ mode: minimalMode ? 'minimal' : 'full',
1632
+ features: rulebookFeatures,
1633
+ coverageThreshold: existingConfig.coverageThreshold ?? 95,
1634
+ language: existingConfig.language ?? 'en',
1635
+ outputLanguage: existingConfig.outputLanguage ?? 'en',
1636
+ cliTools: existingConfig.cliTools ?? [],
1637
+ maxParallelTasks: existingConfig.maxParallelTasks ?? 5,
1638
+ timeouts: existingConfig.timeouts ?? {
1639
+ taskExecution: 3600000,
1640
+ cliResponse: 180000,
1641
+ testRun: 600000,
1642
+ },
1643
+ ...(existingConfig.memory ? { memory: existingConfig.memory } : {}),
1644
+ ...(existingConfig.ralph ? { ralph: existingConfig.ralph } : {}),
1645
+ ...(existingConfig.skills ? { skills: existingConfig.skills } : {}),
1646
+ ...(leanMode
1647
+ ? { agentsMode: 'lean' }
1648
+ : existingConfig.agentsMode
1649
+ ? { agentsMode: existingConfig.agentsMode }
1650
+ : {}),
1651
+ };
1652
+ await configManager.saveConfig(rulebookConfig);
1653
+ configSpinner.succeed('.rulebook configuration updated');
1654
+ // Auto-setup Claude Code integration (MCP + skills)
1655
+ const claudeSpinner = ora('Checking Claude Code integration...').start();
1656
+ try {
1657
+ const { setupClaudeCodeIntegration } = await import('../core/claude-mcp.js');
1658
+ const result = await setupClaudeCodeIntegration(cwd);
1659
+ if (result.detected) {
1660
+ claudeSpinner.succeed('Claude Code integration updated');
1661
+ if (result.mcpConfigured) {
1662
+ console.log(chalk.gray(' • MCP server added to .mcp.json'));
1663
+ }
1664
+ if (result.skillsInstalled.length > 0) {
1665
+ console.log(chalk.gray(` • ${result.skillsInstalled.length} skills updated in .claude/commands/`));
1666
+ }
1667
+ if (result.agentTeamsEnabled) {
1668
+ console.log(chalk.gray(' • Multi-agent teams enabled in .claude/settings.json'));
1669
+ }
1670
+ if (result.agentDefinitionsInstalled.length > 0) {
1671
+ console.log(chalk.gray(` • ${result.agentDefinitionsInstalled.length} agent definitions updated in .claude/agents/`));
1513
1672
  }
1514
1673
  }
1515
- catch {
1516
- // Skip if Ralph scripts installation fails
1517
- }
1518
- // Ensure PLANS.md exists (create if missing, never overwrite)
1519
- try {
1520
- const { initPlans } = await import('../core/plans-manager.js');
1521
- await initPlans(cwd);
1522
- }
1523
- catch {
1524
- // Non-blocking
1525
- }
1526
- // Migrate memory directory if old structure exists
1527
- try {
1528
- await migrateMemoryDirectory();
1529
- }
1530
- catch {
1531
- // Silently skip if migration fails
1532
- }
1533
- // Install plugin in Claude Code
1534
- try {
1535
- await setupClaudeCodePlugin();
1536
- }
1537
- catch {
1538
- // Silently skip if plugin installation fails
1674
+ else {
1675
+ claudeSpinner.info('Claude Code not detected (skipped)');
1539
1676
  }
1540
- // Clean up any accidental duplicate directories
1541
- try {
1542
- const fsPromises = await import('fs/promises');
1543
- const accidentalDir = path.join(cwd, '.rulebook', '.rulebook');
1544
- if (existsSync(accidentalDir)) {
1545
- await fsPromises.rm(accidentalDir, { recursive: true, force: true });
1546
- }
1677
+ }
1678
+ catch {
1679
+ claudeSpinner.info('Claude Code integration skipped');
1680
+ }
1681
+ // Install/update Ralph shell scripts
1682
+ try {
1683
+ const { installRalphScripts } = await import('../core/ralph-scripts.js');
1684
+ const scripts = await installRalphScripts(cwd);
1685
+ if (scripts.length > 0) {
1686
+ console.log(chalk.gray(` • ${scripts.length} Ralph scripts updated in .rulebook/scripts/`));
1547
1687
  }
1548
- catch {
1549
- // Ignore cleanup errors
1550
- }
1551
- // Success message
1552
- console.log(chalk.bold.green('\n✅ Update complete!\n'));
1553
- console.log(chalk.white('Updated components:'));
1554
- console.log(chalk.green(' ✓ AGENTS.md - Merged with latest templates'));
1555
- console.log(chalk.green(` ✓ .rulebook - Updated to v${getRulebookVersion()}`));
1556
- console.log(chalk.white('\nWhat was updated:'));
1557
- console.log(chalk.gray(` - ${detection.languages.length} language templates`));
1558
- console.log(chalk.gray(` - ${detection.modules.filter((m) => m.detected).length} MCP modules`));
1559
- console.log(chalk.gray(' - Git workflow rules'));
1560
- console.log(chalk.gray(' - Rulebook task management'));
1561
- console.log(chalk.gray(' - Pre-commit command standardization'));
1562
- console.log(chalk.yellow('\n⚠ Review the updated AGENTS.md to ensure your custom rules are preserved'));
1563
- console.log(chalk.white('\nNext steps:'));
1564
- console.log(chalk.gray(' 1. Review AGENTS.md changes'));
1565
- console.log(chalk.gray(' 2. Test that your project still builds'));
1566
- console.log(chalk.gray(' 3. Run quality checks (lint, test, build)'));
1567
- console.log(chalk.gray(' 4. Commit the updated files\n'));
1568
- if (minimalMode && minimalArtifacts.length > 0) {
1569
- console.log(chalk.green('Essentials ensured:'));
1570
- for (const artifact of minimalArtifacts) {
1571
- console.log(chalk.gray(` - ${path.relative(cwd, artifact)}`));
1572
- }
1573
- console.log('');
1688
+ }
1689
+ catch {
1690
+ // Skip if Ralph scripts installation fails
1691
+ }
1692
+ // Ensure PLANS.md exists (create if missing, never overwrite)
1693
+ try {
1694
+ const { initPlans } = await import('../core/plans-manager.js');
1695
+ await initPlans(cwd);
1696
+ }
1697
+ catch {
1698
+ // Non-blocking
1699
+ }
1700
+ // Migrate memory directory if old structure exists
1701
+ try {
1702
+ await migrateMemoryDirectory();
1703
+ }
1704
+ catch {
1705
+ // Silently skip if migration fails
1706
+ }
1707
+ // Install plugin in Claude Code
1708
+ try {
1709
+ await setupClaudeCodePlugin();
1710
+ }
1711
+ catch {
1712
+ // Silently skip if plugin installation fails
1713
+ }
1714
+ // Clean up any accidental duplicate directories
1715
+ try {
1716
+ const fsPromises = await import('fs/promises');
1717
+ const accidentalDir = path.join(cwd, '.rulebook', '.rulebook');
1718
+ if (existsSync(accidentalDir)) {
1719
+ await fsPromises.rm(accidentalDir, { recursive: true, force: true });
1574
1720
  }
1575
1721
  }
1576
- catch (error) {
1577
- console.error(chalk.red('\n❌ Update failed:'), error);
1578
- process.exit(1);
1722
+ catch {
1723
+ // Ignore cleanup errors
1724
+ }
1725
+ // Success message
1726
+ console.log(chalk.bold.green('\n✅ Update complete!\n'));
1727
+ console.log(chalk.white('Updated components:'));
1728
+ console.log(chalk.green(' ✓ AGENTS.md - Merged with latest templates'));
1729
+ console.log(chalk.green(` ✓ .rulebook - Updated to v${getRulebookVersion()}`));
1730
+ console.log(chalk.white('\nWhat was updated:'));
1731
+ console.log(chalk.gray(` - ${detection.languages.length} language templates`));
1732
+ console.log(chalk.gray(` - ${detection.modules.filter((m) => m.detected).length} MCP modules`));
1733
+ console.log(chalk.gray(' - Git workflow rules'));
1734
+ console.log(chalk.gray(' - Rulebook task management'));
1735
+ console.log(chalk.gray(' - Pre-commit command standardization'));
1736
+ console.log(chalk.yellow('\n⚠ Review the updated AGENTS.md to ensure your custom rules are preserved'));
1737
+ console.log(chalk.white('\nNext steps:'));
1738
+ console.log(chalk.gray(' 1. Review AGENTS.md changes'));
1739
+ console.log(chalk.gray(' 2. Test that your project still builds'));
1740
+ console.log(chalk.gray(' 3. Run quality checks (lint, test, build)'));
1741
+ console.log(chalk.gray(' 4. Commit the updated files\n'));
1742
+ if (minimalMode && minimalArtifacts.length > 0) {
1743
+ console.log(chalk.green('Essentials ensured:'));
1744
+ for (const artifact of minimalArtifacts) {
1745
+ console.log(chalk.gray(` - ${path.relative(cwd, artifact)}`));
1746
+ }
1747
+ console.log('');
1579
1748
  }
1580
1749
  }
1581
1750
  // ============================================
@@ -3336,4 +3505,146 @@ export async function ralphImportIssuesCommand(options) {
3336
3505
  process.exit(1);
3337
3506
  }
3338
3507
  }
3508
+ // ============================================
3509
+ // Workspace Commands (v4.2)
3510
+ // ============================================
3511
+ export async function workspaceInitCommand() {
3512
+ const cwd = process.cwd();
3513
+ const configPath = path.join(cwd, '.rulebook-workspace.json');
3514
+ if (existsSync(configPath)) {
3515
+ console.log(chalk.yellow('Workspace already initialized at .rulebook-workspace.json'));
3516
+ return;
3517
+ }
3518
+ const spinner = ora('Detecting workspace structure...').start();
3519
+ // Try auto-discovery first
3520
+ let config = WorkspaceManager.findWorkspaceConfig(cwd);
3521
+ if (config) {
3522
+ spinner.succeed(`Detected workspace: ${config.name} (${config.projects.length} projects)`);
3523
+ console.log('\n Projects found:');
3524
+ for (const p of config.projects) {
3525
+ console.log(` - ${chalk.cyan(p.name)} → ${p.path}`);
3526
+ }
3527
+ }
3528
+ else {
3529
+ spinner.info('No workspace structure detected. Creating empty workspace config.');
3530
+ config = {
3531
+ name: path.basename(cwd),
3532
+ version: '1.0.0',
3533
+ projects: [],
3534
+ };
3535
+ }
3536
+ // Write config
3537
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
3538
+ console.log(chalk.green(`\n Created: ${configPath}`));
3539
+ // Check for legacy .mcp.json files
3540
+ const migration = await migrateLegacyMcpConfigs(cwd);
3541
+ if (migration.migratedFiles.length > 0) {
3542
+ console.log(chalk.yellow(`\n Migrated ${migration.migratedFiles.length} legacy .mcp.json files (backups at *.mcp.json.bak)`));
3543
+ }
3544
+ console.log(chalk.dim('\n Use `rulebook workspace add <path>` to add more projects'));
3545
+ console.log(chalk.dim(' Use `rulebook mcp init --workspace` to configure MCP for workspace'));
3546
+ }
3547
+ export async function workspaceAddCommand(projectPath) {
3548
+ const cwd = process.cwd();
3549
+ const configPath = path.join(cwd, '.rulebook-workspace.json');
3550
+ if (!existsSync(configPath)) {
3551
+ console.error(chalk.red('No workspace found. Run `rulebook workspace init` first.'));
3552
+ process.exit(1);
3553
+ }
3554
+ const config = JSON.parse(readFileSync(configPath, 'utf-8'));
3555
+ const resolvedPath = path.resolve(cwd, projectPath);
3556
+ const name = path.basename(resolvedPath);
3557
+ // Check for duplicates
3558
+ if (config.projects.some((p) => p.name === name)) {
3559
+ console.error(chalk.red(`Project "${name}" already exists in workspace.`));
3560
+ process.exit(1);
3561
+ }
3562
+ // Use relative path if within workspace, absolute otherwise
3563
+ const isSubpath = resolvedPath.startsWith(cwd);
3564
+ const storedPath = isSubpath ? path.relative(cwd, resolvedPath) : resolvedPath;
3565
+ const project = {
3566
+ name,
3567
+ path: storedPath.startsWith('.') ? storedPath : `./${storedPath}`,
3568
+ };
3569
+ config.projects.push(project);
3570
+ if (!config.defaultProject) {
3571
+ config.defaultProject = name;
3572
+ }
3573
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
3574
+ console.log(chalk.green(`Added project "${name}" → ${project.path}`));
3575
+ }
3576
+ export async function workspaceRemoveCommand(projectName) {
3577
+ const cwd = process.cwd();
3578
+ const configPath = path.join(cwd, '.rulebook-workspace.json');
3579
+ if (!existsSync(configPath)) {
3580
+ console.error(chalk.red('No workspace found. Run `rulebook workspace init` first.'));
3581
+ process.exit(1);
3582
+ }
3583
+ const config = JSON.parse(readFileSync(configPath, 'utf-8'));
3584
+ const idx = config.projects.findIndex((p) => p.name === projectName);
3585
+ if (idx === -1) {
3586
+ console.error(chalk.red(`Project "${projectName}" not found in workspace.`));
3587
+ process.exit(1);
3588
+ }
3589
+ config.projects.splice(idx, 1);
3590
+ if (config.defaultProject === projectName) {
3591
+ config.defaultProject = config.projects[0]?.name;
3592
+ }
3593
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n');
3594
+ console.log(chalk.green(`Removed project "${projectName}" from workspace.`));
3595
+ }
3596
+ export async function workspaceListCommand() {
3597
+ const cwd = process.cwd();
3598
+ const config = WorkspaceManager.findWorkspaceConfig(cwd);
3599
+ if (!config) {
3600
+ console.log(chalk.yellow('No workspace found. Run `rulebook workspace init` to create one.'));
3601
+ return;
3602
+ }
3603
+ console.log(chalk.bold(`\nWorkspace: ${config.name}`));
3604
+ console.log(chalk.dim(` Version: ${config.version}`));
3605
+ if (config.defaultProject) {
3606
+ console.log(chalk.dim(` Default: ${config.defaultProject}`));
3607
+ }
3608
+ console.log();
3609
+ for (const p of config.projects) {
3610
+ const isDefault = p.name === config.defaultProject;
3611
+ const marker = isDefault ? chalk.green(' (default)') : '';
3612
+ const disabled = p.enabled === false ? chalk.red(' [disabled]') : '';
3613
+ console.log(` ${chalk.cyan(p.name)}${marker}${disabled}`);
3614
+ console.log(` ${chalk.dim(p.path)}`);
3615
+ }
3616
+ console.log(chalk.dim(`\n ${config.projects.length} project(s) total`));
3617
+ }
3618
+ export async function workspaceStatusCommand() {
3619
+ const cwd = process.cwd();
3620
+ const config = WorkspaceManager.findWorkspaceConfig(cwd);
3621
+ if (!config) {
3622
+ console.log(chalk.yellow('No workspace found. Run `rulebook workspace init` to create one.'));
3623
+ return;
3624
+ }
3625
+ const manager = new WorkspaceManager(config, cwd);
3626
+ const spinner = ora('Checking workspace status...').start();
3627
+ try {
3628
+ const status = await manager.getStatus();
3629
+ spinner.stop();
3630
+ console.log(chalk.bold(`\nWorkspace: ${status.name}`));
3631
+ console.log(` Projects: ${status.totalProjects} | Active workers: ${status.activeWorkers}`);
3632
+ console.log();
3633
+ for (const p of status.projects) {
3634
+ const configBadge = p.hasRulebookConfig ? chalk.green('.rulebook') : chalk.dim('no config');
3635
+ const memBadge = p.memoryEnabled ? chalk.blue('memory') : '';
3636
+ const taskBadge = p.taskCount > 0 ? chalk.yellow(`${p.taskCount} tasks`) : '';
3637
+ const badges = [configBadge, memBadge, taskBadge].filter(Boolean).join(' ');
3638
+ console.log(` ${chalk.cyan(p.name)} ${badges}`);
3639
+ console.log(` ${chalk.dim(p.path)}`);
3640
+ }
3641
+ console.log();
3642
+ }
3643
+ catch (error) {
3644
+ spinner.fail(`Failed: ${String(error)}`);
3645
+ }
3646
+ finally {
3647
+ await manager.shutdownAll();
3648
+ }
3649
+ }
3339
3650
  //# sourceMappingURL=commands.js.map