@hivehub/rulebook 4.3.0 → 4.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +10 -1
- package/dist/cli/commands.d.ts +32 -0
- package/dist/cli/commands.d.ts.map +1 -1
- package/dist/cli/commands.js +210 -14
- package/dist/cli/commands.js.map +1 -1
- package/dist/core/claude-mcp.d.ts +2 -1
- package/dist/core/claude-mcp.d.ts.map +1 -1
- package/dist/core/claude-mcp.js +16 -8
- package/dist/core/claude-mcp.js.map +1 -1
- package/dist/core/config-manager.d.ts.map +1 -1
- package/dist/core/config-manager.js +1 -2
- package/dist/core/config-manager.js.map +1 -1
- 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.map +1 -1
- package/dist/core/generator.js +26 -0
- package/dist/core/generator.js.map +1 -1
- package/dist/core/indexer/background-indexer.d.ts +7 -0
- package/dist/core/indexer/background-indexer.d.ts.map +1 -1
- package/dist/core/indexer/background-indexer.js +54 -25
- 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/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 +68 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/rulebook-server.d.ts.map +1 -1
- package/dist/mcp/rulebook-server.js +515 -19
- package/dist/mcp/rulebook-server.js.map +1 -1
- package/dist/memory/memory-manager.d.ts +3 -0
- package/dist/memory/memory-manager.d.ts.map +1 -1
- package/dist/memory/memory-manager.js +18 -1
- package/dist/memory/memory-manager.js.map +1 -1
- package/dist/memory/memory-store.d.ts.map +1 -1
- package/dist/memory/memory-store.js +3 -1
- package/dist/memory/memory-store.js.map +1 -1
- package/dist/types.d.ts +43 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/templates/agents/context-intelligence.md +52 -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/DECISIONS.md +38 -0
- package/templates/core/KNOWLEDGE.md +49 -0
|
@@ -9,6 +9,17 @@ 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) => { clearTimeout(timer); resolve(val); }, (err) => { clearTimeout(timer); reject(err); });
|
|
21
|
+
});
|
|
22
|
+
}
|
|
12
23
|
// Find .rulebook file/directory by walking up directories
|
|
13
24
|
export function findRulebookConfig(startDir) {
|
|
14
25
|
let current = resolve(startDir);
|
|
@@ -57,18 +68,30 @@ function loadConfig() {
|
|
|
57
68
|
}
|
|
58
69
|
export async function startRulebookMcpServer() {
|
|
59
70
|
// --- Workspace vs Single-Project Mode ---
|
|
60
|
-
|
|
71
|
+
let isWorkspaceMode = process.argv.includes('--workspace');
|
|
61
72
|
let workspaceManager = null;
|
|
62
73
|
// Default managers (single-project mode OR default workspace project)
|
|
63
74
|
let taskManager;
|
|
64
75
|
let skillsManager;
|
|
65
76
|
let configManager;
|
|
66
77
|
let projectRoot;
|
|
78
|
+
// Resolve start directory (shared by both modes)
|
|
79
|
+
const projectRootFlagIndex = process.argv.indexOf('--project-root');
|
|
80
|
+
const startDir = projectRootFlagIndex !== -1 && process.argv[projectRootFlagIndex + 1]
|
|
81
|
+
? process.argv[projectRootFlagIndex + 1]
|
|
82
|
+
: process.cwd();
|
|
83
|
+
// Auto-detect workspace mode: if not explicitly set, check if a workspace
|
|
84
|
+
// config exists (workspace.json, .code-workspace, or monorepo indicators).
|
|
85
|
+
// This handles the common case where VSCode multi-root workspaces or
|
|
86
|
+
// monorepos start the MCP server from the workspace root without --workspace.
|
|
87
|
+
if (!isWorkspaceMode) {
|
|
88
|
+
const autoDetected = WorkspaceManager.findWorkspaceConfig(startDir);
|
|
89
|
+
if (autoDetected) {
|
|
90
|
+
isWorkspaceMode = true;
|
|
91
|
+
console.error('[rulebook-mcp] Auto-detected workspace configuration, switching to workspace mode.');
|
|
92
|
+
}
|
|
93
|
+
}
|
|
67
94
|
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
95
|
const wsConfig = WorkspaceManager.findWorkspaceConfig(startDir);
|
|
73
96
|
if (!wsConfig) {
|
|
74
97
|
console.error('[rulebook-mcp] No workspace config found. Run `rulebook workspace init` to create .rulebook/workspace.json.');
|
|
@@ -129,7 +152,25 @@ export async function startRulebookMcpServer() {
|
|
|
129
152
|
}
|
|
130
153
|
const server = new McpServer({
|
|
131
154
|
name: 'rulebook-task-management',
|
|
132
|
-
version: '4.
|
|
155
|
+
version: '4.4.1',
|
|
156
|
+
});
|
|
157
|
+
// --- Wrap all tool handlers with timeout guard ---
|
|
158
|
+
// Intercept registerTool to automatically add timeout protection to every handler.
|
|
159
|
+
const originalRegisterTool = server.registerTool.bind(server);
|
|
160
|
+
server.registerTool = ((name, config, handler) => {
|
|
161
|
+
const wrappedHandler = async (...handlerArgs) => {
|
|
162
|
+
try {
|
|
163
|
+
return await withTimeout(handler(...handlerArgs), MCP_TOOL_TIMEOUT_MS, name);
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
167
|
+
console.error(`[rulebook-mcp] ${name} error: ${msg}`);
|
|
168
|
+
return {
|
|
169
|
+
content: [{ type: 'text', text: JSON.stringify({ success: false, error: msg }) }],
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
return originalRegisterTool(name, config, wrappedHandler);
|
|
133
174
|
});
|
|
134
175
|
// Zod schema reused across tools for workspace project targeting
|
|
135
176
|
const projectIdSchema = z
|
|
@@ -660,9 +701,16 @@ export async function startRulebookMcpServer() {
|
|
|
660
701
|
console.error(`[rulebook-mcp] Memory DB: ${memoryDbPath}`);
|
|
661
702
|
memoryManager = createMemoryManager(projectRoot, rulebookConfig.memory);
|
|
662
703
|
autoCaptureEnabled = rulebookConfig.memory.autoCapture !== false;
|
|
663
|
-
// Boot Background Indexer
|
|
704
|
+
// Boot Background Indexer — deferred to avoid blocking server startup
|
|
664
705
|
bgIndexer = new BackgroundIndexer(memoryManager, projectRoot, { enabled: true });
|
|
665
|
-
|
|
706
|
+
setTimeout(() => {
|
|
707
|
+
try {
|
|
708
|
+
bgIndexer?.start();
|
|
709
|
+
}
|
|
710
|
+
catch (e) {
|
|
711
|
+
console.error('[rulebook-mcp] BackgroundIndexer start failed:', e);
|
|
712
|
+
}
|
|
713
|
+
}, 5000);
|
|
666
714
|
global.__indexerStatus = () => bgIndexer?.getStatus();
|
|
667
715
|
}
|
|
668
716
|
catch (e) {
|
|
@@ -685,24 +733,28 @@ export async function startRulebookMcpServer() {
|
|
|
685
733
|
/**
|
|
686
734
|
* Auto-capture: save tool interactions to memory in the background.
|
|
687
735
|
* Fire-and-forget — never blocks or fails the original tool call.
|
|
736
|
+
* Has its own 2s timeout to prevent hanging the event loop.
|
|
688
737
|
*/
|
|
738
|
+
const AUTO_CAPTURE_TIMEOUT_MS = 2000;
|
|
689
739
|
async function autoCapture(toolName, args, resultText) {
|
|
690
740
|
if (!memoryManager || !autoCaptureEnabled)
|
|
691
741
|
return;
|
|
692
742
|
try {
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
743
|
+
await withTimeout((async () => {
|
|
744
|
+
const { captureFromToolCall } = await import('../memory/memory-hooks.js');
|
|
745
|
+
const captured = captureFromToolCall(toolName, args, resultText);
|
|
746
|
+
if (!captured)
|
|
747
|
+
return;
|
|
748
|
+
await memoryManager.saveMemory({
|
|
749
|
+
type: captured.type,
|
|
750
|
+
title: captured.title,
|
|
751
|
+
content: captured.content,
|
|
752
|
+
tags: captured.tags,
|
|
753
|
+
});
|
|
754
|
+
})(), AUTO_CAPTURE_TIMEOUT_MS, `autoCapture(${toolName})`);
|
|
703
755
|
}
|
|
704
756
|
catch {
|
|
705
|
-
// Never fail the original tool call
|
|
757
|
+
// Never fail the original tool call — timeout or error is silently dropped
|
|
706
758
|
}
|
|
707
759
|
}
|
|
708
760
|
function memoryNotEnabled() {
|
|
@@ -1628,6 +1680,450 @@ export async function startRulebookMcpServer() {
|
|
|
1628
1680
|
}
|
|
1629
1681
|
});
|
|
1630
1682
|
}
|
|
1683
|
+
// ============================================
|
|
1684
|
+
// Context Intelligence Layer Tools (v4.4)
|
|
1685
|
+
// ============================================
|
|
1686
|
+
// Register tool: rulebook_decision_create
|
|
1687
|
+
server.registerTool('rulebook_decision_create', {
|
|
1688
|
+
title: 'Create Decision Record',
|
|
1689
|
+
description: 'Create a new architectural decision record (ADR)',
|
|
1690
|
+
inputSchema: {
|
|
1691
|
+
title: z.string().describe('Decision title'),
|
|
1692
|
+
context: z.string().optional().describe('Context and problem statement'),
|
|
1693
|
+
decision: z.string().optional().describe('The decision made'),
|
|
1694
|
+
alternatives: z.array(z.string()).optional().describe('Alternatives considered'),
|
|
1695
|
+
consequences: z.string().optional().describe('Consequences and tradeoffs'),
|
|
1696
|
+
relatedTasks: z.array(z.string()).optional().describe('Related task IDs'),
|
|
1697
|
+
projectId: projectIdSchema,
|
|
1698
|
+
},
|
|
1699
|
+
}, async (args) => {
|
|
1700
|
+
try {
|
|
1701
|
+
const root = args.projectId && workspaceManager
|
|
1702
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1703
|
+
: projectRoot;
|
|
1704
|
+
const { DecisionManager } = await import('../core/decision-manager.js');
|
|
1705
|
+
const dm = new DecisionManager(root);
|
|
1706
|
+
const decision = await dm.create(args.title, {
|
|
1707
|
+
context: args.context,
|
|
1708
|
+
decision: args.decision,
|
|
1709
|
+
alternatives: args.alternatives,
|
|
1710
|
+
consequences: args.consequences,
|
|
1711
|
+
relatedTasks: args.relatedTasks,
|
|
1712
|
+
});
|
|
1713
|
+
return {
|
|
1714
|
+
content: [
|
|
1715
|
+
{
|
|
1716
|
+
type: 'text',
|
|
1717
|
+
text: JSON.stringify({ success: true, decision }),
|
|
1718
|
+
},
|
|
1719
|
+
],
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
catch (error) {
|
|
1723
|
+
return {
|
|
1724
|
+
content: [
|
|
1725
|
+
{
|
|
1726
|
+
type: 'text',
|
|
1727
|
+
text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
|
|
1728
|
+
},
|
|
1729
|
+
],
|
|
1730
|
+
};
|
|
1731
|
+
}
|
|
1732
|
+
});
|
|
1733
|
+
// Register tool: rulebook_decision_list
|
|
1734
|
+
server.registerTool('rulebook_decision_list', {
|
|
1735
|
+
title: 'List Decision Records',
|
|
1736
|
+
description: 'List all architectural decision records',
|
|
1737
|
+
inputSchema: {
|
|
1738
|
+
status: z.string().optional().describe('Filter by status (proposed, accepted, rejected, superseded, deprecated)'),
|
|
1739
|
+
projectId: projectIdSchema,
|
|
1740
|
+
},
|
|
1741
|
+
}, async (args) => {
|
|
1742
|
+
try {
|
|
1743
|
+
const root = args.projectId && workspaceManager
|
|
1744
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1745
|
+
: projectRoot;
|
|
1746
|
+
const { DecisionManager } = await import('../core/decision-manager.js');
|
|
1747
|
+
const dm = new DecisionManager(root);
|
|
1748
|
+
const decisions = await dm.list(args.status);
|
|
1749
|
+
return {
|
|
1750
|
+
content: [
|
|
1751
|
+
{
|
|
1752
|
+
type: 'text',
|
|
1753
|
+
text: JSON.stringify({ success: true, decisions, count: decisions.length }),
|
|
1754
|
+
},
|
|
1755
|
+
],
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
catch (error) {
|
|
1759
|
+
return {
|
|
1760
|
+
content: [
|
|
1761
|
+
{
|
|
1762
|
+
type: 'text',
|
|
1763
|
+
text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
|
|
1764
|
+
},
|
|
1765
|
+
],
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
});
|
|
1769
|
+
// Register tool: rulebook_decision_show
|
|
1770
|
+
server.registerTool('rulebook_decision_show', {
|
|
1771
|
+
title: 'Show Decision Record',
|
|
1772
|
+
description: 'Show full details of a decision record',
|
|
1773
|
+
inputSchema: {
|
|
1774
|
+
id: z.number().describe('Decision ID'),
|
|
1775
|
+
projectId: projectIdSchema,
|
|
1776
|
+
},
|
|
1777
|
+
}, async (args) => {
|
|
1778
|
+
try {
|
|
1779
|
+
const root = args.projectId && workspaceManager
|
|
1780
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1781
|
+
: projectRoot;
|
|
1782
|
+
const { DecisionManager } = await import('../core/decision-manager.js');
|
|
1783
|
+
const dm = new DecisionManager(root);
|
|
1784
|
+
const result = await dm.show(args.id);
|
|
1785
|
+
if (!result) {
|
|
1786
|
+
return {
|
|
1787
|
+
content: [
|
|
1788
|
+
{
|
|
1789
|
+
type: 'text',
|
|
1790
|
+
text: JSON.stringify({ success: false, error: `Decision ${args.id} not found` }),
|
|
1791
|
+
},
|
|
1792
|
+
],
|
|
1793
|
+
};
|
|
1794
|
+
}
|
|
1795
|
+
return {
|
|
1796
|
+
content: [
|
|
1797
|
+
{
|
|
1798
|
+
type: 'text',
|
|
1799
|
+
text: JSON.stringify({ success: true, decision: result.decision, content: result.content }),
|
|
1800
|
+
},
|
|
1801
|
+
],
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1804
|
+
catch (error) {
|
|
1805
|
+
return {
|
|
1806
|
+
content: [
|
|
1807
|
+
{
|
|
1808
|
+
type: 'text',
|
|
1809
|
+
text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
|
|
1810
|
+
},
|
|
1811
|
+
],
|
|
1812
|
+
};
|
|
1813
|
+
}
|
|
1814
|
+
});
|
|
1815
|
+
// Register tool: rulebook_decision_update
|
|
1816
|
+
server.registerTool('rulebook_decision_update', {
|
|
1817
|
+
title: 'Update Decision Record',
|
|
1818
|
+
description: 'Update an existing architectural decision record',
|
|
1819
|
+
inputSchema: {
|
|
1820
|
+
id: z.number().describe('Decision ID to update'),
|
|
1821
|
+
status: z.string().optional().describe('New status (proposed, accepted, rejected, superseded, deprecated)'),
|
|
1822
|
+
context: z.string().optional().describe('Updated context'),
|
|
1823
|
+
decision: z.string().optional().describe('Updated decision text'),
|
|
1824
|
+
projectId: projectIdSchema,
|
|
1825
|
+
},
|
|
1826
|
+
}, async (args) => {
|
|
1827
|
+
try {
|
|
1828
|
+
const root = args.projectId && workspaceManager
|
|
1829
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1830
|
+
: projectRoot;
|
|
1831
|
+
const { DecisionManager } = await import('../core/decision-manager.js');
|
|
1832
|
+
const dm = new DecisionManager(root);
|
|
1833
|
+
const updated = await dm.update(args.id, {
|
|
1834
|
+
status: args.status,
|
|
1835
|
+
context: args.context,
|
|
1836
|
+
decision: args.decision,
|
|
1837
|
+
});
|
|
1838
|
+
if (!updated) {
|
|
1839
|
+
return {
|
|
1840
|
+
content: [
|
|
1841
|
+
{
|
|
1842
|
+
type: 'text',
|
|
1843
|
+
text: JSON.stringify({ success: false, error: `Decision ${args.id} not found` }),
|
|
1844
|
+
},
|
|
1845
|
+
],
|
|
1846
|
+
};
|
|
1847
|
+
}
|
|
1848
|
+
return {
|
|
1849
|
+
content: [
|
|
1850
|
+
{
|
|
1851
|
+
type: 'text',
|
|
1852
|
+
text: JSON.stringify({ success: true, decision: updated }),
|
|
1853
|
+
},
|
|
1854
|
+
],
|
|
1855
|
+
};
|
|
1856
|
+
}
|
|
1857
|
+
catch (error) {
|
|
1858
|
+
return {
|
|
1859
|
+
content: [
|
|
1860
|
+
{
|
|
1861
|
+
type: 'text',
|
|
1862
|
+
text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
|
|
1863
|
+
},
|
|
1864
|
+
],
|
|
1865
|
+
};
|
|
1866
|
+
}
|
|
1867
|
+
});
|
|
1868
|
+
// Register tool: rulebook_knowledge_add
|
|
1869
|
+
server.registerTool('rulebook_knowledge_add', {
|
|
1870
|
+
title: 'Add Knowledge Entry',
|
|
1871
|
+
description: 'Add a new pattern or anti-pattern to the project knowledge base',
|
|
1872
|
+
inputSchema: {
|
|
1873
|
+
type: z.string().describe('Knowledge type: "pattern" or "anti-pattern"'),
|
|
1874
|
+
title: z.string().describe('Entry title'),
|
|
1875
|
+
category: z.string().describe('Category (e.g. code, architecture, testing, security)'),
|
|
1876
|
+
description: z.string().describe('Description of the pattern'),
|
|
1877
|
+
example: z.string().optional().describe('Code or usage example'),
|
|
1878
|
+
whenToUse: z.string().optional().describe('When to use this pattern'),
|
|
1879
|
+
whenNotToUse: z.string().optional().describe('When NOT to use this pattern'),
|
|
1880
|
+
tags: z.array(z.string()).optional().describe('Tags for search'),
|
|
1881
|
+
projectId: projectIdSchema,
|
|
1882
|
+
},
|
|
1883
|
+
}, async (args) => {
|
|
1884
|
+
try {
|
|
1885
|
+
const root = args.projectId && workspaceManager
|
|
1886
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1887
|
+
: projectRoot;
|
|
1888
|
+
const { KnowledgeManager } = await import('../core/knowledge-manager.js');
|
|
1889
|
+
const km = new KnowledgeManager(root);
|
|
1890
|
+
const entry = await km.add(args.type, args.title, {
|
|
1891
|
+
category: args.category,
|
|
1892
|
+
description: args.description,
|
|
1893
|
+
example: args.example,
|
|
1894
|
+
whenToUse: args.whenToUse,
|
|
1895
|
+
whenNotToUse: args.whenNotToUse,
|
|
1896
|
+
tags: args.tags,
|
|
1897
|
+
});
|
|
1898
|
+
return {
|
|
1899
|
+
content: [
|
|
1900
|
+
{
|
|
1901
|
+
type: 'text',
|
|
1902
|
+
text: JSON.stringify({ success: true, entry }),
|
|
1903
|
+
},
|
|
1904
|
+
],
|
|
1905
|
+
};
|
|
1906
|
+
}
|
|
1907
|
+
catch (error) {
|
|
1908
|
+
return {
|
|
1909
|
+
content: [
|
|
1910
|
+
{
|
|
1911
|
+
type: 'text',
|
|
1912
|
+
text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
|
|
1913
|
+
},
|
|
1914
|
+
],
|
|
1915
|
+
};
|
|
1916
|
+
}
|
|
1917
|
+
});
|
|
1918
|
+
// Register tool: rulebook_knowledge_list
|
|
1919
|
+
server.registerTool('rulebook_knowledge_list', {
|
|
1920
|
+
title: 'List Knowledge Entries',
|
|
1921
|
+
description: 'List patterns and anti-patterns in the project knowledge base',
|
|
1922
|
+
inputSchema: {
|
|
1923
|
+
type: z.string().optional().describe('Filter by type: "pattern" or "anti-pattern"'),
|
|
1924
|
+
category: z.string().optional().describe('Filter by category'),
|
|
1925
|
+
projectId: projectIdSchema,
|
|
1926
|
+
},
|
|
1927
|
+
}, async (args) => {
|
|
1928
|
+
try {
|
|
1929
|
+
const root = args.projectId && workspaceManager
|
|
1930
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1931
|
+
: projectRoot;
|
|
1932
|
+
const { KnowledgeManager } = await import('../core/knowledge-manager.js');
|
|
1933
|
+
const km = new KnowledgeManager(root);
|
|
1934
|
+
const entries = await km.list(args.type, args.category);
|
|
1935
|
+
return {
|
|
1936
|
+
content: [
|
|
1937
|
+
{
|
|
1938
|
+
type: 'text',
|
|
1939
|
+
text: JSON.stringify({ success: true, entries, count: entries.length }),
|
|
1940
|
+
},
|
|
1941
|
+
],
|
|
1942
|
+
};
|
|
1943
|
+
}
|
|
1944
|
+
catch (error) {
|
|
1945
|
+
return {
|
|
1946
|
+
content: [
|
|
1947
|
+
{
|
|
1948
|
+
type: 'text',
|
|
1949
|
+
text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
|
|
1950
|
+
},
|
|
1951
|
+
],
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1954
|
+
});
|
|
1955
|
+
// Register tool: rulebook_knowledge_show
|
|
1956
|
+
server.registerTool('rulebook_knowledge_show', {
|
|
1957
|
+
title: 'Show Knowledge Entry',
|
|
1958
|
+
description: 'Show full details of a knowledge entry',
|
|
1959
|
+
inputSchema: {
|
|
1960
|
+
id: z.string().describe('Knowledge entry ID (slug)'),
|
|
1961
|
+
projectId: projectIdSchema,
|
|
1962
|
+
},
|
|
1963
|
+
}, async (args) => {
|
|
1964
|
+
try {
|
|
1965
|
+
const root = args.projectId && workspaceManager
|
|
1966
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
1967
|
+
: projectRoot;
|
|
1968
|
+
const { KnowledgeManager } = await import('../core/knowledge-manager.js');
|
|
1969
|
+
const km = new KnowledgeManager(root);
|
|
1970
|
+
const result = await km.show(args.id);
|
|
1971
|
+
if (!result) {
|
|
1972
|
+
return {
|
|
1973
|
+
content: [
|
|
1974
|
+
{
|
|
1975
|
+
type: 'text',
|
|
1976
|
+
text: JSON.stringify({ success: false, error: `Knowledge entry "${args.id}" not found` }),
|
|
1977
|
+
},
|
|
1978
|
+
],
|
|
1979
|
+
};
|
|
1980
|
+
}
|
|
1981
|
+
return {
|
|
1982
|
+
content: [
|
|
1983
|
+
{
|
|
1984
|
+
type: 'text',
|
|
1985
|
+
text: JSON.stringify({ success: true, entry: result.entry, content: result.content }),
|
|
1986
|
+
},
|
|
1987
|
+
],
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
catch (error) {
|
|
1991
|
+
return {
|
|
1992
|
+
content: [
|
|
1993
|
+
{
|
|
1994
|
+
type: 'text',
|
|
1995
|
+
text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
|
|
1996
|
+
},
|
|
1997
|
+
],
|
|
1998
|
+
};
|
|
1999
|
+
}
|
|
2000
|
+
});
|
|
2001
|
+
// Register tool: rulebook_learn_capture
|
|
2002
|
+
server.registerTool('rulebook_learn_capture', {
|
|
2003
|
+
title: 'Capture Learning',
|
|
2004
|
+
description: 'Capture a learning or insight for future reference',
|
|
2005
|
+
inputSchema: {
|
|
2006
|
+
title: z.string().describe('Brief title for the learning'),
|
|
2007
|
+
content: z.string().describe('Full content of the learning'),
|
|
2008
|
+
tags: z.array(z.string()).optional().describe('Tags for search'),
|
|
2009
|
+
relatedTask: z.string().optional().describe('Related task ID'),
|
|
2010
|
+
projectId: projectIdSchema,
|
|
2011
|
+
},
|
|
2012
|
+
}, async (args) => {
|
|
2013
|
+
try {
|
|
2014
|
+
const root = args.projectId && workspaceManager
|
|
2015
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2016
|
+
: projectRoot;
|
|
2017
|
+
const { LearnManager } = await import('../core/learn-manager.js');
|
|
2018
|
+
const lm = new LearnManager(root);
|
|
2019
|
+
const learning = await lm.capture(args.title, args.content, {
|
|
2020
|
+
tags: args.tags,
|
|
2021
|
+
relatedTask: args.relatedTask,
|
|
2022
|
+
});
|
|
2023
|
+
return {
|
|
2024
|
+
content: [
|
|
2025
|
+
{
|
|
2026
|
+
type: 'text',
|
|
2027
|
+
text: JSON.stringify({ success: true, learning }),
|
|
2028
|
+
},
|
|
2029
|
+
],
|
|
2030
|
+
};
|
|
2031
|
+
}
|
|
2032
|
+
catch (error) {
|
|
2033
|
+
return {
|
|
2034
|
+
content: [
|
|
2035
|
+
{
|
|
2036
|
+
type: 'text',
|
|
2037
|
+
text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
|
|
2038
|
+
},
|
|
2039
|
+
],
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2042
|
+
});
|
|
2043
|
+
// Register tool: rulebook_learn_list
|
|
2044
|
+
server.registerTool('rulebook_learn_list', {
|
|
2045
|
+
title: 'List Learnings',
|
|
2046
|
+
description: 'List captured learnings, newest first',
|
|
2047
|
+
inputSchema: {
|
|
2048
|
+
limit: z.number().optional().describe('Maximum number of learnings to return'),
|
|
2049
|
+
projectId: projectIdSchema,
|
|
2050
|
+
},
|
|
2051
|
+
}, async (args) => {
|
|
2052
|
+
try {
|
|
2053
|
+
const root = args.projectId && workspaceManager
|
|
2054
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2055
|
+
: projectRoot;
|
|
2056
|
+
const { LearnManager } = await import('../core/learn-manager.js');
|
|
2057
|
+
const lm = new LearnManager(root);
|
|
2058
|
+
const learnings = await lm.list(args.limit);
|
|
2059
|
+
return {
|
|
2060
|
+
content: [
|
|
2061
|
+
{
|
|
2062
|
+
type: 'text',
|
|
2063
|
+
text: JSON.stringify({ success: true, learnings, count: learnings.length }),
|
|
2064
|
+
},
|
|
2065
|
+
],
|
|
2066
|
+
};
|
|
2067
|
+
}
|
|
2068
|
+
catch (error) {
|
|
2069
|
+
return {
|
|
2070
|
+
content: [
|
|
2071
|
+
{
|
|
2072
|
+
type: 'text',
|
|
2073
|
+
text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
|
|
2074
|
+
},
|
|
2075
|
+
],
|
|
2076
|
+
};
|
|
2077
|
+
}
|
|
2078
|
+
});
|
|
2079
|
+
// Register tool: rulebook_learn_promote
|
|
2080
|
+
server.registerTool('rulebook_learn_promote', {
|
|
2081
|
+
title: 'Promote Learning',
|
|
2082
|
+
description: 'Promote a learning to a knowledge entry or decision record',
|
|
2083
|
+
inputSchema: {
|
|
2084
|
+
id: z.string().describe('Learning ID to promote'),
|
|
2085
|
+
target: z.enum(['knowledge', 'decision']).describe('Promote to knowledge base or decision record'),
|
|
2086
|
+
title: z.string().optional().describe('Override title for the promoted entry'),
|
|
2087
|
+
projectId: projectIdSchema,
|
|
2088
|
+
},
|
|
2089
|
+
}, async (args) => {
|
|
2090
|
+
try {
|
|
2091
|
+
const root = args.projectId && workspaceManager
|
|
2092
|
+
? (await workspaceManager.getWorker(args.projectId)).projectRoot
|
|
2093
|
+
: projectRoot;
|
|
2094
|
+
const { LearnManager } = await import('../core/learn-manager.js');
|
|
2095
|
+
const lm = new LearnManager(root);
|
|
2096
|
+
const result = await lm.promote(args.id, args.target, { title: args.title });
|
|
2097
|
+
if (!result) {
|
|
2098
|
+
return {
|
|
2099
|
+
content: [
|
|
2100
|
+
{
|
|
2101
|
+
type: 'text',
|
|
2102
|
+
text: JSON.stringify({ success: false, error: `Learning "${args.id}" not found` }),
|
|
2103
|
+
},
|
|
2104
|
+
],
|
|
2105
|
+
};
|
|
2106
|
+
}
|
|
2107
|
+
return {
|
|
2108
|
+
content: [
|
|
2109
|
+
{
|
|
2110
|
+
type: 'text',
|
|
2111
|
+
text: JSON.stringify({ success: true, promoted: result }),
|
|
2112
|
+
},
|
|
2113
|
+
],
|
|
2114
|
+
};
|
|
2115
|
+
}
|
|
2116
|
+
catch (error) {
|
|
2117
|
+
return {
|
|
2118
|
+
content: [
|
|
2119
|
+
{
|
|
2120
|
+
type: 'text',
|
|
2121
|
+
text: JSON.stringify({ success: false, error: error instanceof Error ? error.message : String(error) }),
|
|
2122
|
+
},
|
|
2123
|
+
],
|
|
2124
|
+
};
|
|
2125
|
+
}
|
|
2126
|
+
});
|
|
1631
2127
|
const transport = new StdioServerTransport();
|
|
1632
2128
|
await server.connect(transport);
|
|
1633
2129
|
}
|