@hivehub/rulebook 4.1.0 → 4.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) 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 +137 -1
  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 +727 -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.map +1 -1
  31. package/dist/core/indexer/background-indexer.js +26 -5
  32. package/dist/core/indexer/background-indexer.js.map +1 -1
  33. package/dist/core/indexer/file-parser.d.ts.map +1 -1
  34. package/dist/core/indexer/file-parser.js +1 -1
  35. package/dist/core/indexer/file-parser.js.map +1 -1
  36. package/dist/core/indexer/indexer-types.d.ts.map +1 -1
  37. package/dist/core/workspace/legacy-migrator.d.ts +29 -0
  38. package/dist/core/workspace/legacy-migrator.d.ts.map +1 -0
  39. package/dist/core/workspace/legacy-migrator.js +142 -0
  40. package/dist/core/workspace/legacy-migrator.js.map +1 -0
  41. package/dist/core/workspace/project-worker.d.ts +49 -0
  42. package/dist/core/workspace/project-worker.d.ts.map +1 -0
  43. package/dist/core/workspace/project-worker.js +108 -0
  44. package/dist/core/workspace/project-worker.js.map +1 -0
  45. package/dist/core/workspace/workspace-manager.d.ts +90 -0
  46. package/dist/core/workspace/workspace-manager.d.ts.map +1 -0
  47. package/dist/core/workspace/workspace-manager.js +347 -0
  48. package/dist/core/workspace/workspace-manager.js.map +1 -0
  49. package/dist/core/workspace/workspace-types.d.ts +37 -0
  50. package/dist/core/workspace/workspace-types.d.ts.map +1 -0
  51. package/dist/core/workspace/workspace-types.js +8 -0
  52. package/dist/core/workspace/workspace-types.js.map +1 -0
  53. package/dist/index.js +43 -7
  54. package/dist/index.js.map +1 -1
  55. package/dist/mcp/rulebook-server.d.ts.map +1 -1
  56. package/dist/mcp/rulebook-server.js +367 -100
  57. package/dist/mcp/rulebook-server.js.map +1 -1
  58. package/dist/memory/memory-manager.js +2 -2
  59. package/dist/memory/memory-manager.js.map +1 -1
  60. package/dist/memory/memory-search.js.map +1 -1
  61. package/dist/memory/memory-store.d.ts.map +1 -1
  62. package/dist/memory/memory-store.js +1 -1
  63. package/dist/memory/memory-store.js.map +1 -1
  64. package/dist/types.d.ts +1 -0
  65. package/dist/types.d.ts.map +1 -1
  66. package/package.json +22 -21
  67. package/templates/agents/implementer.md +35 -35
  68. package/templates/agents/researcher.md +34 -34
  69. package/templates/agents/team-lead.md +34 -34
  70. package/templates/agents/tester.md +42 -42
  71. package/templates/ci/rulebook-review.yml +26 -26
  72. package/templates/cli/AIDER.md +49 -49
  73. package/templates/cli/AMAZON_Q.md +25 -25
  74. package/templates/cli/AUGGIE.md +32 -32
  75. package/templates/cli/CLAUDE.md +117 -117
  76. package/templates/cli/CLINE.md +99 -99
  77. package/templates/cli/CODEBUDDY.md +20 -20
  78. package/templates/cli/CODEIUM.md +20 -20
  79. package/templates/cli/CODEX.md +21 -21
  80. package/templates/cli/CONTINUE.md +34 -34
  81. package/templates/cli/CURSOR_CLI.md +62 -62
  82. package/templates/cli/FACTORY.md +18 -18
  83. package/templates/cli/GEMINI.md +35 -35
  84. package/templates/cli/KILOCODE.md +18 -18
  85. package/templates/cli/OPENCODE.md +18 -18
  86. package/templates/cli/_GENERIC_TEMPLATE.md +29 -29
  87. package/templates/commands/rulebook-memory-save.md +48 -48
  88. package/templates/commands/rulebook-memory-search.md +47 -47
  89. package/templates/commands/rulebook-task-apply.md +67 -67
  90. package/templates/commands/rulebook-task-archive.md +94 -94
  91. package/templates/commands/rulebook-task-create.md +93 -93
  92. package/templates/commands/rulebook-task-list.md +42 -42
  93. package/templates/commands/rulebook-task-show.md +52 -52
  94. package/templates/commands/rulebook-task-validate.md +53 -53
  95. package/templates/core/AGENTS_LEAN.md +25 -25
  96. package/templates/core/AGENTS_OVERRIDE.md +16 -16
  97. package/templates/core/AGENT_AUTOMATION.md +288 -288
  98. package/templates/core/DAG.md +304 -304
  99. package/templates/core/DOCUMENTATION_RULES.md +36 -36
  100. package/templates/core/MULTI_AGENT.md +74 -74
  101. package/templates/core/PLANS.md +28 -28
  102. package/templates/core/QUALITY_ENFORCEMENT.md +68 -68
  103. package/templates/core/RALPH.md +471 -471
  104. package/templates/core/RULEBOOK.md +1935 -1935
  105. package/templates/core/WORKSPACE.md +69 -0
  106. package/templates/frameworks/ANGULAR.md +36 -36
  107. package/templates/frameworks/DJANGO.md +83 -83
  108. package/templates/frameworks/ELECTRON.md +147 -147
  109. package/templates/frameworks/FLASK.md +38 -38
  110. package/templates/frameworks/FLUTTER.md +55 -55
  111. package/templates/frameworks/JQUERY.md +32 -32
  112. package/templates/frameworks/LARAVEL.md +38 -38
  113. package/templates/frameworks/NESTJS.md +43 -43
  114. package/templates/frameworks/NEXTJS.md +127 -127
  115. package/templates/frameworks/NUXT.md +40 -40
  116. package/templates/frameworks/RAILS.md +66 -66
  117. package/templates/frameworks/REACT.md +38 -38
  118. package/templates/frameworks/REACT_NATIVE.md +47 -47
  119. package/templates/frameworks/SPRING.md +39 -39
  120. package/templates/frameworks/SYMFONY.md +36 -36
  121. package/templates/frameworks/VUE.md +36 -36
  122. package/templates/frameworks/ZEND.md +35 -35
  123. package/templates/git/CI_CD_PATTERNS.md +661 -661
  124. package/templates/git/GITHUB_ACTIONS.md +728 -728
  125. package/templates/git/GITLAB_CI.md +730 -730
  126. package/templates/git/GIT_WORKFLOW.md +1157 -1157
  127. package/templates/git/SECRETS_MANAGEMENT.md +585 -585
  128. package/templates/hooks/COMMIT_MSG.md +530 -530
  129. package/templates/hooks/POST_CHECKOUT.md +546 -546
  130. package/templates/hooks/PREPARE_COMMIT_MSG.md +619 -619
  131. package/templates/hooks/PRE_COMMIT.md +414 -414
  132. package/templates/hooks/PRE_PUSH.md +601 -601
  133. package/templates/ides/CONTINUE_RULES.md +16 -16
  134. package/templates/ides/COPILOT.md +37 -37
  135. package/templates/ides/COPILOT_INSTRUCTIONS.md +23 -23
  136. package/templates/ides/CURSOR.md +43 -43
  137. package/templates/ides/GEMINI_RULES.md +17 -17
  138. package/templates/ides/JETBRAINS_AI.md +35 -35
  139. package/templates/ides/REPLIT.md +36 -36
  140. package/templates/ides/TABNINE.md +29 -29
  141. package/templates/ides/VSCODE.md +40 -40
  142. package/templates/ides/WINDSURF.md +36 -36
  143. package/templates/ides/WINDSURF_RULES.md +14 -14
  144. package/templates/ides/ZED.md +32 -32
  145. package/templates/ides/cursor-mdc/go.mdc +24 -24
  146. package/templates/ides/cursor-mdc/python.mdc +24 -24
  147. package/templates/ides/cursor-mdc/quality.mdc +25 -25
  148. package/templates/ides/cursor-mdc/ralph.mdc +39 -39
  149. package/templates/ides/cursor-mdc/rulebook.mdc +38 -38
  150. package/templates/ides/cursor-mdc/rust.mdc +24 -24
  151. package/templates/ides/cursor-mdc/typescript.mdc +25 -25
  152. package/templates/languages/C.md +333 -333
  153. package/templates/languages/CPP.md +743 -743
  154. package/templates/languages/CSHARP.md +417 -417
  155. package/templates/languages/ELIXIR.md +454 -454
  156. package/templates/languages/ERLANG.md +361 -361
  157. package/templates/languages/GO.md +645 -645
  158. package/templates/languages/HASKELL.md +177 -177
  159. package/templates/languages/JAVA.md +607 -607
  160. package/templates/languages/JAVASCRIPT.md +631 -631
  161. package/templates/languages/JULIA.md +97 -97
  162. package/templates/languages/KOTLIN.md +511 -511
  163. package/templates/languages/LISP.md +100 -100
  164. package/templates/languages/LUA.md +74 -74
  165. package/templates/languages/OBJECTIVEC.md +90 -90
  166. package/templates/languages/PHP.md +416 -416
  167. package/templates/languages/PYTHON.md +682 -682
  168. package/templates/languages/RUBY.md +421 -421
  169. package/templates/languages/RUST.md +477 -477
  170. package/templates/languages/SAS.md +73 -73
  171. package/templates/languages/SCALA.md +348 -348
  172. package/templates/languages/SOLIDITY.md +580 -580
  173. package/templates/languages/SQL.md +137 -137
  174. package/templates/languages/SWIFT.md +466 -466
  175. package/templates/languages/TYPESCRIPT.md +591 -591
  176. package/templates/languages/ZIG.md +265 -265
  177. package/templates/modules/ATLASSIAN.md +255 -255
  178. package/templates/modules/CONTEXT7.md +54 -54
  179. package/templates/modules/FIGMA.md +267 -267
  180. package/templates/modules/GITHUB_MCP.md +64 -64
  181. package/templates/modules/GRAFANA.md +328 -328
  182. package/templates/modules/MEMORY.md +126 -126
  183. package/templates/modules/NOTION.md +247 -247
  184. package/templates/modules/PLAYWRIGHT.md +90 -90
  185. package/templates/modules/RULEBOOK_MCP.md +156 -156
  186. package/templates/modules/SERENA.md +337 -337
  187. package/templates/modules/SUPABASE.md +223 -223
  188. package/templates/modules/SYNAP.md +69 -69
  189. package/templates/modules/VECTORIZER.md +63 -63
  190. package/templates/modules/sequential-thinking.md +42 -42
  191. package/templates/ralph/ralph-history.bat +4 -4
  192. package/templates/ralph/ralph-history.sh +5 -5
  193. package/templates/ralph/ralph-init.bat +5 -5
  194. package/templates/ralph/ralph-init.sh +5 -5
  195. package/templates/ralph/ralph-pause.bat +5 -5
  196. package/templates/ralph/ralph-pause.sh +5 -5
  197. package/templates/ralph/ralph-run.bat +5 -5
  198. package/templates/ralph/ralph-run.sh +5 -5
  199. package/templates/ralph/ralph-status.bat +4 -4
  200. package/templates/ralph/ralph-status.sh +5 -5
  201. package/templates/services/AZURE_BLOB.md +184 -184
  202. package/templates/services/CASSANDRA.md +239 -239
  203. package/templates/services/DATADOG.md +26 -26
  204. package/templates/services/DOCKER.md +124 -124
  205. package/templates/services/DOCKER_COMPOSE.md +168 -168
  206. package/templates/services/DYNAMODB.md +308 -308
  207. package/templates/services/ELASTICSEARCH.md +347 -347
  208. package/templates/services/GCS.md +178 -178
  209. package/templates/services/HELM.md +194 -194
  210. package/templates/services/INFLUXDB.md +265 -265
  211. package/templates/services/KAFKA.md +341 -341
  212. package/templates/services/KUBERNETES.md +208 -208
  213. package/templates/services/MARIADB.md +183 -183
  214. package/templates/services/MEMCACHED.md +242 -242
  215. package/templates/services/MINIO.md +201 -201
  216. package/templates/services/MONGODB.md +268 -268
  217. package/templates/services/MYSQL.md +358 -358
  218. package/templates/services/NEO4J.md +247 -247
  219. package/templates/services/OPENTELEMETRY.md +25 -25
  220. package/templates/services/ORACLE.md +290 -290
  221. package/templates/services/PINO.md +24 -24
  222. package/templates/services/POSTGRESQL.md +326 -326
  223. package/templates/services/PROMETHEUS.md +33 -33
  224. package/templates/services/RABBITMQ.md +286 -286
  225. package/templates/services/REDIS.md +292 -292
  226. package/templates/services/S3.md +298 -298
  227. package/templates/services/SENTRY.md +23 -23
  228. package/templates/services/SQLITE.md +294 -294
  229. package/templates/services/SQLSERVER.md +294 -294
  230. package/templates/services/WINSTON.md +30 -30
  231. package/templates/skills/cli/aider/SKILL.md +59 -59
  232. package/templates/skills/cli/amazon-q/SKILL.md +35 -35
  233. package/templates/skills/cli/auggie/SKILL.md +42 -42
  234. package/templates/skills/cli/claude/SKILL.md +42 -42
  235. package/templates/skills/cli/cline/SKILL.md +42 -42
  236. package/templates/skills/cli/codebuddy/SKILL.md +30 -30
  237. package/templates/skills/cli/codeium/SKILL.md +30 -30
  238. package/templates/skills/cli/codex/SKILL.md +31 -31
  239. package/templates/skills/cli/continue/SKILL.md +44 -44
  240. package/templates/skills/cli/cursor-cli/SKILL.md +38 -38
  241. package/templates/skills/cli/factory/SKILL.md +28 -28
  242. package/templates/skills/cli/gemini/SKILL.md +45 -45
  243. package/templates/skills/cli/kilocode/SKILL.md +28 -28
  244. package/templates/skills/cli/opencode/SKILL.md +28 -28
  245. package/templates/skills/core/agent-automation/SKILL.md +194 -194
  246. package/templates/skills/core/dag/SKILL.md +314 -314
  247. package/templates/skills/core/documentation-rules/SKILL.md +46 -46
  248. package/templates/skills/core/quality-enforcement/SKILL.md +78 -78
  249. package/templates/skills/core/rulebook/SKILL.md +176 -176
  250. package/templates/skills/frameworks/angular/SKILL.md +46 -46
  251. package/templates/skills/frameworks/django/SKILL.md +93 -93
  252. package/templates/skills/frameworks/electron/SKILL.md +157 -157
  253. package/templates/skills/frameworks/flask/SKILL.md +48 -48
  254. package/templates/skills/frameworks/flutter/SKILL.md +65 -65
  255. package/templates/skills/frameworks/jquery/SKILL.md +42 -42
  256. package/templates/skills/frameworks/laravel/SKILL.md +48 -48
  257. package/templates/skills/frameworks/nestjs/SKILL.md +53 -53
  258. package/templates/skills/frameworks/nextjs/SKILL.md +137 -137
  259. package/templates/skills/frameworks/nuxt/SKILL.md +50 -50
  260. package/templates/skills/frameworks/rails/SKILL.md +76 -76
  261. package/templates/skills/frameworks/react/SKILL.md +48 -48
  262. package/templates/skills/frameworks/react-native/SKILL.md +57 -57
  263. package/templates/skills/frameworks/spring/SKILL.md +49 -49
  264. package/templates/skills/frameworks/symfony/SKILL.md +46 -46
  265. package/templates/skills/frameworks/vue/SKILL.md +46 -46
  266. package/templates/skills/frameworks/zend/SKILL.md +45 -45
  267. package/templates/skills/ides/copilot/SKILL.md +47 -47
  268. package/templates/skills/ides/cursor/SKILL.md +53 -53
  269. package/templates/skills/ides/jetbrains-ai/SKILL.md +45 -45
  270. package/templates/skills/ides/replit/SKILL.md +46 -46
  271. package/templates/skills/ides/tabnine/SKILL.md +39 -39
  272. package/templates/skills/ides/vscode/SKILL.md +50 -50
  273. package/templates/skills/ides/windsurf/SKILL.md +46 -46
  274. package/templates/skills/ides/zed/SKILL.md +42 -42
  275. package/templates/skills/languages/c/SKILL.md +343 -343
  276. package/templates/skills/languages/cpp/SKILL.md +753 -753
  277. package/templates/skills/languages/csharp/SKILL.md +427 -427
  278. package/templates/skills/languages/elixir/SKILL.md +464 -464
  279. package/templates/skills/languages/erlang/SKILL.md +371 -371
  280. package/templates/skills/languages/go/SKILL.md +655 -655
  281. package/templates/skills/languages/haskell/SKILL.md +187 -187
  282. package/templates/skills/languages/java/SKILL.md +617 -617
  283. package/templates/skills/languages/javascript/SKILL.md +641 -641
  284. package/templates/skills/languages/julia/SKILL.md +107 -107
  285. package/templates/skills/languages/kotlin/SKILL.md +521 -521
  286. package/templates/skills/languages/lisp/SKILL.md +110 -110
  287. package/templates/skills/languages/lua/SKILL.md +84 -84
  288. package/templates/skills/languages/objectivec/SKILL.md +100 -100
  289. package/templates/skills/languages/php/SKILL.md +426 -426
  290. package/templates/skills/languages/python/SKILL.md +692 -692
  291. package/templates/skills/languages/ruby/SKILL.md +431 -431
  292. package/templates/skills/languages/rust/SKILL.md +487 -487
  293. package/templates/skills/languages/sas/SKILL.md +83 -83
  294. package/templates/skills/languages/scala/SKILL.md +358 -358
  295. package/templates/skills/languages/solidity/SKILL.md +590 -590
  296. package/templates/skills/languages/sql/SKILL.md +147 -147
  297. package/templates/skills/languages/swift/SKILL.md +476 -476
  298. package/templates/skills/languages/typescript/SKILL.md +302 -302
  299. package/templates/skills/languages/zig/SKILL.md +275 -275
  300. package/templates/skills/modules/atlassian/SKILL.md +265 -265
  301. package/templates/skills/modules/context7/SKILL.md +64 -64
  302. package/templates/skills/modules/figma/SKILL.md +277 -277
  303. package/templates/skills/modules/github-mcp/SKILL.md +74 -74
  304. package/templates/skills/modules/grafana/SKILL.md +338 -338
  305. package/templates/skills/modules/memory/SKILL.md +73 -73
  306. package/templates/skills/modules/notion/SKILL.md +257 -257
  307. package/templates/skills/modules/playwright/SKILL.md +100 -100
  308. package/templates/skills/modules/rulebook-mcp/SKILL.md +166 -166
  309. package/templates/skills/modules/serena/SKILL.md +347 -347
  310. package/templates/skills/modules/supabase/SKILL.md +233 -233
  311. package/templates/skills/modules/synap/SKILL.md +79 -79
  312. package/templates/skills/modules/vectorizer/SKILL.md +73 -73
  313. package/templates/skills/services/azure-blob/SKILL.md +194 -194
  314. package/templates/skills/services/cassandra/SKILL.md +249 -249
  315. package/templates/skills/services/dynamodb/SKILL.md +318 -318
  316. package/templates/skills/services/elasticsearch/SKILL.md +357 -357
  317. package/templates/skills/services/gcs/SKILL.md +188 -188
  318. package/templates/skills/services/influxdb/SKILL.md +275 -275
  319. package/templates/skills/services/kafka/SKILL.md +351 -351
  320. package/templates/skills/services/mariadb/SKILL.md +193 -193
  321. package/templates/skills/services/memcached/SKILL.md +252 -252
  322. package/templates/skills/services/minio/SKILL.md +211 -211
  323. package/templates/skills/services/mongodb/SKILL.md +278 -278
  324. package/templates/skills/services/mysql/SKILL.md +368 -368
  325. package/templates/skills/services/neo4j/SKILL.md +257 -257
  326. package/templates/skills/services/oracle/SKILL.md +300 -300
  327. package/templates/skills/services/postgresql/SKILL.md +336 -336
  328. package/templates/skills/services/rabbitmq/SKILL.md +296 -296
  329. package/templates/skills/services/redis/SKILL.md +302 -302
  330. package/templates/skills/services/s3/SKILL.md +308 -308
  331. package/templates/skills/services/sqlite/SKILL.md +304 -304
  332. package/templates/skills/services/sqlserver/SKILL.md +304 -304
  333. package/templates/skills/workflows/ralph/SKILL.md +309 -309
  334. package/templates/skills/workflows/ralph/install.sh +87 -87
  335. package/templates/skills/workflows/ralph/manifest.json +158 -158
