@hivehub/rulebook 4.4.1 → 5.0.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 (82) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/README.md +45 -4
  3. package/dist/cli/commands.d.ts.map +1 -1
  4. package/dist/cli/commands.js +86 -0
  5. package/dist/cli/commands.js.map +1 -1
  6. package/dist/core/agent-template-engine.d.ts +51 -0
  7. package/dist/core/agent-template-engine.d.ts.map +1 -0
  8. package/dist/core/agent-template-engine.js +273 -0
  9. package/dist/core/agent-template-engine.js.map +1 -0
  10. package/dist/core/complexity-detector.d.ts +36 -0
  11. package/dist/core/complexity-detector.d.ts.map +1 -0
  12. package/dist/core/complexity-detector.js +254 -0
  13. package/dist/core/complexity-detector.js.map +1 -0
  14. package/dist/core/generator.d.ts +1 -0
  15. package/dist/core/generator.d.ts.map +1 -1
  16. package/dist/core/generator.js +21 -3
  17. package/dist/core/generator.js.map +1 -1
  18. package/dist/core/indexer/background-indexer.d.ts +2 -2
  19. package/dist/core/indexer/background-indexer.d.ts.map +1 -1
  20. package/dist/core/indexer/background-indexer.js +55 -61
  21. package/dist/core/indexer/background-indexer.js.map +1 -1
  22. package/dist/core/rule-engine.d.ts +64 -0
  23. package/dist/core/rule-engine.d.ts.map +1 -0
  24. package/dist/core/rule-engine.js +333 -0
  25. package/dist/core/rule-engine.js.map +1 -0
  26. package/dist/core/task-manager.d.ts +4 -0
  27. package/dist/core/task-manager.d.ts.map +1 -1
  28. package/dist/core/task-manager.js +39 -24
  29. package/dist/core/task-manager.js.map +1 -1
  30. package/dist/index.js +182 -0
  31. package/dist/index.js.map +1 -1
  32. package/dist/mcp/rulebook-server.d.ts.map +1 -1
  33. package/dist/mcp/rulebook-server.js +278 -26
  34. package/dist/mcp/rulebook-server.js.map +1 -1
  35. package/dist/memory/hnsw-index.d.ts +3 -1
  36. package/dist/memory/hnsw-index.d.ts.map +1 -1
  37. package/dist/memory/hnsw-index.js +121 -18
  38. package/dist/memory/hnsw-index.js.map +1 -1
  39. package/dist/memory/memory-manager.d.ts +2 -0
  40. package/dist/memory/memory-manager.d.ts.map +1 -1
  41. package/dist/memory/memory-manager.js +16 -7
  42. package/dist/memory/memory-manager.js.map +1 -1
  43. package/dist/memory/memory-store.d.ts +15 -3
  44. package/dist/memory/memory-store.d.ts.map +1 -1
  45. package/dist/memory/memory-store.js +327 -274
  46. package/dist/memory/memory-store.js.map +1 -1
  47. package/dist/types.d.ts +17 -0
  48. package/dist/types.d.ts.map +1 -1
  49. package/package.json +8 -3
  50. package/templates/agents/compiler/codegen-debugger.md +34 -0
  51. package/templates/agents/compiler/stdlib-engineer.md +28 -0
  52. package/templates/agents/compiler/test-coverage-guardian.md +31 -0
  53. package/templates/agents/game-engine/cpp-core-expert.md +35 -0
  54. package/templates/agents/game-engine/render-engineer.md +22 -0
  55. package/templates/agents/game-engine/shader-engineer.md +38 -0
  56. package/templates/agents/game-engine/systems-integration.md +43 -0
  57. package/templates/agents/generic/code-reviewer.md +39 -0
  58. package/templates/agents/generic/docs-writer.md +25 -0
  59. package/templates/agents/generic/project-manager.md +34 -0
  60. package/templates/agents/generic/researcher.md +34 -0
  61. package/templates/agents/generic/test-engineer.md +40 -0
  62. package/templates/agents/mobile/platform-specialist.md +22 -0
  63. package/templates/agents/mobile/ui-engineer.md +22 -0
  64. package/templates/agents/web-app/api-designer.md +22 -0
  65. package/templates/agents/web-app/backend-engineer.md +30 -0
  66. package/templates/agents/web-app/database-engineer.md +22 -0
  67. package/templates/agents/web-app/frontend-engineer.md +29 -0
  68. package/templates/agents/web-app/security-reviewer.md +32 -0
  69. package/templates/core/AGENT_AUTOMATION.md +8 -0
  70. package/templates/core/RULEBOOK.md +12 -0
  71. package/templates/core/TIER1_PROHIBITIONS.md +154 -0
  72. package/templates/core/TOKEN_OPTIMIZATION.md +49 -0
  73. package/templates/git/GIT_WORKFLOW.md +35 -0
  74. package/templates/rules/follow-task-sequence.md +36 -0
  75. package/templates/rules/git-safety.md +29 -0
  76. package/templates/rules/incremental-tests.md +29 -0
  77. package/templates/rules/no-deferred.md +31 -0
  78. package/templates/rules/no-shortcuts.md +30 -0
  79. package/templates/rules/research-first.md +30 -0
  80. package/templates/rules/sequential-editing.md +21 -0
  81. package/templates/rules/session-workflow.md +24 -0
  82. package/templates/rules/task-decomposition.md +32 -0
