@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
@@ -1,12 +1,14 @@
1
1
  #!/usr/bin/env node
2
2
  import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
3
3
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
4
+ import { existsSync, readFileSync, statSync } from 'fs';
5
+ import { basename, dirname, join, resolve } from 'path';
4
6
  import { z } from 'zod';
5
- import { TaskManager } from '../core/task-manager.js';
6
- import { SkillsManager, getDefaultTemplatesPath } from '../core/skills-manager.js';
7
7
  import { ConfigManager } from '../core/config-manager.js';
8
- import { join, dirname, resolve, basename } from 'path';
9
- import { readFileSync, existsSync, statSync } from 'fs';
8
+ import { BackgroundIndexer } from '../core/indexer/background-indexer.js';
9
+ import { SkillsManager, getDefaultTemplatesPath } from '../core/skills-manager.js';
10
+ import { TaskManager } from '../core/task-manager.js';
11
+ import { WorkspaceManager } from '../core/workspace/workspace-manager.js';
10
12
  // Find .rulebook file/directory by walking up directories
11
13
  export function findRulebookConfig(startDir) {
12
14
  let current = resolve(startDir);
@@ -54,23 +56,94 @@ function loadConfig() {
54
56
  return { projectRoot, tasksDir, archiveDir };
55
57
  }
56
58
  export async function startRulebookMcpServer() {
57
- const config = loadConfig();
58
- const taskManager = new TaskManager(config.projectRoot, '.rulebook');
59
- const skillsManager = new SkillsManager(getDefaultTemplatesPath(), config.projectRoot);
60
- 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. Create .rulebook-workspace.json or use a *.code-workspace file.');
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
+ }
61
130
  const server = new McpServer({
62
131
  name: 'rulebook-task-management',
63
- version: '4.0.0',
132
+ version: '4.2.0',
64
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)');
65
136
  // Register tool: rulebook_task_create
66
137
  server.registerTool('rulebook_task_create', {
67
138
  title: 'Create Rulebook Task',
68
139
  description: 'Create a new Rulebook task',
69
140
  inputSchema: {
70
141
  taskId: z.string().describe('Task ID in kebab-case'),
142
+ projectId: projectIdSchema,
71
143
  },
72
144
  }, async (args) => {
73
- await taskManager.createTask(args.taskId);
145
+ const tm = await getTaskMgr(args.projectId);
146
+ await tm.createTask(args.taskId);
74
147
  const resultText = JSON.stringify({
75
148
  success: true,
76
149
  taskId: args.taskId,
@@ -89,9 +162,11 @@ export async function startRulebookMcpServer() {
89
162
  .enum(['pending', 'in-progress', 'completed', 'blocked'])
90
163
  .optional()
91
164
  .describe('Filter by status'),
165
+ projectId: projectIdSchema,
92
166
  },
93
167
  }, async (args) => {
94
- const tasks = await taskManager.listTasks(args.includeArchived || false);
168
+ const tm = await getTaskMgr(args.projectId);
169
+ const tasks = await tm.listTasks(args.includeArchived || false);
95
170
  let filtered = tasks;
96
171
  if (args.status) {
97
172
  filtered = tasks.filter((t) => t.status === args.status);
@@ -120,9 +195,11 @@ export async function startRulebookMcpServer() {
120
195
  description: 'Show task details',
121
196
  inputSchema: {
122
197
  taskId: z.string().describe('Task ID to show'),
198
+ projectId: projectIdSchema,
123
199
  },
124
200
  }, async (args) => {
125
- const task = await taskManager.showTask(args.taskId);
201
+ const tm = await getTaskMgr(args.projectId);
202
+ const task = await tm.showTask(args.taskId);
126
203
  return {
127
204
  content: [
128
205
  {
@@ -157,10 +234,12 @@ export async function startRulebookMcpServer() {
157
234
  .enum(['pending', 'in-progress', 'completed', 'blocked'])
158
235
  .optional()
159
236
  .describe('New status'),
237
+ projectId: projectIdSchema,
160
238
  },
161
239
  }, async (args) => {
240
+ const tm = await getTaskMgr(args.projectId);
162
241
  if (args.status) {
163
- await taskManager.updateTaskStatus(args.taskId, args.status);
242
+ await tm.updateTaskStatus(args.taskId, args.status);
164
243
  }
165
244
  const resultText = JSON.stringify({
166
245
  success: true,
@@ -176,9 +255,11 @@ export async function startRulebookMcpServer() {
176
255
  description: 'Validate task format',
177
256
  inputSchema: {
178
257
  taskId: z.string().describe('Task ID to validate'),
258
+ projectId: projectIdSchema,
179
259
  },
180
260
  }, async (args) => {
181
- const validation = await taskManager.validateTask(args.taskId);
261
+ const tm = await getTaskMgr(args.projectId);
262
+ const validation = await tm.validateTask(args.taskId);
182
263
  return {
183
264
  content: [
184
265
  {
@@ -199,9 +280,11 @@ export async function startRulebookMcpServer() {
199
280
  inputSchema: {
200
281
  taskId: z.string().describe('Task ID to archive'),
201
282
  skipValidation: z.boolean().optional().describe('Skip validation before archiving'),
283
+ projectId: projectIdSchema,
202
284
  },
203
285
  }, async (args) => {
204
- await taskManager.archiveTask(args.taskId, args.skipValidation || false);
286
+ const tm = await getTaskMgr(args.projectId);
287
+ await tm.archiveTask(args.taskId, args.skipValidation || false);
205
288
  const resultText = JSON.stringify({
206
289
  success: true,
207
290
  taskId: args.taskId,
@@ -216,9 +299,11 @@ export async function startRulebookMcpServer() {
216
299
  description: 'Delete a task permanently',
217
300
  inputSchema: {
218
301
  taskId: z.string().describe('Task ID to delete'),
302
+ projectId: projectIdSchema,
219
303
  },
220
304
  }, async (args) => {
221
- await taskManager.deleteTask(args.taskId);
305
+ const tm = await getTaskMgr(args.projectId);
306
+ await tm.deleteTask(args.taskId);
222
307
  const resultText = JSON.stringify({
223
308
  success: true,
224
309
  taskId: args.taskId,
@@ -251,19 +336,21 @@ export async function startRulebookMcpServer() {
251
336
  .optional()
252
337
  .describe('Filter by category'),
253
338
  enabledOnly: z.boolean().optional().describe('Show only enabled skills'),
339
+ projectId: projectIdSchema,
254
340
  },
255
341
  }, async (args) => {
256
342
  try {
343
+ const sm = await getSkillsMgr(args.projectId);
344
+ const cm = await getConfigMgr(args.projectId);
257
345
  let skills;
258
346
  if (args.category) {
259
- skills = await skillsManager.getSkillsByCategory(args.category);
347
+ skills = await sm.getSkillsByCategory(args.category);
260
348
  }
261
349
  else {
262
- skills = await skillsManager.getSkills();
350
+ skills = await sm.getSkills();
263
351
  }
264
- // Check enabled status from config
265
- const rulebookConfig = await configManager.loadConfig();
266
- const enabledIds = new Set(rulebookConfig.skills?.enabled || []);
352
+ const rbConfig = await cm.loadConfig();
353
+ const enabledIds = new Set(rbConfig.skills?.enabled || []);
267
354
  let filteredSkills = skills.map((s) => ({
268
355
  id: s.id,
269
356
  name: s.metadata.name,
@@ -310,10 +397,13 @@ export async function startRulebookMcpServer() {
310
397
  description: 'Show detailed information about a specific skill',
311
398
  inputSchema: {
312
399
  skillId: z.string().describe('Skill ID (e.g., languages/typescript)'),
400
+ projectId: projectIdSchema,
313
401
  },
314
402
  }, async (args) => {
315
403
  try {
316
- 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);
317
407
  if (!skill) {
318
408
  return {
319
409
  content: [
@@ -328,8 +418,8 @@ export async function startRulebookMcpServer() {
328
418
  ],
329
419
  };
330
420
  }
331
- const rulebookConfig = await configManager.loadConfig();
332
- 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;
333
423
  return {
334
424
  content: [
335
425
  {
@@ -374,14 +464,16 @@ export async function startRulebookMcpServer() {
374
464
  description: 'Enable a skill in the project configuration',
375
465
  inputSchema: {
376
466
  skillId: z.string().describe('Skill ID to enable (e.g., languages/typescript)'),
467
+ projectId: projectIdSchema,
377
468
  },
378
469
  }, async (args) => {
379
470
  try {
380
- let rulebookConfig = await configManager.loadConfig();
381
- rulebookConfig = await skillsManager.enableSkill(args.skillId, rulebookConfig);
382
- await configManager.saveConfig(rulebookConfig);
383
- // Validate to check for conflicts
384
- 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);
385
477
  const resultText = JSON.stringify({
386
478
  success: true,
387
479
  skillId: args.skillId,
@@ -412,12 +504,14 @@ export async function startRulebookMcpServer() {
412
504
  description: 'Disable a skill in the project configuration',
413
505
  inputSchema: {
414
506
  skillId: z.string().describe('Skill ID to disable (e.g., languages/typescript)'),
507
+ projectId: projectIdSchema,
415
508
  },
416
509
  }, async (args) => {
417
510
  try {
418
- let rulebookConfig = await configManager.loadConfig();
419
- // Check if skill is enabled
420
- 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)) {
421
515
  return {
422
516
  content: [
423
517
  {
@@ -430,8 +524,8 @@ export async function startRulebookMcpServer() {
430
524
  ],
431
525
  };
432
526
  }
433
- rulebookConfig = await skillsManager.disableSkill(args.skillId, rulebookConfig);
434
- await configManager.saveConfig(rulebookConfig);
527
+ rbConfig = await sm.disableSkill(args.skillId, rbConfig);
528
+ await cm.saveConfig(rbConfig);
435
529
  const resultText = JSON.stringify({
436
530
  success: true,
437
531
  skillId: args.skillId,
@@ -460,12 +554,15 @@ export async function startRulebookMcpServer() {
460
554
  description: 'Search for skills by name, description, or tags',
461
555
  inputSchema: {
462
556
  query: z.string().describe('Search query'),
557
+ projectId: projectIdSchema,
463
558
  },
464
559
  }, async (args) => {
465
560
  try {
466
- const skills = await skillsManager.searchSkills(args.query);
467
- const rulebookConfig = await configManager.loadConfig();
468
- 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 || []);
469
566
  return {
470
567
  content: [
471
568
  {
@@ -504,11 +601,15 @@ export async function startRulebookMcpServer() {
504
601
  server.registerTool('rulebook_skill_validate', {
505
602
  title: 'Validate Skills Configuration',
506
603
  description: 'Validate the current skills configuration for conflicts and dependencies',
507
- inputSchema: {},
508
- }, async () => {
604
+ inputSchema: {
605
+ projectId: projectIdSchema,
606
+ },
607
+ }, async (args) => {
509
608
  try {
510
- const rulebookConfig = await configManager.loadConfig();
511
- 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);
512
613
  return {
513
614
  content: [
514
615
  {
@@ -519,7 +620,7 @@ export async function startRulebookMcpServer() {
519
620
  errors: validation.errors,
520
621
  warnings: validation.warnings,
521
622
  conflicts: validation.conflicts,
522
- enabledCount: rulebookConfig.skills?.enabled?.length || 0,
623
+ enabledCount: rbConfig.skills?.enabled?.length || 0,
523
624
  }),
524
625
  },
525
626
  ],
@@ -542,22 +643,42 @@ export async function startRulebookMcpServer() {
542
643
  // ============================================
543
644
  // Memory System Functions (v3.0)
544
645
  // ============================================
545
- // Conditionally initialize MemoryManager
646
+ // Conditionally initialize MemoryManager (single-project mode only;
647
+ // in workspace mode each worker manages its own memory)
546
648
  let memoryManager = null;
649
+ let bgIndexer = null;
547
650
  let autoCaptureEnabled = false;
548
- const rulebookConfig = await configManager.loadConfig();
549
- if (rulebookConfig.memory?.enabled) {
550
- try {
551
- const { createMemoryManager } = await import('../memory/memory-manager.js');
552
- const memoryDbPath = join(config.projectRoot, rulebookConfig.memory.dbPath ?? '.rulebook/memory/memory.db');
553
- console.error(`[rulebook-mcp] Memory DB: ${memoryDbPath}`);
554
- memoryManager = createMemoryManager(config.projectRoot, rulebookConfig.memory);
555
- autoCaptureEnabled = rulebookConfig.memory.autoCapture !== false; // enabled by default when memory is on
556
- }
557
- catch {
558
- // Memory module not available
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
+ }
559
668
  }
560
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
+ });
561
682
  /**
562
683
  * Auto-capture: save tool interactions to memory in the background.
563
684
  * Fire-and-forget — never blocks or fails the original tool call.
@@ -603,12 +724,14 @@ export async function startRulebookMcpServer() {
603
724
  limit: z.number().optional().describe('Max results (default 20)'),
604
725
  mode: z.enum(['bm25', 'vector', 'hybrid']).optional().describe('Search mode'),
605
726
  type: z.string().optional().describe('Filter by memory type'),
727
+ projectId: projectIdSchema,
606
728
  },
607
729
  }, async (args) => {
608
- if (!memoryManager)
730
+ const mm = await getMemMgr(args.projectId);
731
+ if (!mm)
609
732
  return memoryNotEnabled();
610
733
  try {
611
- const results = await memoryManager.searchMemories({
734
+ const results = await mm.searchMemories({
612
735
  query: args.query,
613
736
  limit: args.limit,
614
737
  mode: args.mode,
@@ -638,12 +761,14 @@ export async function startRulebookMcpServer() {
638
761
  inputSchema: {
639
762
  memoryId: z.string().describe('Memory ID to anchor timeline'),
640
763
  window: z.number().optional().describe('Number of memories before/after (default 5)'),
764
+ projectId: projectIdSchema,
641
765
  },
642
766
  }, async (args) => {
643
- if (!memoryManager)
767
+ const mm = await getMemMgr(args.projectId);
768
+ if (!mm)
644
769
  return memoryNotEnabled();
645
770
  try {
646
- const timeline = await memoryManager.getTimeline(args.memoryId, args.window);
771
+ const timeline = await mm.getTimeline(args.memoryId, args.window);
647
772
  return {
648
773
  content: [{ type: 'text', text: JSON.stringify({ success: true, timeline }) }],
649
774
  };
@@ -662,12 +787,14 @@ export async function startRulebookMcpServer() {
662
787
  description: 'Get full details for specific memory IDs',
663
788
  inputSchema: {
664
789
  ids: z.array(z.string()).describe('Memory IDs to fetch'),
790
+ projectId: projectIdSchema,
665
791
  },
666
792
  }, async (args) => {
667
- if (!memoryManager)
793
+ const mm = await getMemMgr(args.projectId);
794
+ if (!mm)
668
795
  return memoryNotEnabled();
669
796
  try {
670
- const memories = await memoryManager.getFullDetails(args.ids);
797
+ const memories = await mm.getFullDetails(args.ids);
671
798
  return {
672
799
  content: [{ type: 'text', text: JSON.stringify({ success: true, memories }) }],
673
800
  };
@@ -691,12 +818,14 @@ export async function startRulebookMcpServer() {
691
818
  title: z.string().describe('Memory title'),
692
819
  content: z.string().describe('Memory content'),
693
820
  tags: z.array(z.string()).optional().describe('Tags'),
821
+ projectId: projectIdSchema,
694
822
  },
695
823
  }, async (args) => {
696
- if (!memoryManager)
824
+ const mm = await getMemMgr(args.projectId);
825
+ if (!mm)
697
826
  return memoryNotEnabled();
698
827
  try {
699
- const memory = await memoryManager.saveMemory({
828
+ const memory = await mm.saveMemory({
700
829
  type: args.type,
701
830
  title: args.title,
702
831
  content: args.content,
@@ -726,12 +855,15 @@ export async function startRulebookMcpServer() {
726
855
  server.registerTool('rulebook_memory_stats', {
727
856
  title: 'Memory Statistics',
728
857
  description: 'Get memory database statistics',
729
- inputSchema: {},
730
- }, async () => {
731
- if (!memoryManager)
858
+ inputSchema: {
859
+ projectId: projectIdSchema,
860
+ },
861
+ }, async (args) => {
862
+ const mm = await getMemMgr(args.projectId);
863
+ if (!mm)
732
864
  return memoryNotEnabled();
733
865
  try {
734
- const stats = await memoryManager.getStats();
866
+ const stats = await mm.getStats();
735
867
  return {
736
868
  content: [{ type: 'text', text: JSON.stringify({ success: true, stats }) }],
737
869
  };
@@ -750,12 +882,14 @@ export async function startRulebookMcpServer() {
750
882
  description: 'Force memory eviction and cleanup',
751
883
  inputSchema: {
752
884
  force: z.boolean().optional().describe('Force cleanup regardless of size'),
885
+ projectId: projectIdSchema,
753
886
  },
754
887
  }, async (args) => {
755
- if (!memoryManager)
888
+ const mm = await getMemMgr(args.projectId);
889
+ if (!mm)
756
890
  return memoryNotEnabled();
757
891
  try {
758
- const result = await memoryManager.cleanup(args.force ?? false);
892
+ const result = await mm.cleanup(args.force ?? false);
759
893
  return {
760
894
  content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }) }],
761
895
  };
@@ -782,16 +916,16 @@ export async function startRulebookMcpServer() {
782
916
  const { Logger } = await import('../core/logger.js');
783
917
  const { RalphManager } = await import('../core/ralph-manager.js');
784
918
  const { PRDGenerator } = await import('../core/prd-generator.js');
785
- const logger = new Logger(config.projectRoot);
786
- const ralphManager = new RalphManager(config.projectRoot, logger);
787
- 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);
788
922
  const configData = await configManager.loadConfig();
789
923
  const maxIterations = configData.ralph?.maxIterations || 10;
790
924
  const tool = (configData.ralph?.tool || 'claude');
791
925
  // Generate PRD first, then initialize with correct task count
792
- const prd = await prdGenerator.generatePRD(basename(config.projectRoot) || 'project');
926
+ const prd = await prdGenerator.generatePRD(basename(projectRoot) || 'project');
793
927
  const { writeFile } = await import('../utils/file-system.js');
794
- const prdPath = join(config.projectRoot, '.rulebook', 'ralph', 'prd.json');
928
+ const prdPath = join(projectRoot, '.rulebook', 'ralph', 'prd.json');
795
929
  await writeFile(prdPath, JSON.stringify(prd, null, 2));
796
930
  // Initialize after PRD is written so task count is correct
797
931
  await ralphManager.initialize(maxIterations, tool);
@@ -836,8 +970,8 @@ export async function startRulebookMcpServer() {
836
970
  const { RalphParser } = await import('../agents/ralph-parser.js');
837
971
  const { spawn } = await import('child_process');
838
972
  const { execSync } = await import('child_process');
839
- const logger = new Logger(config.projectRoot);
840
- const ralphManager = new RalphManager(config.projectRoot, logger);
973
+ const logger = new Logger(projectRoot);
974
+ const ralphManager = new RalphManager(projectRoot, logger);
841
975
  const configData = await configManager.loadConfig();
842
976
  const maxIterations = args.maxIterations || configData.ralph?.maxIterations || 10;
843
977
  const tool = (args.tool || configData.ralph?.tool || 'claude');
@@ -897,7 +1031,7 @@ export async function startRulebookMcpServer() {
897
1031
  let stdout = '';
898
1032
  let stderr = '';
899
1033
  const proc = spawn(cmd, cmdArgs, {
900
- cwd: config.projectRoot,
1034
+ cwd: projectRoot,
901
1035
  shell: true,
902
1036
  stdio: ['pipe', 'pipe', 'pipe'],
903
1037
  });
@@ -960,7 +1094,7 @@ export async function startRulebookMcpServer() {
960
1094
  }
961
1095
  };
962
1096
  const proc = spawn(cfg.cmd, cfg.args, {
963
- cwd: config.projectRoot,
1097
+ cwd: projectRoot,
964
1098
  shell: true,
965
1099
  stdio: ['pipe', 'pipe', 'pipe'],
966
1100
  });
@@ -1147,8 +1281,8 @@ export async function startRulebookMcpServer() {
1147
1281
  try {
1148
1282
  const { Logger } = await import('../core/logger.js');
1149
1283
  const { RalphManager } = await import('../core/ralph-manager.js');
1150
- const logger = new Logger(config.projectRoot);
1151
- const ralphManager = new RalphManager(config.projectRoot, logger);
1284
+ const logger = new Logger(projectRoot);
1285
+ const ralphManager = new RalphManager(projectRoot, logger);
1152
1286
  const status = await ralphManager.getStatus();
1153
1287
  if (!status) {
1154
1288
  return {
@@ -1214,8 +1348,8 @@ export async function startRulebookMcpServer() {
1214
1348
  try {
1215
1349
  const { Logger } = await import('../core/logger.js');
1216
1350
  const { IterationTracker } = await import('../core/iteration-tracker.js');
1217
- const logger = new Logger(config.projectRoot);
1218
- const tracker = new IterationTracker(config.projectRoot, logger);
1351
+ const logger = new Logger(projectRoot);
1352
+ const tracker = new IterationTracker(projectRoot, logger);
1219
1353
  const history = await tracker.getHistory(args.limit || 10, args.taskId);
1220
1354
  const stats = await tracker.getStatistics();
1221
1355
  return {
@@ -1258,6 +1392,241 @@ export async function startRulebookMcpServer() {
1258
1392
  }
1259
1393
  });
1260
1394
  }
1395
+ // --- Background Indexer Tools ---
1396
+ // Register tool: rulebook_codebase_search
1397
+ server.registerTool('rulebook_codebase_search', {
1398
+ title: 'Codebase Semantic Search',
1399
+ description: 'Search the entire project semantically (via AST chunks and paragraphs)',
1400
+ inputSchema: {
1401
+ query: z.string().describe('The code or concept you are looking for'),
1402
+ limit: z.number().optional().describe('Result limit (default 10)'),
1403
+ },
1404
+ }, async (args) => {
1405
+ if (!memoryManager)
1406
+ return memoryNotEnabled();
1407
+ try {
1408
+ const results = await memoryManager.searchMemories({
1409
+ query: args.query,
1410
+ limit: args.limit ?? 10,
1411
+ mode: 'hybrid', // Force hybrid search for best code-chunk matching
1412
+ });
1413
+ // Filter out normal memories, keep only code nodes
1414
+ const codeResults = results.filter((r) => r.id.startsWith('__code__'));
1415
+ return {
1416
+ content: [
1417
+ { type: 'text', text: JSON.stringify({ success: true, results: codeResults }) },
1418
+ ],
1419
+ };
1420
+ }
1421
+ catch (error) {
1422
+ return {
1423
+ content: [
1424
+ { type: 'text', text: JSON.stringify({ success: false, error: String(error) }) },
1425
+ ],
1426
+ };
1427
+ }
1428
+ });
1429
+ // Register tool: rulebook_codebase_graph
1430
+ server.registerTool('rulebook_codebase_graph', {
1431
+ title: 'Codebase Graph Explorer',
1432
+ description: 'Find relationships (imports, exports) of a specific code node or file',
1433
+ inputSchema: {
1434
+ filePath: z.string().describe('The strict file path to query (e.g. src/core/app.ts)'),
1435
+ },
1436
+ }, async (args) => {
1437
+ if (!memoryManager)
1438
+ return memoryNotEnabled();
1439
+ try {
1440
+ // Since V1 has limited Graph search implementation in memory-search,
1441
+ // we'll return a placeholder indicating the edge relations.
1442
+ // In a real implementation we would call a memoryManager.getGraphAdjacent(args.filePath)
1443
+ return {
1444
+ content: [
1445
+ {
1446
+ type: 'text',
1447
+ text: JSON.stringify({
1448
+ success: true,
1449
+ message: `Graph query for ${args.filePath} accepted. (Note: Graph deep-search pending V2 implementation, use codebase_search for now.)`,
1450
+ filePath: args.filePath,
1451
+ }),
1452
+ },
1453
+ ],
1454
+ };
1455
+ }
1456
+ catch (error) {
1457
+ return {
1458
+ content: [
1459
+ { type: 'text', text: JSON.stringify({ success: false, error: String(error) }) },
1460
+ ],
1461
+ };
1462
+ }
1463
+ });
1464
+ // Register tool: rulebook_indexer_status
1465
+ server.registerTool('rulebook_indexer_status', {
1466
+ title: 'Background Indexer Status',
1467
+ description: 'Get the status of the local autonomous filesystem indexer',
1468
+ inputSchema: {},
1469
+ }, async () => {
1470
+ try {
1471
+ // Because the BackgroundIndexer runs asynchronously, we fetch its global state
1472
+ // assuming it was attached to the server context during boot.
1473
+ const status = global.__indexerStatus
1474
+ ? global.__indexerStatus()
1475
+ : { running: false, error: 'Indexer not attached to global context' };
1476
+ return {
1477
+ content: [{ type: 'text', text: JSON.stringify({ success: true, status }) }],
1478
+ };
1479
+ }
1480
+ catch (error) {
1481
+ return {
1482
+ content: [
1483
+ { type: 'text', text: JSON.stringify({ success: false, error: String(error) }) },
1484
+ ],
1485
+ };
1486
+ }
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
+ }
1261
1630
  const transport = new StdioServerTransport();
1262
1631
  await server.connect(transport);
1263
1632
  }