@@ -8,6 +8,7 @@ import { ConfigManager } from '../core/config-manager.js';
8
8
  import { BackgroundIndexer } from '../core/indexer/background-indexer.js';
9
9
  import { SkillsManager, getDefaultTemplatesPath } from '../core/skills-manager.js';
10
10
  import { TaskManager } from '../core/task-manager.js';
11
+ import { WorkspaceManager } from '../core/workspace/workspace-manager.js';
11
12
  // Find .rulebook file/directory by walking up directories
12
13
  export function findRulebookConfig(startDir) {
13
14
  let current = resolve(startDir);
@@ -55,23 +56,94 @@ function loadConfig() {
55
56
  return { projectRoot, tasksDir, archiveDir };
56
57
  }
57
58
  export async function startRulebookMcpServer() {
58
- const config = loadConfig();
59
- const taskManager = new TaskManager(config.projectRoot, '.rulebook');
60
- const skillsManager = new SkillsManager(getDefaultTemplatesPath(), config.projectRoot);
61
- const configManager = new ConfigManager(config.projectRoot);
59
+ // --- Workspace vs Single-Project Mode ---
60
+ const isWorkspaceMode = process.argv.includes('--workspace');
61
+ let workspaceManager = null;
62
+ // Default managers (single-project mode OR default workspace project)
63
+ let taskManager;
64
+ let skillsManager;
65
+ let configManager;
66
+ let projectRoot;
67
+ if (isWorkspaceMode) {
68
+ const projectRootFlagIndex = process.argv.indexOf('--project-root');
69
+ const startDir = projectRootFlagIndex !== -1 && process.argv[projectRootFlagIndex + 1]
70
+ ? process.argv[projectRootFlagIndex + 1]
71
+ : process.cwd();
72
+ const wsConfig = WorkspaceManager.findWorkspaceConfig(startDir);
73
+ if (!wsConfig) {
74
+ console.error('[rulebook-mcp] No workspace config found. Run `rulebook workspace init` to create .rulebook/workspace.json.');
75
+ process.exit(1);
76
+ }
77
+ workspaceManager = new WorkspaceManager(wsConfig, startDir);
78
+ workspaceManager.startIdleChecker();
79
+ projectRoot = startDir;
80
+ // Initialize default project so existing tools work without projectId
81
+ const defaultId = workspaceManager.getDefaultProjectId();
82
+ if (defaultId) {
83
+ try {
84
+ const defaultWorker = await workspaceManager.getWorker(defaultId);
85
+ taskManager = defaultWorker.getTaskManager();
86
+ skillsManager = defaultWorker.getSkillsManager();
87
+ configManager = defaultWorker.getConfigManager();
88
+ }
89
+ catch (e) {
90
+ console.error(`[rulebook-mcp] Failed to init default project "${defaultId}":`, e);
91
+ process.exit(1);
92
+ }
93
+ }
94
+ console.error(`[rulebook-mcp] Workspace mode: ${wsConfig.name} (${wsConfig.projects.length} projects, default: ${defaultId})`);
95
+ }
96
+ else {
97
+ const singleConfig = loadConfig();
98
+ projectRoot = singleConfig.projectRoot;
99
+ taskManager = new TaskManager(projectRoot, '.rulebook');
100
+ skillsManager = new SkillsManager(getDefaultTemplatesPath(), projectRoot);
101
+ configManager = new ConfigManager(projectRoot);
102
+ }
103
+ // --- Manager Resolution Helpers (workspace-aware) ---
104
+ async function getTaskMgr(projectId) {
105
+ if (!projectId || !workspaceManager)
106
+ return taskManager;
107
+ const w = await workspaceManager.getWorker(projectId);
108
+ return w.getTaskManager();
109
+ }
110
+ async function getConfigMgr(projectId) {
111
+ if (!projectId || !workspaceManager)
112
+ return configManager;
113
+ const w = await workspaceManager.getWorker(projectId);
114
+ return w.getConfigManager();
115
+ }
116
+ async function getSkillsMgr(projectId) {
117
+ if (!projectId || !workspaceManager)
118
+ return skillsManager;
119
+ const w = await workspaceManager.getWorker(projectId);
120
+ return w.getSkillsManager();
121
+ }
122
+ async function getMemMgr(projectId) {
123
+ if (workspaceManager) {
124
+ const pid = projectId ?? workspaceManager.getDefaultProjectId();
125
+ const w = await workspaceManager.getWorker(pid);
126
+ return w.getMemoryManager();
127
+ }
128
+ return memoryManager;
129
+ }
62
130
  const server = new McpServer({
63
131
  name: 'rulebook-task-management',
64
- version: '4.1.0',
132
+ version: '4.2.1',
65
133
  });
134
+ // Zod schema reused across tools for workspace project targeting
135
+ const projectIdSchema = z.string().optional().describe('Project ID (workspace mode only, defaults to default project)');
66
136
  // Register tool: rulebook_task_create
67
137
  server.registerTool('rulebook_task_create', {
68
138
  title: 'Create Rulebook Task',
69
139
  description: 'Create a new Rulebook task',
70
140
  inputSchema: {
71
141
  taskId: z.string().describe('Task ID in kebab-case'),
142
+ projectId: projectIdSchema,
72
143
  },
73
144
  }, async (args) => {
74
- await taskManager.createTask(args.taskId);
145
+ const tm = await getTaskMgr(args.projectId);
146
+ await tm.createTask(args.taskId);
75
147
  const resultText = JSON.stringify({
76
148
  success: true,
77
149
  taskId: args.taskId,
@@ -90,9 +162,11 @@ export async function startRulebookMcpServer() {
90
162
  .enum(['pending', 'in-progress', 'completed', 'blocked'])
91
163
  .optional()
92
164
  .describe('Filter by status'),
165
+ projectId: projectIdSchema,
93
166
  },
94
167
  }, async (args) => {
95
- const tasks = await taskManager.listTasks(args.includeArchived || false);
168
+ const tm = await getTaskMgr(args.projectId);
169
+ const tasks = await tm.listTasks(args.includeArchived || false);
96
170
  let filtered = tasks;
97
171
  if (args.status) {
98
172
  filtered = tasks.filter((t) => t.status === args.status);
@@ -121,9 +195,11 @@ export async function startRulebookMcpServer() {
121
195
  description: 'Show task details',
122
196
  inputSchema: {
123
197
  taskId: z.string().describe('Task ID to show'),
198
+ projectId: projectIdSchema,
124
199
  },
125
200
  }, async (args) => {
126
- const task = await taskManager.showTask(args.taskId);
201
+ const tm = await getTaskMgr(args.projectId);
202
+ const task = await tm.showTask(args.taskId);
127
203
  return {
128
204
  content: [
129
205
  {
@@ -158,10 +234,12 @@ export async function startRulebookMcpServer() {
158
234
  .enum(['pending', 'in-progress', 'completed', 'blocked'])
159
235
  .optional()
160
236
  .describe('New status'),
237
+ projectId: projectIdSchema,
161
238
  },
162
239
  }, async (args) => {
240
+ const tm = await getTaskMgr(args.projectId);
163
241
  if (args.status) {
164
- await taskManager.updateTaskStatus(args.taskId, args.status);
242
+ await tm.updateTaskStatus(args.taskId, args.status);
165
243
  }
166
244
  const resultText = JSON.stringify({
167
245
  success: true,
@@ -177,9 +255,11 @@ export async function startRulebookMcpServer() {
177
255
  description: 'Validate task format',
178
256
  inputSchema: {
179
257
  taskId: z.string().describe('Task ID to validate'),
258
+ projectId: projectIdSchema,
180
259
  },
181
260
  }, async (args) => {
182
- const validation = await taskManager.validateTask(args.taskId);
261
+ const tm = await getTaskMgr(args.projectId);
262
+ const validation = await tm.validateTask(args.taskId);
183
263
  return {
184
264
  content: [
185
265
  {
@@ -200,9 +280,11 @@ export async function startRulebookMcpServer() {
200
280
  inputSchema: {
201
281
  taskId: z.string().describe('Task ID to archive'),
202
282
  skipValidation: z.boolean().optional().describe('Skip validation before archiving'),
283
+ projectId: projectIdSchema,
203
284
  },
204
285
  }, async (args) => {
205
- await taskManager.archiveTask(args.taskId, args.skipValidation || false);
286
+ const tm = await getTaskMgr(args.projectId);
287
+ await tm.archiveTask(args.taskId, args.skipValidation || false);
206
288
  const resultText = JSON.stringify({
207
289
  success: true,
208
290
  taskId: args.taskId,
@@ -217,9 +299,11 @@ export async function startRulebookMcpServer() {
217
299
  description: 'Delete a task permanently',
218
300
  inputSchema: {
219
301
  taskId: z.string().describe('Task ID to delete'),
302
+ projectId: projectIdSchema,
220
303
  },
221
304
  }, async (args) => {
222
- await taskManager.deleteTask(args.taskId);
305
+ const tm = await getTaskMgr(args.projectId);
306
+ await tm.deleteTask(args.taskId);
223
307
  const resultText = JSON.stringify({
224
308
  success: true,
225
309
  taskId: args.taskId,
@@ -252,19 +336,21 @@ export async function startRulebookMcpServer() {
252
336
  .optional()
253
337
  .describe('Filter by category'),
254
338
  enabledOnly: z.boolean().optional().describe('Show only enabled skills'),
339
+ projectId: projectIdSchema,
255
340
  },
256
341
  }, async (args) => {
257
342
  try {
343
+ const sm = await getSkillsMgr(args.projectId);
344
+ const cm = await getConfigMgr(args.projectId);
258
345
  let skills;
259
346
  if (args.category) {
260
- skills = await skillsManager.getSkillsByCategory(args.category);
347
+ skills = await sm.getSkillsByCategory(args.category);
261
348
  }
262
349
  else {
263
- skills = await skillsManager.getSkills();
350
+ skills = await sm.getSkills();
264
351
  }
265
- // Check enabled status from config
266
- const rulebookConfig = await configManager.loadConfig();
267
- const enabledIds = new Set(rulebookConfig.skills?.enabled || []);
352
+ const rbConfig = await cm.loadConfig();
353
+ const enabledIds = new Set(rbConfig.skills?.enabled || []);
268
354
  let filteredSkills = skills.map((s) => ({
269
355
  id: s.id,
270
356
  name: s.metadata.name,
@@ -311,10 +397,13 @@ export async function startRulebookMcpServer() {
311
397
  description: 'Show detailed information about a specific skill',
312
398
  inputSchema: {
313
399
  skillId: z.string().describe('Skill ID (e.g., languages/typescript)'),
400
+ projectId: projectIdSchema,
314
401
  },
315
402
  }, async (args) => {
316
403
  try {
317
- const skill = await skillsManager.getSkillById(args.skillId);
404
+ const sm = await getSkillsMgr(args.projectId);
405
+ const cm = await getConfigMgr(args.projectId);
406
+ const skill = await sm.getSkillById(args.skillId);
318
407
  if (!skill) {
319
408
  return {
320
409
  content: [
@@ -329,8 +418,8 @@ export async function startRulebookMcpServer() {
329
418
  ],
330
419
  };
331
420
  }
332
- const rulebookConfig = await configManager.loadConfig();
333
- const enabled = rulebookConfig.skills?.enabled?.includes(args.skillId) || false;
421
+ const rbConfig = await cm.loadConfig();
422
+ const enabled = rbConfig.skills?.enabled?.includes(args.skillId) || false;
334
423
  return {
335
424
  content: [
336
425
  {
@@ -375,14 +464,16 @@ export async function startRulebookMcpServer() {
375
464
  description: 'Enable a skill in the project configuration',
376
465
  inputSchema: {
377
466
  skillId: z.string().describe('Skill ID to enable (e.g., languages/typescript)'),
467
+ projectId: projectIdSchema,
378
468
  },
379
469
  }, async (args) => {
380
470
  try {
381
- let rulebookConfig = await configManager.loadConfig();
382
- rulebookConfig = await skillsManager.enableSkill(args.skillId, rulebookConfig);
383
- await configManager.saveConfig(rulebookConfig);
384
- // Validate to check for conflicts
385
- const validation = await skillsManager.validateSkills(rulebookConfig);
471
+ const sm = await getSkillsMgr(args.projectId);
472
+ const cm = await getConfigMgr(args.projectId);
473
+ let rbConfig = await cm.loadConfig();
474
+ rbConfig = await sm.enableSkill(args.skillId, rbConfig);
475
+ await cm.saveConfig(rbConfig);
476
+ const validation = await sm.validateSkills(rbConfig);
386
477
  const resultText = JSON.stringify({
387
478
  success: true,
388
479
  skillId: args.skillId,
@@ -413,12 +504,14 @@ export async function startRulebookMcpServer() {
413
504
  description: 'Disable a skill in the project configuration',
414
505
  inputSchema: {
415
506
  skillId: z.string().describe('Skill ID to disable (e.g., languages/typescript)'),
507
+ projectId: projectIdSchema,
416
508
  },
417
509
  }, async (args) => {
418
510
  try {
419
- let rulebookConfig = await configManager.loadConfig();
420
- // Check if skill is enabled
421
- if (!rulebookConfig.skills?.enabled?.includes(args.skillId)) {
511
+ const sm = await getSkillsMgr(args.projectId);
512
+ const cm = await getConfigMgr(args.projectId);
513
+ let rbConfig = await cm.loadConfig();
514
+ if (!rbConfig.skills?.enabled?.includes(args.skillId)) {
422
515
  return {
423
516
  content: [
424
517
  {
@@ -431,8 +524,8 @@ export async function startRulebookMcpServer() {
431
524
  ],
432
525
  };
433
526
  }
434
- rulebookConfig = await skillsManager.disableSkill(args.skillId, rulebookConfig);
435
- await configManager.saveConfig(rulebookConfig);
527
+ rbConfig = await sm.disableSkill(args.skillId, rbConfig);
528
+ await cm.saveConfig(rbConfig);
436
529
  const resultText = JSON.stringify({
437
530
  success: true,
438
531
  skillId: args.skillId,
@@ -461,12 +554,15 @@ export async function startRulebookMcpServer() {
461
554
  description: 'Search for skills by name, description, or tags',
462
555
  inputSchema: {
463
556
  query: z.string().describe('Search query'),
557
+ projectId: projectIdSchema,
464
558
  },
465
559
  }, async (args) => {
466
560
  try {
467
- const skills = await skillsManager.searchSkills(args.query);
468
- const rulebookConfig = await configManager.loadConfig();
469
- const enabledIds = new Set(rulebookConfig.skills?.enabled || []);
561
+ const sm = await getSkillsMgr(args.projectId);
562
+ const cm = await getConfigMgr(args.projectId);
563
+ const skills = await sm.searchSkills(args.query);
564
+ const rbConfig = await cm.loadConfig();
565
+ const enabledIds = new Set(rbConfig.skills?.enabled || []);
470
566
  return {
471
567
  content: [
472
568
  {
@@ -505,11 +601,15 @@ export async function startRulebookMcpServer() {
505
601
  server.registerTool('rulebook_skill_validate', {
506
602
  title: 'Validate Skills Configuration',
507
603
  description: 'Validate the current skills configuration for conflicts and dependencies',
508
- inputSchema: {},
509
- }, async () => {
604
+ inputSchema: {
605
+ projectId: projectIdSchema,
606
+ },
607
+ }, async (args) => {
510
608
  try {
511
- const rulebookConfig = await configManager.loadConfig();
512
- const validation = await skillsManager.validateSkills(rulebookConfig);
609
+ const sm = await getSkillsMgr(args.projectId);
610
+ const cm = await getConfigMgr(args.projectId);
611
+ const rbConfig = await cm.loadConfig();
612
+ const validation = await sm.validateSkills(rbConfig);
513
613
  return {
514
614
  content: [
515
615
  {
@@ -520,7 +620,7 @@ export async function startRulebookMcpServer() {
520
620
  errors: validation.errors,
521
621
  warnings: validation.warnings,
522
622
  conflicts: validation.conflicts,
523
- enabledCount: rulebookConfig.skills?.enabled?.length || 0,
623
+ enabledCount: rbConfig.skills?.enabled?.length || 0,
524
624
  }),
525
625
  },
526
626
  ],
@@ -543,37 +643,42 @@ export async function startRulebookMcpServer() {
543
643
  // ============================================
544
644
  // Memory System Functions (v3.0)
545
645
  // ============================================
546
- // Conditionally initialize MemoryManager
646
+ // Conditionally initialize MemoryManager (single-project mode only;
647
+ // in workspace mode each worker manages its own memory)
547
648
  let memoryManager = null;
548
649
  let bgIndexer = null;
549
650
  let autoCaptureEnabled = false;
550
- const rulebookConfig = await configManager.loadConfig();
551
- if (rulebookConfig.memory?.enabled) {
552
- try {
553
- const { createMemoryManager } = await import('../memory/memory-manager.js');
554
- const memoryDbPath = join(config.projectRoot, rulebookConfig.memory.dbPath ?? '.rulebook/memory/memory.db');
555
- console.error(`[rulebook-mcp] Memory DB: ${memoryDbPath}`);
556
- memoryManager = createMemoryManager(config.projectRoot, rulebookConfig.memory);
557
- autoCaptureEnabled = rulebookConfig.memory.autoCapture !== false; // enabled by default when memory is on
558
- // Boot Background Indexer
559
- bgIndexer = new BackgroundIndexer(memoryManager, config.projectRoot, { enabled: true });
560
- bgIndexer.start();
561
- // Expose status to global so the tool can read it
562
- global.__indexerStatus = () => bgIndexer?.getStatus();
563
- // Intercept process exit to elegantly shut down BackgroundIndexer and SQLite
564
- process.on('SIGINT', async () => {
565
- console.log('[rulebook-mcp] Shutting down indexes...');
566
- if (bgIndexer)
567
- bgIndexer.stop();
568
- if (memoryManager)
569
- await memoryManager.close();
570
- process.exit(0);
571
- });
572
- }
573
- catch (e) {
574
- console.warn('[rulebook-mcp] Failed to boot Memory/Indexer:', e);
651
+ if (!isWorkspaceMode) {
652
+ const rulebookConfig = await configManager.loadConfig();
653
+ if (rulebookConfig.memory?.enabled) {
654
+ try {
655
+ const { createMemoryManager } = await import('../memory/memory-manager.js');
656
+ const memoryDbPath = join(projectRoot, rulebookConfig.memory.dbPath ?? '.rulebook/memory/memory.db');
657
+ console.error(`[rulebook-mcp] Memory DB: ${memoryDbPath}`);
658
+ memoryManager = createMemoryManager(projectRoot, rulebookConfig.memory);
659
+ autoCaptureEnabled = rulebookConfig.memory.autoCapture !== false;
660
+ // Boot Background Indexer
661
+ bgIndexer = new BackgroundIndexer(memoryManager, projectRoot, { enabled: true });
662
+ bgIndexer.start();
663
+ global.__indexerStatus = () => bgIndexer?.getStatus();
664
+ }
665
+ catch (e) {
666
+ console.warn('[rulebook-mcp] Failed to boot Memory/Indexer:', e);
667
+ }
575
668
  }
576
669
  }
670
+ // Graceful shutdown for both modes
671
+ process.on('SIGINT', async () => {
672
+ console.error('[rulebook-mcp] Shutting down...');
673
+ if (workspaceManager) {
674
+ await workspaceManager.shutdownAll();
675
+ }
676
+ if (bgIndexer)
677
+ bgIndexer.stop();
678
+ if (memoryManager)
679
+ await memoryManager.close();
680
+ process.exit(0);
681
+ });
577
682
  /**
578
683
  * Auto-capture: save tool interactions to memory in the background.
579
684
  * Fire-and-forget — never blocks or fails the original tool call.
@@ -619,12 +724,14 @@ export async function startRulebookMcpServer() {
619
724
  limit: z.number().optional().describe('Max results (default 20)'),
620
725
  mode: z.enum(['bm25', 'vector', 'hybrid']).optional().describe('Search mode'),
621
726
  type: z.string().optional().describe('Filter by memory type'),
727
+ projectId: projectIdSchema,
622
728
  },
623
729
  }, async (args) => {
624
- if (!memoryManager)
730
+ const mm = await getMemMgr(args.projectId);
731
+ if (!mm)
625
732
  return memoryNotEnabled();
626
733
  try {
627
- const results = await memoryManager.searchMemories({
734
+ const results = await mm.searchMemories({
628
735
  query: args.query,
629
736
  limit: args.limit,
630
737
  mode: args.mode,
@@ -654,12 +761,14 @@ export async function startRulebookMcpServer() {
654
761
  inputSchema: {
655
762
  memoryId: z.string().describe('Memory ID to anchor timeline'),
656
763
  window: z.number().optional().describe('Number of memories before/after (default 5)'),
764
+ projectId: projectIdSchema,
657
765
  },
658
766
  }, async (args) => {
659
- if (!memoryManager)
767
+ const mm = await getMemMgr(args.projectId);
768
+ if (!mm)
660
769
  return memoryNotEnabled();
661
770
  try {
662
- const timeline = await memoryManager.getTimeline(args.memoryId, args.window);
771
+ const timeline = await mm.getTimeline(args.memoryId, args.window);
663
772
  return {
664
773
  content: [{ type: 'text', text: JSON.stringify({ success: true, timeline }) }],
665
774
  };
@@ -678,12 +787,14 @@ export async function startRulebookMcpServer() {
678
787
  description: 'Get full details for specific memory IDs',
679
788
  inputSchema: {
680
789
  ids: z.array(z.string()).describe('Memory IDs to fetch'),
790
+ projectId: projectIdSchema,
681
791
  },
682
792
  }, async (args) => {
683
- if (!memoryManager)
793
+ const mm = await getMemMgr(args.projectId);
794
+ if (!mm)
684
795
  return memoryNotEnabled();
685
796
  try {
686
- const memories = await memoryManager.getFullDetails(args.ids);
797
+ const memories = await mm.getFullDetails(args.ids);
687
798
  return {
688
799
  content: [{ type: 'text', text: JSON.stringify({ success: true, memories }) }],
689
800
  };
@@ -707,12 +818,14 @@ export async function startRulebookMcpServer() {
707
818
  title: z.string().describe('Memory title'),
708
819
  content: z.string().describe('Memory content'),
709
820
  tags: z.array(z.string()).optional().describe('Tags'),
821
+ projectId: projectIdSchema,
710
822
  },
711
823
  }, async (args) => {
712
- if (!memoryManager)
824
+ const mm = await getMemMgr(args.projectId);
825
+ if (!mm)
713
826
  return memoryNotEnabled();
714
827
  try {
715
- const memory = await memoryManager.saveMemory({
828
+ const memory = await mm.saveMemory({
716
829
  type: args.type,
717
830
  title: args.title,
718
831
  content: args.content,
@@ -742,12 +855,15 @@ export async function startRulebookMcpServer() {
742
855
  server.registerTool('rulebook_memory_stats', {
743
856
  title: 'Memory Statistics',
744
857
  description: 'Get memory database statistics',
745
- inputSchema: {},
746
- }, async () => {
747
- if (!memoryManager)
858
+ inputSchema: {
859
+ projectId: projectIdSchema,
860
+ },
861
+ }, async (args) => {
862
+ const mm = await getMemMgr(args.projectId);
863
+ if (!mm)
748
864
  return memoryNotEnabled();
749
865
  try {
750
- const stats = await memoryManager.getStats();
866
+ const stats = await mm.getStats();
751
867
  return {
752
868
  content: [{ type: 'text', text: JSON.stringify({ success: true, stats }) }],
753
869
  };
@@ -766,12 +882,14 @@ export async function startRulebookMcpServer() {
766
882
  description: 'Force memory eviction and cleanup',
767
883
  inputSchema: {
768
884
  force: z.boolean().optional().describe('Force cleanup regardless of size'),
885
+ projectId: projectIdSchema,
769
886
  },
770
887
  }, async (args) => {
771
- if (!memoryManager)
888
+ const mm = await getMemMgr(args.projectId);
889
+ if (!mm)
772
890
  return memoryNotEnabled();
773
891
  try {
774
- const result = await memoryManager.cleanup(args.force ?? false);
892
+ const result = await mm.cleanup(args.force ?? false);
775
893
  return {
776
894
  content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }) }],
777
895
  };
@@ -798,16 +916,16 @@ export async function startRulebookMcpServer() {
798
916
  const { Logger } = await import('../core/logger.js');
799
917
  const { RalphManager } = await import('../core/ralph-manager.js');
800
918
  const { PRDGenerator } = await import('../core/prd-generator.js');
801
- const logger = new Logger(config.projectRoot);
802
- const ralphManager = new RalphManager(config.projectRoot, logger);
803
- const prdGenerator = new PRDGenerator(config.projectRoot, logger);
919
+ const logger = new Logger(projectRoot);
920
+ const ralphManager = new RalphManager(projectRoot, logger);
921
+ const prdGenerator = new PRDGenerator(projectRoot, logger);
804
922
  const configData = await configManager.loadConfig();
805
923
  const maxIterations = configData.ralph?.maxIterations || 10;
806
924
  const tool = (configData.ralph?.tool || 'claude');
807
925
  // Generate PRD first, then initialize with correct task count
808
- const prd = await prdGenerator.generatePRD(basename(config.projectRoot) || 'project');
926
+ const prd = await prdGenerator.generatePRD(basename(projectRoot) || 'project');
809
927
  const { writeFile } = await import('../utils/file-system.js');
810
- const prdPath = join(config.projectRoot, '.rulebook', 'ralph', 'prd.json');
928
+ const prdPath = join(projectRoot, '.rulebook', 'ralph', 'prd.json');
811
929
  await writeFile(prdPath, JSON.stringify(prd, null, 2));
812
930
  // Initialize after PRD is written so task count is correct
813
931
  await ralphManager.initialize(maxIterations, tool);
@@ -852,8 +970,8 @@ export async function startRulebookMcpServer() {
852
970
  const { RalphParser } = await import('../agents/ralph-parser.js');
853
971
  const { spawn } = await import('child_process');
854
972
  const { execSync } = await import('child_process');
855
- const logger = new Logger(config.projectRoot);
856
- const ralphManager = new RalphManager(config.projectRoot, logger);
973
+ const logger = new Logger(projectRoot);
974
+ const ralphManager = new RalphManager(projectRoot, logger);
857
975
  const configData = await configManager.loadConfig();
858
976
  const maxIterations = args.maxIterations || configData.ralph?.maxIterations || 10;
859
977
  const tool = (args.tool || configData.ralph?.tool || 'claude');
@@ -913,7 +1031,7 @@ export async function startRulebookMcpServer() {
913
1031
  let stdout = '';
914
1032
  let stderr = '';
915
1033
  const proc = spawn(cmd, cmdArgs, {
916
- cwd: config.projectRoot,
1034
+ cwd: projectRoot,
917
1035
  shell: true,
918
1036
  stdio: ['pipe', 'pipe', 'pipe'],
919
1037
  });
@@ -976,7 +1094,7 @@ export async function startRulebookMcpServer() {
976
1094
  }
977
1095
  };
978
1096
  const proc = spawn(cfg.cmd, cfg.args, {
979
- cwd: config.projectRoot,
1097
+ cwd: projectRoot,
980
1098
  shell: true,
981
1099
  stdio: ['pipe', 'pipe', 'pipe'],
982
1100
  });
@@ -1163,8 +1281,8 @@ export async function startRulebookMcpServer() {
1163
1281
  try {
1164
1282
  const { Logger } = await import('../core/logger.js');
1165
1283
  const { RalphManager } = await import('../core/ralph-manager.js');
1166
- const logger = new Logger(config.projectRoot);
1167
- const ralphManager = new RalphManager(config.projectRoot, logger);
1284
+ const logger = new Logger(projectRoot);
1285
+ const ralphManager = new RalphManager(projectRoot, logger);
1168
1286
  const status = await ralphManager.getStatus();
1169
1287
  if (!status) {
1170
1288
  return {
@@ -1230,8 +1348,8 @@ export async function startRulebookMcpServer() {
1230
1348
  try {
1231
1349
  const { Logger } = await import('../core/logger.js');
1232
1350
  const { IterationTracker } = await import('../core/iteration-tracker.js');
1233
- const logger = new Logger(config.projectRoot);
1234
- const tracker = new IterationTracker(config.projectRoot, logger);
1351
+ const logger = new Logger(projectRoot);
1352
+ const tracker = new IterationTracker(projectRoot, logger);
1235
1353
  const history = await tracker.getHistory(args.limit || 10, args.taskId);
1236
1354
  const stats = await tracker.getStatistics();
1237
1355
  return {
@@ -1290,12 +1408,14 @@ export async function startRulebookMcpServer() {
1290
1408
  const results = await memoryManager.searchMemories({
1291
1409
  query: args.query,
1292
1410
  limit: args.limit ?? 10,
1293
- mode: 'hybrid' // Force hybrid search for best code-chunk matching
1411
+ mode: 'hybrid', // Force hybrid search for best code-chunk matching
1294
1412
  });
1295
1413
  // Filter out normal memories, keep only code nodes
1296
1414
  const codeResults = results.filter((r) => r.id.startsWith('__code__'));
1297
1415
  return {
1298
- content: [{ type: 'text', text: JSON.stringify({ success: true, results: codeResults }) }],
1416
+ content: [
1417
+ { type: 'text', text: JSON.stringify({ success: true, results: codeResults }) },
1418
+ ],
1299
1419
  };
1300
1420
  }
1301
1421
  catch (error) {
@@ -1317,17 +1437,20 @@ export async function startRulebookMcpServer() {
1317
1437
  if (!memoryManager)
1318
1438
  return memoryNotEnabled();
1319
1439
  try {
1320
- // Since V1 has limited Graph search implementation in memory-search,
1440
+ // Since V1 has limited Graph search implementation in memory-search,
1321
1441
  // we'll return a placeholder indicating the edge relations.
1322
1442
  // In a real implementation we would call a memoryManager.getGraphAdjacent(args.filePath)
1323
1443
  return {
1324
- content: [{
1325
- type: 'text', text: JSON.stringify({
1444
+ content: [
1445
+ {
1446
+ type: 'text',
1447
+ text: JSON.stringify({
1326
1448
  success: true,
1327
1449
  message: `Graph query for ${args.filePath} accepted. (Note: Graph deep-search pending V2 implementation, use codebase_search for now.)`,
1328
- filePath: args.filePath
1329
- })
1330
- }],
1450
+ filePath: args.filePath,
1451
+ }),
1452
+ },
1453
+ ],
1331
1454
  };
1332
1455
  }
1333
1456
  catch (error) {
@@ -1347,7 +1470,9 @@ export async function startRulebookMcpServer() {
1347
1470
  try {
1348
1471
  // Because the BackgroundIndexer runs asynchronously, we fetch its global state
1349
1472
  // assuming it was attached to the server context during boot.
1350
- const status = global.__indexerStatus ? global.__indexerStatus() : { running: false, error: 'Indexer not attached to global context' };
1473
+ const status = global.__indexerStatus
1474
+ ? global.__indexerStatus()
1475
+ : { running: false, error: 'Indexer not attached to global context' };
1351
1476
  return {
1352
1477
  content: [{ type: 'text', text: JSON.stringify({ success: true, status }) }],
1353
1478
  };
@@ -1360,6 +1485,148 @@ export async function startRulebookMcpServer() {
1360
1485
  };
1361
1486
  }
1362
1487
  });
1488
+ // ============================================
1489
+ // Workspace Tools (v4.2 — workspace mode only)
1490
+ // ============================================
1491
+ if (workspaceManager) {
1492
+ // Register tool: rulebook_workspace_list
1493
+ server.registerTool('rulebook_workspace_list', {
1494
+ title: 'List Workspace Projects',
1495
+ description: 'List all projects in the current workspace',
1496
+ inputSchema: {},
1497
+ }, async () => {
1498
+ const projects = workspaceManager.getProjects();
1499
+ const activeIds = workspaceManager.getActiveWorkerIds();
1500
+ return {
1501
+ content: [
1502
+ {
1503
+ type: 'text',
1504
+ text: JSON.stringify({
1505
+ success: true,
1506
+ workspace: workspaceManager.getConfig().name,
1507
+ defaultProject: workspaceManager.getDefaultProjectId(),
1508
+ projects: projects.map((p) => ({
1509
+ name: p.name,
1510
+ path: p.path,
1511
+ workerActive: activeIds.includes(p.name),
1512
+ })),
1513
+ count: projects.length,
1514
+ }),
1515
+ },
1516
+ ],
1517
+ };
1518
+ });
1519
+ // Register tool: rulebook_workspace_status
1520
+ server.registerTool('rulebook_workspace_status', {
1521
+ title: 'Workspace Status',
1522
+ description: 'Get detailed status of all workspace projects (workers, tasks, memory)',
1523
+ inputSchema: {},
1524
+ }, async () => {
1525
+ try {
1526
+ const status = await workspaceManager.getStatus();
1527
+ return {
1528
+ content: [{ type: 'text', text: JSON.stringify({ success: true, ...status }) }],
1529
+ };
1530
+ }
1531
+ catch (error) {
1532
+ return {
1533
+ content: [
1534
+ { type: 'text', text: JSON.stringify({ success: false, error: String(error) }) },
1535
+ ],
1536
+ };
1537
+ }
1538
+ });
1539
+ // Register tool: rulebook_workspace_search
1540
+ server.registerTool('rulebook_workspace_search', {
1541
+ title: 'Cross-Project Memory Search',
1542
+ description: 'Search memories across all projects in the workspace',
1543
+ inputSchema: {
1544
+ query: z.string().describe('Search query'),
1545
+ limit: z.number().optional().describe('Max results per project (default 10)'),
1546
+ },
1547
+ }, async (args) => {
1548
+ try {
1549
+ const results = await workspaceManager.searchMemoryAcrossProjects(args.query, {
1550
+ limit: args.limit,
1551
+ });
1552
+ return {
1553
+ content: [
1554
+ {
1555
+ type: 'text',
1556
+ text: JSON.stringify({
1557
+ success: true,
1558
+ results,
1559
+ projectsSearched: results.length,
1560
+ }),
1561
+ },
1562
+ ],
1563
+ };
1564
+ }
1565
+ catch (error) {
1566
+ return {
1567
+ content: [
1568
+ { type: 'text', text: JSON.stringify({ success: false, error: String(error) }) },
1569
+ ],
1570
+ };
1571
+ }
1572
+ });
1573
+ // Register tool: rulebook_workspace_tasks
1574
+ server.registerTool('rulebook_workspace_tasks', {
1575
+ title: 'List Tasks Across Projects',
1576
+ description: 'List tasks from all workspace projects',
1577
+ inputSchema: {
1578
+ status: z
1579
+ .enum(['pending', 'in-progress', 'completed', 'blocked'])
1580
+ .optional()
1581
+ .describe('Filter by status'),
1582
+ },
1583
+ }, async (args) => {
1584
+ try {
1585
+ const allTasks = [];
1586
+ for (const project of workspaceManager.getProjects()) {
1587
+ try {
1588
+ const tm = await getTaskMgr(project.name);
1589
+ const tasks = await tm.listTasks(false);
1590
+ const filtered = args.status
1591
+ ? tasks.filter((t) => t.status === args.status)
1592
+ : tasks;
1593
+ if (filtered.length > 0) {
1594
+ allTasks.push({
1595
+ project: project.name,
1596
+ tasks: filtered.map((t) => ({
1597
+ id: t.id,
1598
+ title: t.title,
1599
+ status: t.status,
1600
+ })),
1601
+ });
1602
+ }
1603
+ }
1604
+ catch {
1605
+ // Skip projects that fail
1606
+ }
1607
+ }
1608
+ return {
1609
+ content: [
1610
+ {
1611
+ type: 'text',
1612
+ text: JSON.stringify({
1613
+ success: true,
1614
+ projects: allTasks,
1615
+ totalTasks: allTasks.reduce((sum, p) => sum + p.tasks.length, 0),
1616
+ }),
1617
+ },
1618
+ ],
1619
+ };
1620
+ }
1621
+ catch (error) {
1622
+ return {
1623
+ content: [
1624
+ { type: 'text', text: JSON.stringify({ success: false, error: String(error) }) },
1625
+ ],
1626
+ };
1627
+ }
1628
+ });
1629
+ }
1363
1630
  const transport = new StdioServerTransport();
1364
1631
  await server.connect(transport);
1365
1632
  }