@@ -17,7 +17,13 @@ function withTimeout(promise, ms = MCP_TOOL_TIMEOUT_MS, label = 'tool') {
17
17
  const timer = setTimeout(() => {
18
18
  reject(new Error(`[rulebook-mcp] ${label} timed out after ${ms}ms`));
19
19
  }, ms);
20
- promise.then((val) => { clearTimeout(timer); resolve(val); }, (err) => { clearTimeout(timer); reject(err); });
20
+ promise.then((val) => {
21
+ clearTimeout(timer);
22
+ resolve(val);
23
+ }, (err) => {
24
+ clearTimeout(timer);
25
+ reject(err);
26
+ });
21
27
  });
22
28
  }
23
29
  // Find .rulebook file/directory by walking up directories
@@ -152,7 +158,7 @@ export async function startRulebookMcpServer() {
152
158
  }
153
159
  const server = new McpServer({
154
160
  name: 'rulebook-task-management',
155
- version: '4.4.1',
161
+ version: '5.0.0',
156
162
  });
157
163
  // --- Wrap all tool handlers with timeout guard ---
158
164
  // Intercept registerTool to automatically add timeout protection to every handler.
@@ -166,7 +172,9 @@ export async function startRulebookMcpServer() {
166
172
  const msg = error instanceof Error ? error.message : String(error);
167
173
  console.error(`[rulebook-mcp] ${name} error: ${msg}`);
168
174
  return {
169
- content: [{ type: 'text', text: JSON.stringify({ success: false, error: msg }) }],
175
+ content: [
176
+ { type: 'text', text: JSON.stringify({ success: false, error: msg }) },
177
+ ],
170
178
  };
171
179
  }
172
180
  };
@@ -1046,12 +1054,9 @@ export async function startRulebookMcpServer() {
1046
1054
  ],
1047
1055
  };
1048
1056
  }
