@hivehub/rulebook 4.3.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.
- package/.claude/commands/rulebook-decision-create.md +55 -0
- package/.claude/commands/rulebook-decision-list.md +15 -0
- package/.claude/commands/rulebook-knowledge-add.md +41 -0
- package/.claude/commands/rulebook-knowledge-list.md +15 -0
- package/.claude/commands/rulebook-learn-capture.md +48 -0
- package/.claude/commands/rulebook-learn-list.md +13 -0
- package/.claude/commands/rulebook-task-archive.md +24 -0
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +53 -10
- package/dist/cli/commands.d.ts +32 -0
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +257 -0
- package/dist/cli/commands.js.map +1 -1
- package/dist/core/agent-template-engine.d.ts +51 -0
- package/dist/core/agent-template-engine.d.ts.map +1 -0
- package/dist/core/agent-template-engine.js +273 -0
- package/dist/core/agent-template-engine.js.map +1 -0
- package/dist/core/complexity-detector.d.ts +36 -0
- package/dist/core/complexity-detector.d.ts.map +1 -0
- package/dist/core/complexity-detector.js +254 -0
- package/dist/core/complexity-detector.js.map +1 -0
- package/dist/core/decision-manager.d.ts +25 -0
- package/dist/core/decision-manager.d.ts.map +1 -0
- package/dist/core/decision-manager.js +188 -0
- package/dist/core/decision-manager.js.map +1 -0
- package/dist/core/generator.d.ts +1 -0
- package/dist/core/generator.d.ts.map +1 -1
- package/dist/core/generator.js +47 -3
- package/dist/core/generator.js.map +1 -1
- package/dist/core/indexer/background-indexer.d.ts +9 -2
- package/dist/core/indexer/background-indexer.d.ts.map +1 -1
- package/dist/core/indexer/background-indexer.js +99 -83
- package/dist/core/indexer/background-indexer.js.map +1 -1
- package/dist/core/knowledge-manager.d.ts +24 -0
- package/dist/core/knowledge-manager.d.ts.map +1 -0
- package/dist/core/knowledge-manager.js +173 -0
- package/dist/core/knowledge-manager.js.map +1 -0
- package/dist/core/learn-manager.d.ts +29 -0
- package/dist/core/learn-manager.d.ts.map +1 -0
- package/dist/core/learn-manager.js +159 -0
- package/dist/core/learn-manager.js.map +1 -0
- package/dist/core/rule-engine.d.ts +64 -0
- package/dist/core/rule-engine.d.ts.map +1 -0
- package/dist/core/rule-engine.js +333 -0
- package/dist/core/rule-engine.js.map +1 -0
- package/dist/core/task-manager.d.ts +4 -0
- package/dist/core/task-manager.d.ts.map +1 -1
- package/dist/core/task-manager.js +39 -24
- package/dist/core/task-manager.js.map +1 -1
- package/dist/core/workspace/project-worker.d.ts +9 -0
- package/dist/core/workspace/project-worker.d.ts.map +1 -1
- package/dist/core/workspace/project-worker.js +27 -0
- package/dist/core/workspace/project-worker.js.map +1 -1
- package/dist/index.js +250 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/rulebook-server.d.ts.map +1 -1
- package/dist/mcp/rulebook-server.js +758 -22
- package/dist/mcp/rulebook-server.js.map +1 -1
- package/dist/memory/hnsw-index.d.ts +3 -1
- package/dist/memory/hnsw-index.d.ts.map +1 -1
- package/dist/memory/hnsw-index.js +121 -18
- package/dist/memory/hnsw-index.js.map +1 -1
- package/dist/memory/memory-manager.d.ts +5 -0
- package/dist/memory/memory-manager.d.ts.map +1 -1
- package/dist/memory/memory-manager.js +34 -8
- package/dist/memory/memory-manager.js.map +1 -1
- package/dist/memory/memory-store.d.ts +15 -3
- package/dist/memory/memory-store.d.ts.map +1 -1
- package/dist/memory/memory-store.js +327 -272
- package/dist/memory/memory-store.js.map +1 -1
- package/dist/types.d.ts +60 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -3
- package/templates/agents/compiler/codegen-debugger.md +34 -0
- package/templates/agents/compiler/stdlib-engineer.md +28 -0
- package/templates/agents/compiler/test-coverage-guardian.md +31 -0
- package/templates/agents/context-intelligence.md +52 -0
- package/templates/agents/game-engine/cpp-core-expert.md +35 -0
- package/templates/agents/game-engine/render-engineer.md +22 -0
- package/templates/agents/game-engine/shader-engineer.md +38 -0
- package/templates/agents/game-engine/systems-integration.md +43 -0
- package/templates/agents/generic/code-reviewer.md +39 -0
- package/templates/agents/generic/docs-writer.md +25 -0
- package/templates/agents/generic/project-manager.md +34 -0
- package/templates/agents/generic/researcher.md +34 -0
- package/templates/agents/generic/test-engineer.md +40 -0
- package/templates/agents/mobile/platform-specialist.md +22 -0
- package/templates/agents/mobile/ui-engineer.md +22 -0
- package/templates/agents/web-app/api-designer.md +22 -0
- package/templates/agents/web-app/backend-engineer.md +30 -0
- package/templates/agents/web-app/database-engineer.md +22 -0
- package/templates/agents/web-app/frontend-engineer.md +29 -0
- package/templates/agents/web-app/security-reviewer.md +32 -0
- package/templates/commands/rulebook-decision-create.md +55 -0
- package/templates/commands/rulebook-decision-list.md +15 -0
- package/templates/commands/rulebook-knowledge-add.md +41 -0
- package/templates/commands/rulebook-knowledge-list.md +15 -0
- package/templates/commands/rulebook-learn-capture.md +48 -0
- package/templates/commands/rulebook-learn-list.md +13 -0
- package/templates/core/AGENT_AUTOMATION.md +8 -0
- package/templates/core/DECISIONS.md +38 -0
- package/templates/core/KNOWLEDGE.md +49 -0
- package/templates/core/RULEBOOK.md +12 -0
- package/templates/core/TIER1_PROHIBITIONS.md +154 -0
- package/templates/core/TOKEN_OPTIMIZATION.md +49 -0
- package/templates/git/GIT_WORKFLOW.md +35 -0
- package/templates/rules/follow-task-sequence.md +36 -0
- package/templates/rules/git-safety.md +29 -0
- package/templates/rules/incremental-tests.md +29 -0
- package/templates/rules/no-deferred.md +31 -0
- package/templates/rules/no-shortcuts.md +30 -0
- package/templates/rules/research-first.md +30 -0
- package/templates/rules/sequential-editing.md +21 -0
- package/templates/rules/session-workflow.md +24 -0
- package/templates/rules/task-decomposition.md +32 -0
|
@@ -9,6 +9,23 @@ 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
11
|
import { WorkspaceManager } from '../core/workspace/workspace-manager.js';
|
|
12
|
+
// --- Timeout guard for MCP tool handlers ---
|
|
13
|
+
// Prevents the MCP server from hanging when a tool handler blocks (SQLite, WASM, fs).
|
|
14
|
+
const MCP_TOOL_TIMEOUT_MS = parseInt(process.env.RULEBOOK_MCP_TIMEOUT_MS ?? '10000', 10);
|
|
15
|
+
function withTimeout(promise, ms = MCP_TOOL_TIMEOUT_MS, label = 'tool') {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const timer = setTimeout(() => {
|
|
18
|
+
reject(new Error(`[rulebook-mcp] ${label} timed out after ${ms}ms`));
|
|
19
|
+
}, ms);
|
|
20
|
+
promise.then((val) => {
|
|
21
|
+
clearTimeout(timer);
|
|
22
|
+
resolve(val);
|
|
23
|
+
}, (err) => {
|
|
24
|
+
clearTimeout(timer);
|
|
25
|
+
reject(err);
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
12
29
|
// Find .rulebook file/directory by walking up directories
|
|
13
30
|
export function findRulebookConfig(startDir) {
|
|
14
31
|
let current = resolve(startDir);
|
|
@@ -141,7 +158,27 @@ export async function startRulebookMcpServer() {
|
|
|
141
158
|
}
|
|
142
159
|
const server = new McpServer({
|
|
143
160
|
name: 'rulebook-task-management',
|
|
144
|
-
version: '
|
|
161
|
+
version: '5.0.0',
|
|
162
|
+
});
|
|
163
|
+
// --- Wrap all tool handlers with timeout guard ---
|
|
164
|
+
// Intercept registerTool to automatically add timeout protection to every handler.
|
|
165
|
+
const originalRegisterTool = server.registerTool.bind(server);
|
|
166
|
+
server.registerTool = ((name, config, handler) => {
|
|
167
|
+
const wrappedHandler = async (...handlerArgs) => {
|
|
168
|
+
try {
|
|
169
|
+
return await withTimeout(handler(...handlerArgs), MCP_TOOL_TIMEOUT_MS, name);
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
173
|
+
console.error(`[rulebook-mcp] ${name} error: ${msg}`);
|
|
174
|
+
return {
|
|
175
|
+
content: [
|
|
176
|
+
{ type: 'text', text: JSON.stringify({ success: false, error: msg }) },
|
|
177
|
+
],
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
return originalRegisterTool(name, config, wrappedHandler);
|
|
145
182
|
});
|
|
146
183
|
// Zod schema reused across tools for workspace project targeting
|
|
147
184
|
const projectIdSchema = z
|
|
@@ -672,9 +709,16 @@ export async function startRulebookMcpServer() {
|
|
|
672
709
|
console.error(`[rulebook-mcp] Memory DB: ${memoryDbPath}`);
|
|
673
710
|
memoryManager = createMemoryManager(projectRoot, rulebookConfig.memory);
|
|
674
711
|
autoCaptureEnabled = rulebookConfig.memory.autoCapture !== false;
|
|
675
|
-
// Boot Background Indexer
|
|
712
|
+
// Boot Background Indexer — deferred to avoid blocking server startup
|
|
676
713
|
bgIndexer = new BackgroundIndexer(memoryManager, projectRoot, { enabled: true });
|
|
677
|
-
|
|
714
|
+
setTimeout(() => {
|
|
715
|
+
try {
|
|
716
|
+
bgIndexer?.start();
|
|
717
|
+
}
|
|
718
|
+
catch (e) {
|
|
719
|
+
console.error('[rulebook-mcp] BackgroundIndexer start failed:', e);
|
|
720
|
+
}
|
|
721
|
+
}, 5000);
|
|
678
722
|
global.__indexerStatus = () => bgIndexer?.getStatus();
|
|
679
723
|
}
|
|
680
724
|
catch (e) {
|
|
@@ -697,24 +741,28 @@ export async function startRulebookMcpServer() {
|
|
|
697
741
|
/**
|
|
698
742
|
* Auto-capture: save tool interactions to memory in the background.
|
|
699
743
|
* Fire-and-forget — never blocks or fails the original tool call.
|
|
744
|
+
* Has its own 2s timeout to prevent hanging the event loop.
|
|
700
745
|
*/
|
|
746
|
+
const AUTO_CAPTURE_TIMEOUT_MS = 2000;
|
|
701
747
|
async function autoCapture(toolName, args, resultText) {
|
|
702
748
|
if (!memoryManager || !autoCaptureEnabled)
|
|
703
749
|
return;
|
|
704
750
|
try {
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
751
|
+
await withTimeout((async () => {
|
|
752
|
+
const { captureFromToolCall } = await import('../memory/memory-hooks.js');
|
|
753
|
+
const captured = captureFromToolCall(toolName, args, resultText);
|
|
754
|
+
if (!captured)
|
|
755
|
+
return;
|
|
756
|
+
await memoryManager.saveMemory({
|
|
757
|
+
type: captured.type,
|
|
758
|
+
title: captured.title,
|
|
759
|
+
content: captured.content,
|
|
760
|
+
tags: captured.tags,
|
|
761
|
+
});
|
|
762
|
+
})(), AUTO_CAPTURE_TIMEOUT_MS, `autoCapture(${toolName})`);
|
|
715
763
|
}
|
|
716
764
|
catch {
|
|
717
|
-
// Never fail the original tool call
|
|
765
|
+
// Never fail the original tool call — timeout or error is silently dropped
|
|
718
766
|
}
|
|
719
767
|
}
|
|
720
768
|
function memoryNotEnabled() {
|
|
@@ -1006,12 +1054,9 @@ export async function startRulebookMcpServer() {
|
|
|
1006
1054
|
],
|
|
1007
1055
|
};
|
|
1008
1056
|
}
|
|
1009
|
-
//
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
};
|
|
1013
|
-
process.on('SIGTERM', cleanupLock);
|
|
1014
|
-
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.
|
|
1015
1060
|
try {
|
|
1016
1061
|
// Validate tool is available before starting
|
|
1017
1062
|
const toolCmdNames = {
|
|
@@ -1272,8 +1317,6 @@ export async function startRulebookMcpServer() {
|
|
|
1272
1317
|
finally {
|
|
1273
1318
|
// Always release lock, even on error
|
|
1274
1319
|
await ralphManager.releaseLock();
|
|
1275
|
-
process.removeListener('SIGTERM', cleanupLock);
|
|
1276
|
-
process.removeListener('SIGINT', cleanupLock);
|
|
1277
1320
|
}
|
|
1278
1321
|
}
|
|
1279
1322
|
catch (error) {
|
|
@@ -1640,6 +1683,699 @@ export async function startRulebookMcpServer() {
|
|
|
1640
1683
|
}
|
|
1641
1684
|
});
|
|
1642
1685
|
}
|
|
1686
|
+
// ============================================
|
|
1687
|
+
// Context Intelligence Layer Tools (v4.4)
|
|
1688
|
+
// ============================================
|
|
1689
|
+
// Register tool: rulebook_decision_create
|
|
1690
|
+
server.registerTool('rulebook_decision_create', {
|
|
1691
|
+
title: 'Create Decision Record',
|
|
1692
|
+
description: 'Create a new architectural decision record (ADR)',
|
|
1693
|
+
inputSchema: {
|
|
1694
|
+
title: z.string().describe('Decision title'),
|
|
1695
|
+
context: z.string().optional().describe('Context and problem statement'),
|
|
1696
|
+
decision: z.string().optional().describe('The decision made'),
|
|
1697
|
+
alternatives: z.array(z.string()).optional().describe('Alternatives considered'),
|
|
1698
|
+
consequences: z.string().optional().describe('Consequences and tradeoffs'),
|
|
1699
|
+
relatedTasks: z.array(z.string()).optional().describe('Related task IDs'),
|
|
1700
|
+
projectId: projectIdSchema,
|
|
1701
|
+
},
|
|
1702
|
+
}, async (args) => {
|
|
1703
|
+
try {
|
|
1704
|
+
const root = args.projectId && workspaceManager
|
|
1705
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1706
|
+
: projectRoot;
|
|
1707
|
+
const { DecisionManager } = await import('../core/decision-manager.js');
|
|
1708
|
+
const dm = new DecisionManager(root);
|
|
1709
|
+
const decision = await dm.create(args.title, {
|
|
1710
|
+
context: args.context,
|
|
1711
|
+
decision: args.decision,
|
|
1712
|
+
alternatives: args.alternatives,
|
|
1713
|
+
consequences: args.consequences,
|
|
1714
|
+
relatedTasks: args.relatedTasks,
|
|
1715
|
+
});
|
|
1716
|
+
return {
|
|
1717
|
+
content: [
|
|
1718
|
+
{
|
|
1719
|
+
type: 'text',
|
|
1720
|
+
text: JSON.stringify({ success: true, decision }),
|
|
1721
|
+
},
|
|
1722
|
+
],
|
|
1723
|
+
};
|
|
1724
|
+
}
|
|
1725
|
+
catch (error) {
|
|
1726
|
+
return {
|
|
1727
|
+
content: [
|
|
1728
|
+
{
|
|
1729
|
+
type: 'text',
|
|
1730
|
+
text: JSON.stringify({
|
|
1731
|
+
success: false,
|
|
1732
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1733
|
+
}),
|
|
1734
|
+
},
|
|
1735
|
+
],
|
|
1736
|
+
};
|
|
1737
|
+
}
|
|
1738
|
+
});
|
|
1739
|
+
// Register tool: rulebook_decision_list
|
|
1740
|
+
server.registerTool('rulebook_decision_list', {
|
|
1741
|
+
title: 'List Decision Records',
|
|
1742
|
+
description: 'List all architectural decision records',
|
|
1743
|
+
inputSchema: {
|
|
1744
|
+
status: z
|
|
1745
|
+
.string()
|
|
1746
|
+
.optional()
|
|
1747
|
+
.describe('Filter by status (proposed, accepted, rejected, superseded, deprecated)'),
|
|
1748
|
+
projectId: projectIdSchema,
|
|
1749
|
+
},
|
|
1750
|
+
}, async (args) => {
|
|
1751
|
+
try {
|
|
1752
|
+
const root = args.projectId && workspaceManager
|
|
1753
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1754
|
+
: projectRoot;
|
|
1755
|
+
const { DecisionManager } = await import('../core/decision-manager.js');
|
|
1756
|
+
const dm = new DecisionManager(root);
|
|
1757
|
+
const decisions = await dm.list(args.status);
|
|
1758
|
+
return {
|
|
1759
|
+
content: [
|
|
1760
|
+
{
|
|
1761
|
+
type: 'text',
|
|
1762
|
+
text: JSON.stringify({ success: true, decisions, count: decisions.length }),
|
|
1763
|
+
},
|
|
1764
|
+
],
|
|
1765
|
+
};
|
|
1766
|
+
}
|
|
1767
|
+
catch (error) {
|
|
1768
|
+
return {
|
|
1769
|
+
content: [
|
|
1770
|
+
{
|
|
1771
|
+
type: 'text',
|
|
1772
|
+
text: JSON.stringify({
|
|
1773
|
+
success: false,
|
|
1774
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1775
|
+
}),
|
|
1776
|
+
},
|
|
1777
|
+
],
|
|
1778
|
+
};
|
|
1779
|
+
}
|
|
1780
|
+
});
|
|
1781
|
+
// Register tool: rulebook_decision_show
|
|
1782
|
+
server.registerTool('rulebook_decision_show', {
|
|
1783
|
+
title: 'Show Decision Record',
|
|
1784
|
+
description: 'Show full details of a decision record',
|
|
1785
|
+
inputSchema: {
|
|
1786
|
+
id: z.number().describe('Decision ID'),
|
|
1787
|
+
projectId: projectIdSchema,
|
|
1788
|
+
},
|
|
1789
|
+
}, async (args) => {
|
|
1790
|
+
try {
|
|
1791
|
+
const root = args.projectId && workspaceManager
|
|
1792
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1793
|
+
: projectRoot;
|
|
1794
|
+
const { DecisionManager } = await import('../core/decision-manager.js');
|
|
1795
|
+
const dm = new DecisionManager(root);
|
|
1796
|
+
const result = await dm.show(args.id);
|
|
1797
|
+
if (!result) {
|
|
1798
|
+
return {
|
|
1799
|
+
content: [
|
|
1800
|
+
{
|
|
1801
|
+
type: 'text',
|
|
1802
|
+
text: JSON.stringify({ success: false, error: `Decision ${args.id} not found` }),
|
|
1803
|
+
},
|
|
1804
|
+
],
|
|
1805
|
+
};
|
|
1806
|
+
}
|
|
1807
|
+
return {
|
|
1808
|
+
content: [
|
|
1809
|
+
{
|
|
1810
|
+
type: 'text',
|
|
1811
|
+
text: JSON.stringify({
|
|
1812
|
+
success: true,
|
|
1813
|
+
decision: result.decision,
|
|
1814
|
+
content: result.content,
|
|
1815
|
+
}),
|
|
1816
|
+
},
|
|
1817
|
+
],
|
|
1818
|
+
};
|
|
1819
|
+
}
|
|
1820
|
+
catch (error) {
|
|
1821
|
+
return {
|
|
1822
|
+
content: [
|
|
1823
|
+
{
|
|
1824
|
+
type: 'text',
|
|
1825
|
+
text: JSON.stringify({
|
|
1826
|
+
success: false,
|
|
1827
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1828
|
+
}),
|
|
1829
|
+
},
|
|
1830
|
+
],
|
|
1831
|
+
};
|
|
1832
|
+
}
|
|
1833
|
+
});
|
|
1834
|
+
// Register tool: rulebook_decision_update
|
|
1835
|
+
server.registerTool('rulebook_decision_update', {
|
|
1836
|
+
title: 'Update Decision Record',
|
|
1837
|
+
description: 'Update an existing architectural decision record',
|
|
1838
|
+
inputSchema: {
|
|
1839
|
+
id: z.number().describe('Decision ID to update'),
|
|
1840
|
+
status: z
|
|
1841
|
+
.string()
|
|
1842
|
+
.optional()
|
|
1843
|
+
.describe('New status (proposed, accepted, rejected, superseded, deprecated)'),
|
|
1844
|
+
context: z.string().optional().describe('Updated context'),
|
|
1845
|
+
decision: z.string().optional().describe('Updated decision text'),
|
|
1846
|
+
projectId: projectIdSchema,
|
|
1847
|
+
},
|
|
1848
|
+
}, async (args) => {
|
|
1849
|
+
try {
|
|
1850
|
+
const root = args.projectId && workspaceManager
|
|
1851
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1852
|
+
: projectRoot;
|
|
1853
|
+
const { DecisionManager } = await import('../core/decision-manager.js');
|
|
1854
|
+
const dm = new DecisionManager(root);
|
|
1855
|
+
const updated = await dm.update(args.id, {
|
|
1856
|
+
status: args.status,
|
|
1857
|
+
context: args.context,
|
|
1858
|
+
decision: args.decision,
|
|
1859
|
+
});
|
|
1860
|
+
if (!updated) {
|
|
1861
|
+
return {
|
|
1862
|
+
content: [
|
|
1863
|
+
{
|
|
1864
|
+
type: 'text',
|
|
1865
|
+
text: JSON.stringify({ success: false, error: `Decision ${args.id} not found` }),
|
|
1866
|
+
},
|
|
1867
|
+
],
|
|
1868
|
+
};
|
|
1869
|
+
}
|
|
1870
|
+
return {
|
|
1871
|
+
content: [
|
|
1872
|
+
{
|
|
1873
|
+
type: 'text',
|
|
1874
|
+
text: JSON.stringify({ success: true, decision: updated }),
|
|
1875
|
+
},
|
|
1876
|
+
],
|
|
1877
|
+
};
|
|
1878
|
+
}
|
|
1879
|
+
catch (error) {
|
|
1880
|
+
return {
|
|
1881
|
+
content: [
|
|
1882
|
+
{
|
|
1883
|
+
type: 'text',
|
|
1884
|
+
text: JSON.stringify({
|
|
1885
|
+
success: false,
|
|
1886
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1887
|
+
}),
|
|
1888
|
+
},
|
|
1889
|
+
],
|
|
1890
|
+
};
|
|
1891
|
+
}
|
|
1892
|
+
});
|
|
1893
|
+
// Register tool: rulebook_knowledge_add
|
|
1894
|
+
server.registerTool('rulebook_knowledge_add', {
|
|
1895
|
+
title: 'Add Knowledge Entry',
|
|
1896
|
+
description: 'Add a new pattern or anti-pattern to the project knowledge base',
|
|
1897
|
+
inputSchema: {
|
|
1898
|
+
type: z.string().describe('Knowledge type: "pattern" or "anti-pattern"'),
|
|
1899
|
+
title: z.string().describe('Entry title'),
|
|
1900
|
+
category: z.string().describe('Category (e.g. code, architecture, testing, security)'),
|
|
1901
|
+
description: z.string().describe('Description of the pattern'),
|
|
1902
|
+
example: z.string().optional().describe('Code or usage example'),
|
|
1903
|
+
whenToUse: z.string().optional().describe('When to use this pattern'),
|
|
1904
|
+
whenNotToUse: z.string().optional().describe('When NOT to use this pattern'),
|
|
1905
|
+
tags: z.array(z.string()).optional().describe('Tags for search'),
|
|
1906
|
+
projectId: projectIdSchema,
|
|
1907
|
+
},
|
|
1908
|
+
}, async (args) => {
|
|
1909
|
+
try {
|
|
1910
|
+
const root = args.projectId && workspaceManager
|
|
1911
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1912
|
+
: projectRoot;
|
|
1913
|
+
const { KnowledgeManager } = await import('../core/knowledge-manager.js');
|
|
1914
|
+
const km = new KnowledgeManager(root);
|
|
1915
|
+
const entry = await km.add(args.type, args.title, {
|
|
1916
|
+
category: args.category,
|
|
1917
|
+
description: args.description,
|
|
1918
|
+
example: args.example,
|
|
1919
|
+
whenToUse: args.whenToUse,
|
|
1920
|
+
whenNotToUse: args.whenNotToUse,
|
|
1921
|
+
tags: args.tags,
|
|
1922
|
+
});
|
|
1923
|
+
return {
|
|
1924
|
+
content: [
|
|
1925
|
+
{
|
|
1926
|
+
type: 'text',
|
|
1927
|
+
text: JSON.stringify({ success: true, entry }),
|
|
1928
|
+
},
|
|
1929
|
+
],
|
|
1930
|
+
};
|
|
1931
|
+
}
|
|
1932
|
+
catch (error) {
|
|
1933
|
+
return {
|
|
1934
|
+
content: [
|
|
1935
|
+
{
|
|
1936
|
+
type: 'text',
|
|
1937
|
+
text: JSON.stringify({
|
|
1938
|
+
success: false,
|
|
1939
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1940
|
+
}),
|
|
1941
|
+
},
|
|
1942
|
+
],
|
|
1943
|
+
};
|
|
1944
|
+
}
|
|
1945
|
+
});
|
|
1946
|
+
// Register tool: rulebook_knowledge_list
|
|
1947
|
+
server.registerTool('rulebook_knowledge_list', {
|
|
1948
|
+
title: 'List Knowledge Entries',
|
|
1949
|
+
description: 'List patterns and anti-patterns in the project knowledge base',
|
|
1950
|
+
inputSchema: {
|
|
1951
|
+
type: z.string().optional().describe('Filter by type: "pattern" or "anti-pattern"'),
|
|
1952
|
+
category: z.string().optional().describe('Filter by category'),
|
|
1953
|
+
projectId: projectIdSchema,
|
|
1954
|
+
},
|
|
1955
|
+
}, async (args) => {
|
|
1956
|
+
try {
|
|
1957
|
+
const root = args.projectId && workspaceManager
|
|
1958
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1959
|
+
: projectRoot;
|
|
1960
|
+
const { KnowledgeManager } = await import('../core/knowledge-manager.js');
|
|
1961
|
+
const km = new KnowledgeManager(root);
|
|
1962
|
+
const entries = await km.list(args.type, args.category);
|
|
1963
|
+
return {
|
|
1964
|
+
content: [
|
|
1965
|
+
{
|
|
1966
|
+
type: 'text',
|
|
1967
|
+
text: JSON.stringify({ success: true, entries, count: entries.length }),
|
|
1968
|
+
},
|
|
1969
|
+
],
|
|
1970
|
+
};
|
|
1971
|
+
}
|
|
1972
|
+
catch (error) {
|
|
1973
|
+
return {
|
|
1974
|
+
content: [
|
|
1975
|
+
{
|
|
1976
|
+
type: 'text',
|
|
1977
|
+
text: JSON.stringify({
|
|
1978
|
+
success: false,
|
|
1979
|
+
error: error instanceof Error ? error.message : String(error),
|
|
1980
|
+
}),
|
|
1981
|
+
},
|
|
1982
|
+
],
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
});
|
|
1986
|
+
// Register tool: rulebook_knowledge_show
|
|
1987
|
+
server.registerTool('rulebook_knowledge_show', {
|
|
1988
|
+
title: 'Show Knowledge Entry',
|
|
1989
|
+
description: 'Show full details of a knowledge entry',
|
|
1990
|
+
inputSchema: {
|
|
1991
|
+
id: z.string().describe('Knowledge entry ID (slug)'),
|
|
1992
|
+
projectId: projectIdSchema,
|
|
1993
|
+
},
|
|
1994
|
+
}, async (args) => {
|
|
1995
|
+
try {
|
|
1996
|
+
const root = args.projectId && workspaceManager
|
|
1997
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1998
|
+
: projectRoot;
|
|
1999
|
+
const { KnowledgeManager } = await import('../core/knowledge-manager.js');
|
|
2000
|
+
const km = new KnowledgeManager(root);
|
|
2001
|
+
const result = await km.show(args.id);
|
|
2002
|
+
if (!result) {
|
|
2003
|
+
return {
|
|
2004
|
+
content: [
|
|
2005
|
+
{
|
|
2006
|
+
type: 'text',
|
|
2007
|
+
text: JSON.stringify({
|
|
2008
|
+
success: false,
|
|
2009
|
+
error: `Knowledge entry "${args.id}" not found`,
|
|
2010
|
+
}),
|
|
2011
|
+
},
|
|
2012
|
+
],
|
|
2013
|
+
};
|
|
2014
|
+
}
|
|
2015
|
+
return {
|
|
2016
|
+
content: [
|
|
2017
|
+
{
|
|
2018
|
+
type: 'text',
|
|
2019
|
+
text: JSON.stringify({ success: true, entry: result.entry, content: result.content }),
|
|
2020
|
+
},
|
|
2021
|
+
],
|
|
2022
|
+
};
|
|
2023
|
+
}
|
|
2024
|
+
catch (error) {
|
|
2025
|
+
return {
|
|
2026
|
+
content: [
|
|
2027
|
+
{
|
|
2028
|
+
type: 'text',
|
|
2029
|
+
text: JSON.stringify({
|
|
2030
|
+
success: false,
|
|
2031
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2032
|
+
}),
|
|
2033
|
+
},
|
|
2034
|
+
],
|
|
2035
|
+
};
|
|
2036
|
+
}
|
|
2037
|
+
});
|
|
2038
|
+
// Register tool: rulebook_learn_capture
|
|
2039
|
+
server.registerTool('rulebook_learn_capture', {
|
|
2040
|
+
title: 'Capture Learning',
|
|
2041
|
+
description: 'Capture a learning or insight for future reference',
|
|
2042
|
+
inputSchema: {
|
|
2043
|
+
title: z.string().describe('Brief title for the learning'),
|
|
2044
|
+
content: z.string().describe('Full content of the learning'),
|
|
2045
|
+
tags: z.array(z.string()).optional().describe('Tags for search'),
|
|
2046
|
+
relatedTask: z.string().optional().describe('Related task ID'),
|
|
2047
|
+
projectId: projectIdSchema,
|
|
2048
|
+
},
|
|
2049
|
+
}, async (args) => {
|
|
2050
|
+
try {
|
|
2051
|
+
const root = args.projectId && workspaceManager
|
|
2052
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2053
|
+
: projectRoot;
|
|
2054
|
+
const { LearnManager } = await import('../core/learn-manager.js');
|
|
2055
|
+
const lm = new LearnManager(root);
|
|
2056
|
+
const learning = await lm.capture(args.title, args.content, {
|
|
2057
|
+
tags: args.tags,
|
|
2058
|
+
relatedTask: args.relatedTask,
|
|
2059
|
+
});
|
|
2060
|
+
return {
|
|
2061
|
+
content: [
|
|
2062
|
+
{
|
|
2063
|
+
type: 'text',
|
|
2064
|
+
text: JSON.stringify({ success: true, learning }),
|
|
2065
|
+
},
|
|
2066
|
+
],
|
|
2067
|
+
};
|
|
2068
|
+
}
|
|
2069
|
+
catch (error) {
|
|
2070
|
+
return {
|
|
2071
|
+
content: [
|
|
2072
|
+
{
|
|
2073
|
+
type: 'text',
|
|
2074
|
+
text: JSON.stringify({
|
|
2075
|
+
success: false,
|
|
2076
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2077
|
+
}),
|
|
2078
|
+
},
|
|
2079
|
+
],
|
|
2080
|
+
};
|
|
2081
|
+
}
|
|
2082
|
+
});
|
|
2083
|
+
// Register tool: rulebook_learn_list
|
|
2084
|
+
server.registerTool('rulebook_learn_list', {
|
|
2085
|
+
title: 'List Learnings',
|
|
2086
|
+
description: 'List captured learnings, newest first',
|
|
2087
|
+
inputSchema: {
|
|
2088
|
+
limit: z.number().optional().describe('Maximum number of learnings to return'),
|
|
2089
|
+
projectId: projectIdSchema,
|
|
2090
|
+
},
|
|
2091
|
+
}, async (args) => {
|
|
2092
|
+
try {
|
|
2093
|
+
const root = args.projectId && workspaceManager
|
|
2094
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2095
|
+
: projectRoot;
|
|
2096
|
+
const { LearnManager } = await import('../core/learn-manager.js');
|
|
2097
|
+
const lm = new LearnManager(root);
|
|
2098
|
+
const learnings = await lm.list(args.limit);
|
|
2099
|
+
return {
|
|
2100
|
+
content: [
|
|
2101
|
+
{
|
|
2102
|
+
type: 'text',
|
|
2103
|
+
text: JSON.stringify({ success: true, learnings, count: learnings.length }),
|
|
2104
|
+
},
|
|
2105
|
+
],
|
|
2106
|
+
};
|
|
2107
|
+
}
|
|
2108
|
+
catch (error) {
|
|
2109
|
+
return {
|
|
2110
|
+
content: [
|
|
2111
|
+
{
|
|
2112
|
+
type: 'text',
|
|
2113
|
+
text: JSON.stringify({
|
|
2114
|
+
success: false,
|
|
2115
|
+
error: error instanceof Error ? error.message : String(error),
|
|
2116
|
+
}),
|
|
2117
|
+
},
|
|
2118
|
+
],
|
|
2119
|
+
};
|
|
2120
|
+
}
|
|
2121
|
+
});
|
|
2122
|
+
// Register tool: rulebook_learn_promote
|
|
2123
|
+
server.registerTool('rulebook_learn_promote', {
|
|
2124
|
+
title: 'Promote Learning',
|
|
2125
|
+
description: 'Promote a learning to a knowledge entry or decision record',
|
|
2126
|
+
inputSchema: {
|
|
2127
|
+
id: z.string().describe('Learning ID to promote'),
|
|
2128
|
+
target: z
|
|
2129
|
+
.enum(['knowledge', 'decision'])
|
|
2130
|
+
.describe('Promote to knowledge base or decision record'),
|
|
2131
|
+
title: z.string().optional().describe('Override title for the promoted entry'),
|
|
2132
|
+
projectId: projectIdSchema,
|
|
2133
|
+
},
|
|
2134
|
+
}, async (args) => {
|
|
2135
|
+
try {
|
|
2136
|
+
const root = args.projectId && workspaceManager
|
|
2137
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2138
|
+
: projectRoot;
|
|
2139
|
+
const { LearnManager } = await import('../core/learn-manager.js');
|
|
2140
|
+
const lm = new LearnManager(root);
|
|
2141
|
+
const result = await lm.promote(args.id, args.target, { title: args.title });
|
|
2142
|
+
if (!result) {
|
|
2143
|
+
return {
|
|
2144
|
+
content: [
|
|
2145
|
+
{
|
|
2146
|
+
type: 'text',
|
|
2147
|
+
text: JSON.stringify({ success: false, error: `Learning "${args.id}" not found` }),
|
|
2148
|
+
},
|
|
2149
|
+
],
|
|
2150
|
+
};
|
|
2151
|
+
}
|
|
2152
|
+
return {
|
|
2153
|
+
content: [
|
|
2154
|
+
{
|
|
2155
|
+
type: 'text',
|
|
2156
|
+
text: JSON.stringify({ success: true, promoted: result }),
|
|
2157
|
+
},
|
|
2158
|
+
],
|
|
2159
|
+
};
|
|
2160
|
+
}
|
|
2161
|
+
catch (error) {
|
|
2162
|
+
return {
|
|
2163
|
+
content: [
|
|
2164
|
+
{
|
|
2165
|
+
type: 'text',
|
|
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
|
+
}),
|
|
2374
|
+
},
|
|
2375
|
+
],
|
|
2376
|
+
};
|
|
2377
|
+
}
|
|
2378
|
+
});
|
|
1643
2379
|
const transport = new StdioServerTransport();
|
|
1644
2380
|
await server.connect(transport);
|
|
1645
2381
|
}
|