1049
- // Ensure lock is released on exit, even on crashes
1050
- const cleanupLock = async () => {
1051
- await ralphManager.releaseLock();
1052
- };
1053
- process.on('SIGTERM', cleanupLock);
1054
- process.on('SIGINT', cleanupLock);
1057
+ // Lock cleanup is handled by the top-level SIGINT handler (line ~858)
1058
+ // which shuts down all managers. Adding per-call SIGINT/SIGTERM listeners
1059
+ // causes accumulation and race conditions with the server shutdown handler.
1055
1060
  try {
1056
1061
  // Validate tool is available before starting
1057
1062
  const toolCmdNames = {
@@ -1312,8 +1317,6 @@ export async function startRulebookMcpServer() {
1312
1317
  finally {
1313
1318
  // Always release lock, even on error
1314
1319
  await ralphManager.releaseLock();
1315
- process.removeListener('SIGTERM', cleanupLock);
1316
- process.removeListener('SIGINT', cleanupLock);
1317
1320
  }
1318
1321
  }
1319
1322
  catch (error) {
@@ -1724,7 +1727,10 @@ export async function startRulebookMcpServer() {
1724
1727
  content: [
1725
1728
  {
1726
1729
  type: 'text',
1727
- text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
1730
+ text: JSON.stringify({
1731
+ success: false,
1732
+ error: error instanceof Error ? error.message : String(error),
1733
+ }),
1728
1734
  },
1729
1735
  ],
1730
1736
  };
@@ -1735,7 +1741,10 @@ export async function startRulebookMcpServer() {
1735
1741
  title: 'List Decision Records',
1736
1742
  description: 'List all architectural decision records',
1737
1743
  inputSchema: {
1738
- status: z.string().optional().describe('Filter by status (proposed, accepted, rejected, superseded, deprecated)'),
1744
+ status: z
1745
+ .string()
1746
+ .optional()
1747
+ .describe('Filter by status (proposed, accepted, rejected, superseded, deprecated)'),
1739
1748
  projectId: projectIdSchema,
1740
1749
  },
1741
1750
  }, async (args) => {
@@ -1760,7 +1769,10 @@ export async function startRulebookMcpServer() {
1760
1769
  content: [
1761
1770
  {
1762
1771
  type: 'text',
1763
- text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
1772
+ text: JSON.stringify({
1773
+ success: false,
1774
+ error: error instanceof Error ? error.message : String(error),
1775
+ }),
1764
1776
  },
1765
1777
  ],
1766
1778
  };
@@ -1796,7 +1808,11 @@ export async function startRulebookMcpServer() {
1796
1808
  content: [
1797
1809
  {
1798
1810
  type: 'text',
1799
- text: JSON.stringify({ success: true, decision: result.decision, content: result.content }),
1811
+ text: JSON.stringify({
1812
+ success: true,
1813
+ decision: result.decision,
1814
+ content: result.content,
1815
+ }),
1800
1816
  },
1801
1817
  ],
1802
1818
  };
@@ -1806,7 +1822,10 @@ export async function startRulebookMcpServer() {
1806
1822
  content: [
1807
1823
  {
1808
1824
  type: 'text',
1809
- text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
1825
+ text: JSON.stringify({
1826
+ success: false,
1827
+ error: error instanceof Error ? error.message : String(error),
1828
+ }),
1810
1829
  },
1811
1830
  ],
1812
1831
  };
@@ -1818,7 +1837,10 @@ export async function startRulebookMcpServer() {
1818
1837
  description: 'Update an existing architectural decision record',
1819
1838
  inputSchema: {
1820
1839
  id: z.number().describe('Decision ID to update'),
1821
- status: z.string().optional().describe('New status (proposed, accepted, rejected, superseded, deprecated)'),
1840
+ status: z
1841
+ .string()
1842
+ .optional()
1843
+ .describe('New status (proposed, accepted, rejected, superseded, deprecated)'),
1822
1844
  context: z.string().optional().describe('Updated context'),
1823
1845
  decision: z.string().optional().describe('Updated decision text'),
1824
1846
  projectId: projectIdSchema,
@@ -1859,7 +1881,10 @@ export async function startRulebookMcpServer() {
1859
1881
  content: [
1860
1882
  {
1861
1883
  type: 'text',
1862
- text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
1884
+ text: JSON.stringify({
1885
+ success: false,
1886
+ error: error instanceof Error ? error.message : String(error),
1887
+ }),
1863
1888
  },
1864
1889
  ],
1865
1890
  };
@@ -1909,7 +1934,10 @@ export async function startRulebookMcpServer() {
1909
1934
  content: [
1910
1935
  {
1911
1936
  type: 'text',
1912
- text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
1937
+ text: JSON.stringify({
1938
+ success: false,
1939
+ error: error instanceof Error ? error.message : String(error),
1940
+ }),
1913
1941
  },
1914
1942
  ],
1915
1943
  };
@@ -1946,7 +1974,10 @@ export async function startRulebookMcpServer() {
1946
1974
  content: [
1947
1975
  {
1948
1976
  type: 'text',
1949
- text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
1977
+ text: JSON.stringify({
1978
+ success: false,
1979
+ error: error instanceof Error ? error.message : String(error),
1980
+ }),
1950
1981
  },
1951
1982
  ],
1952
1983
  };
@@ -1973,7 +2004,10 @@ export async function startRulebookMcpServer() {
1973
2004
  content: [
1974
2005
  {
1975
2006
  type: 'text',
1976
- text: JSON.stringify({ success: false, error: `Knowledge entry "${args.id}" not found` }),
2007
+ text: JSON.stringify({
2008
+ success: false,
2009
+ error: `Knowledge entry "${args.id}" not found`,
2010
+ }),
1977
2011
  },
1978
2012
  ],
1979
2013
  };
@@ -1992,7 +2026,10 @@ export async function startRulebookMcpServer() {
1992
2026
  content: [
1993
2027
  {
1994
2028
  type: 'text',
1995
- text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
2029
+ text: JSON.stringify({
2030
+ success: false,
2031
+ error: error instanceof Error ? error.message : String(error),
2032
+ }),
1996
2033
  },
1997
2034
  ],
1998
2035
  };
@@ -2034,7 +2071,10 @@ export async function startRulebookMcpServer() {
2034
2071
  content: [
2035
2072
  {
2036
2073
  type: 'text',
2037
- text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
2074
+ text: JSON.stringify({
2075
+ success: false,
2076
+ error: error instanceof Error ? error.message : String(error),
2077
+ }),
2038
2078
  },
2039
2079
  ],
2040
2080
  };
@@ -2070,7 +2110,10 @@ export async function startRulebookMcpServer() {
2070
2110
  content: [
2071
2111
  {
2072
2112
  type: 'text',
2073
- text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
2113
+ text: JSON.stringify({
2114
+ success: false,
2115
+ error: error instanceof Error ? error.message : String(error),
2116
+ }),
2074
2117
  },
2075
2118
  ],
2076
2119
  };
@@ -2082,7 +2125,9 @@ export async function startRulebookMcpServer() {
2082
2125
  description: 'Promote a learning to a knowledge entry or decision record',
2083
2126
  inputSchema: {
2084
2127
  id: z.string().describe('Learning ID to promote'),
2085
- target: z.enum(['knowledge', 'decision']).describe('Promote to knowledge base or decision record'),
2128
+ target: z
2129
+ .enum(['knowledge', 'decision'])
2130
+ .describe('Promote to knowledge base or decision record'),
2086
2131
  title: z.string().optional().describe('Override title for the promoted entry'),
2087
2132
  projectId: projectIdSchema,
2088
2133
  },
@@ -2118,7 +2163,214 @@ export async function startRulebookMcpServer() {
2118
2163
  content: [
2119
2164
  {
2120
2165
  type: 'text',
2121
- text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
2166
+ text: JSON.stringify({
2167
+ success: false,
2168
+ error: error instanceof Error ? error.message : String(error),
2169
+ }),
2170
+ },
2171
+ ],
2172
+ };
2173
+ }
2174
+ });
2175
+ // ── v5.0 Tools: Session Management, Rules, Blockers ──────────────────
2176
+ // Register tool: rulebook_session_start
2177
+ server.registerTool('rulebook_session_start', {
2178
+ title: 'Start Session',
2179
+ description: 'Load session context: reads PLANS.md and searches relevant memories. Call at the start of every session.',
2180
+ inputSchema: {
2181
+ query: z
2182
+ .string()
2183
+ .optional()
2184
+ .describe('Optional search query to find relevant past memories'),
2185
+ projectId: projectIdSchema,
2186
+ },
2187
+ }, async (args) => {
2188
+ try {
2189
+ const root = args.projectId && workspaceManager
2190
+ ? (await workspaceManager.getWorker(args.projectId)).projectRoot
2191
+ : projectRoot;
2192
+ const { join } = await import('path');
2193
+ const { existsSync } = await import('fs');
2194
+ const { readFile } = await import('fs/promises');
2195
+ const result = { plans: null, memories: [] };
2196
+ // Read PLANS.md
2197
+ const plansPath = join(root, '.rulebook', 'PLANS.md');
2198
+ if (existsSync(plansPath)) {
2199
+ result.plans = await readFile(plansPath, 'utf-8');
2200
+ }
2201
+ // Search relevant memories
2202
+ if (args.query && memoryManager) {
2203
+ const searchResults = await memoryManager.searchMemories({
2204
+ query: args.query,
2205
+ mode: 'hybrid',
2206
+ limit: 5,
2207
+ });
2208
+ result.memories = searchResults;
2209
+ }
2210
+ return {
2211
+ content: [{ type: 'text', text: JSON.stringify({ success: true, ...result }) }],
2212
+ };
2213
+ }
2214
+ catch (error) {
2215
+ return {
2216
+ content: [
2217
+ {
2218
+ type: 'text',
2219
+ text: JSON.stringify({
2220
+ success: false,
2221
+ error: error instanceof Error ? error.message : String(error),
2222
+ }),
2223
+ },
2224
+ ],
2225
+ };
2226
+ }
2227
+ });
2228
+ // Register tool: rulebook_session_end
2229
+ server.registerTool('rulebook_session_end', {
2230
+ title: 'End Session',
2231
+ description: 'Save session summary to PLANS.md history section. Call at the end of every session.',
2232
+ inputSchema: {
2233
+ summary: z.string().describe('Session summary: what was accomplished, key decisions, next steps'),
2234
+ projectId: projectIdSchema,
2235
+ },
2236
+ }, async (args) => {
2237
+ try {
2238
+ const root = args.projectId && workspaceManager
2239
+ ? (await workspaceManager.getWorker(args.projectId)).projectRoot
2240
+ : projectRoot;
2241
+ const { join } = await import('path');
2242
+ const { existsSync } = await import('fs');
2243
+ const { readFile, writeFile } = await import('fs/promises');
2244
+ const plansPath = join(root, '.rulebook', 'PLANS.md');
2245
+ const date = new Date().toISOString().split('T')[0];
2246
+ const entry = `### ${date}\n${args.summary}\n`;
2247
+ if (existsSync(plansPath)) {
2248
+ let content = await readFile(plansPath, 'utf-8');
2249
+ // Insert after <!-- PLANS:HISTORY:START -->
2250
+ if (content.includes('<!-- PLANS:HISTORY:START -->')) {
2251
+ content = content.replace('<!-- PLANS:HISTORY:START -->', `<!-- PLANS:HISTORY:START -->\n${entry}`);
2252
+ }
2253
+ else {
2254
+ content += `\n## Session History\n\n<!-- PLANS:HISTORY:START -->\n${entry}<!-- PLANS:HISTORY:END -->\n`;
2255
+ }
2256
+ await writeFile(plansPath, content, 'utf-8');
2257
+ }
2258
+ else {
2259
+ // Create PLANS.md from scratch
2260
+ const newContent = `# Project Plans & Session Context\n\n<!-- PLANS:CONTEXT:START -->\n_No active context._\n<!-- PLANS:CONTEXT:END -->\n\n<!-- PLANS:TASK:START -->\n_No task in progress._\n<!-- PLANS:TASK:END -->\n\n## Session History\n\n<!-- PLANS:HISTORY:START -->\n${entry}<!-- PLANS:HISTORY:END -->\n`;
2261
+ const { mkdirSync } = await import('fs');
2262
+ const dir = join(root, '.rulebook');
2263
+ if (!existsSync(dir))
2264
+ mkdirSync(dir, { recursive: true });
2265
+ await writeFile(plansPath, newContent, 'utf-8');
2266
+ }
2267
+ // Also save to memory if available
2268
+ if (memoryManager) {
2269
+ await memoryManager.saveMemory({
2270
+ type: 'observation',
2271
+ title: `Session summary ${date}`,
2272
+ content: args.summary,
2273
+ tags: ['session', 'summary'],
2274
+ });
2275
+ }
2276
+ return {
2277
+ content: [
2278
+ { type: 'text', text: JSON.stringify({ success: true, message: 'Session summary saved to PLANS.md' }) },
2279
+ ],
2280
+ };
2281
+ }
2282
+ catch (error) {
2283
+ return {
2284
+ content: [
2285
+ {
2286
+ type: 'text',
2287
+ text: JSON.stringify({
2288
+ success: false,
2289
+ error: error instanceof Error ? error.message : String(error),
2290
+ }),
2291
+ },
2292
+ ],
2293
+ };
2294
+ }
2295
+ });
2296
+ // Register tool: rulebook_rules_list
2297
+ server.registerTool('rulebook_rules_list', {
2298
+ title: 'List Rules',
2299
+ description: 'List all canonical rules from .rulebook/rules/ with tier and tool targeting info',
2300
+ inputSchema: {
2301
+ projectId: projectIdSchema,
2302
+ },
2303
+ }, async (args) => {
2304
+ try {
2305
+ const root = args.projectId && workspaceManager
2306
+ ? (await workspaceManager.getWorker(args.projectId)).projectRoot
2307
+ : projectRoot;
2308
+ const { listRules } = await import('../core/rule-engine.js');
2309
+ const rules = await listRules(root);
2310
+ return {
2311
+ content: [{ type: 'text', text: JSON.stringify({ success: true, rules, count: rules.length }) }],
2312
+ };
2313
+ }
2314
+ catch (error) {
2315
+ return {
2316
+ content: [
2317
+ {
2318
+ type: 'text',
2319
+ text: JSON.stringify({
2320
+ success: false,
2321
+ error: error instanceof Error ? error.message : String(error),
2322
+ }),
2323
+ },
2324
+ ],
2325
+ };
2326
+ }
2327
+ });
2328
+ // Register tool: rulebook_blockers
2329
+ server.registerTool('rulebook_blockers', {
2330
+ title: 'Show Blockers',
2331
+ description: 'Show task blocker chain with cascade impact analysis',
2332
+ inputSchema: {
2333
+ projectId: projectIdSchema,
2334
+ },
2335
+ }, async (args) => {
2336
+ try {
2337
+ const tm = await getTaskMgr(args.projectId);
2338
+ const tasks = await tm.listTasks();
2339
+ // Build blocker chain from task metadata
2340
+ const blockers = [];
2341
+ for (const task of tasks) {
2342
+ const metadata = await tm.getTaskMetadata(task.id);
2343
+ const blocks = Array.isArray(metadata?.blocks) ? metadata.blocks : [];
2344
+ const blockedBy = Array.isArray(metadata?.blockedBy) ? metadata.blockedBy : [];
2345
+ if (blocks.length > 0 || blockedBy.length > 0) {
2346
+ blockers.push({
2347
+ taskId: task.id,
2348
+ blocks,
2349
+ blockedBy,
2350
+ cascadeImpact: metadata?.cascadeImpact || blocks.length,
2351
+ });
2352
+ }
2353
+ }
2354
+ // Sort by cascade impact (highest first)
2355
+ blockers.sort((a, b) => b.cascadeImpact - a.cascadeImpact);
2356
+ return {
2357
+ content: [
2358
+ {
2359
+ type: 'text',
2360
+ text: JSON.stringify({ success: true, blockers, count: blockers.length }),
2361
+ },
2362
+ ],
2363
+ };
2364
+ }
2365
+ catch (error) {
2366
+ return {
2367
+ content: [
2368
+ {
2369
+ type: 'text',
2370
+ text: JSON.stringify({
2371
+ success: false,
2372
+ error: error instanceof Error ? error.message : String(error),
2373
+ }),
2122
2374
  },
2123
2375
  ],
2124
2376
  };