forge-server 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/bin/setup-forge.sh +1 -1
  2. package/bin/setup.js +99 -0
  3. package/dist/cli.js +37 -37
  4. package/dist/cli.js.map +1 -1
  5. package/dist/index.js +4 -4
  6. package/dist/index.js.map +1 -1
  7. package/dist/storage/schema.js +113 -113
  8. package/dist/storage/schema.js.map +1 -1
  9. package/dist/storage/sqlite.js +1 -1
  10. package/dist/storage/sqlite.js.map +1 -1
  11. package/dist/util/logger.d.ts +1 -1
  12. package/dist/util/logger.js +1 -1
  13. package/dist/util/types.js +1 -1
  14. package/dist/util/types.js.map +1 -1
  15. package/package.json +8 -2
  16. package/plugin/.mcp.json +1 -1
  17. package/.claude/hooks/worktree-create.sh +0 -64
  18. package/.claude/hooks/worktree-remove.sh +0 -57
  19. package/.claude/settings.local.json +0 -29
  20. package/.forge/knowledge/conventions.yaml +0 -1
  21. package/.forge/knowledge/decisions.yaml +0 -1
  22. package/.forge/knowledge/gotchas.yaml +0 -1
  23. package/.forge/knowledge/patterns.yaml +0 -1
  24. package/.forge/manifest.yaml +0 -6
  25. package/CLAUDE.md +0 -144
  26. package/docker-compose.yml +0 -20
  27. package/docs/plans/2026-02-27-swarm-coordination/architecture.md +0 -203
  28. package/docs/plans/2026-02-27-swarm-coordination/vision.md +0 -57
  29. package/docs/plans/completed/2026-02-26-forge-plugin-bundling/architecture.md +0 -1
  30. package/docs/plans/completed/2026-02-26-forge-plugin-bundling/vision.md +0 -300
  31. package/docs/plans/completed/2026-02-27-forge-swarm-learning/architecture.md +0 -480
  32. package/docs/plans/completed/2026-02-27-forge-swarm-learning/verification-checklist.md +0 -462
  33. package/docs/plans/completed/2026-02-27-git-history-atlassian/git-jira-plan.md +0 -181
  34. package/src/cli.ts +0 -655
  35. package/src/context/.gitkeep +0 -0
  36. package/src/context/codebase.ts +0 -393
  37. package/src/context/injector.ts +0 -797
  38. package/src/context/memory.ts +0 -187
  39. package/src/context/session-index.ts +0 -327
  40. package/src/context/session.ts +0 -152
  41. package/src/index.ts +0 -47
  42. package/src/ingestion/.gitkeep +0 -0
  43. package/src/ingestion/chunker.ts +0 -277
  44. package/src/ingestion/embedder.ts +0 -167
  45. package/src/ingestion/git-analyzer.ts +0 -545
  46. package/src/ingestion/indexer.ts +0 -984
  47. package/src/ingestion/markdown-chunker.ts +0 -337
  48. package/src/ingestion/markdown-knowledge.ts +0 -175
  49. package/src/ingestion/parser.ts +0 -475
  50. package/src/ingestion/watcher.ts +0 -182
  51. package/src/knowledge/.gitkeep +0 -0
  52. package/src/knowledge/hydrator.ts +0 -246
  53. package/src/knowledge/registry.ts +0 -463
  54. package/src/knowledge/search.ts +0 -565
  55. package/src/knowledge/store.ts +0 -262
  56. package/src/learning/.gitkeep +0 -0
  57. package/src/learning/confidence.ts +0 -193
  58. package/src/learning/patterns.ts +0 -360
  59. package/src/learning/trajectory.ts +0 -268
  60. package/src/memory/.gitkeep +0 -0
  61. package/src/memory/memory-compat.ts +0 -233
  62. package/src/memory/observation-store.ts +0 -224
  63. package/src/memory/session-tracker.ts +0 -332
  64. package/src/pipeline/.gitkeep +0 -0
  65. package/src/pipeline/engine.ts +0 -1139
  66. package/src/pipeline/events.ts +0 -253
  67. package/src/pipeline/parallel.ts +0 -394
  68. package/src/pipeline/state-machine.ts +0 -199
  69. package/src/query/.gitkeep +0 -0
  70. package/src/query/graph-queries.ts +0 -262
  71. package/src/query/hybrid-search.ts +0 -337
  72. package/src/query/intent-detector.ts +0 -131
  73. package/src/query/ranking.ts +0 -161
  74. package/src/server.ts +0 -352
  75. package/src/storage/.gitkeep +0 -0
  76. package/src/storage/falkordb-store.ts +0 -388
  77. package/src/storage/file-cache.ts +0 -141
  78. package/src/storage/interfaces.ts +0 -201
  79. package/src/storage/qdrant-store.ts +0 -557
  80. package/src/storage/schema.ts +0 -139
  81. package/src/storage/sqlite.ts +0 -168
  82. package/src/tools/.gitkeep +0 -0
  83. package/src/tools/collaboration-tools.ts +0 -208
  84. package/src/tools/context-tools.ts +0 -493
  85. package/src/tools/graph-tools.ts +0 -295
  86. package/src/tools/ingestion-tools.ts +0 -122
  87. package/src/tools/learning-tools.ts +0 -181
  88. package/src/tools/memory-tools.ts +0 -234
  89. package/src/tools/phase-tools.ts +0 -1452
  90. package/src/tools/pipeline-tools.ts +0 -188
  91. package/src/tools/registration-tools.ts +0 -450
  92. package/src/util/.gitkeep +0 -0
  93. package/src/util/circuit-breaker.ts +0 -193
  94. package/src/util/config.ts +0 -177
  95. package/src/util/logger.ts +0 -53
  96. package/src/util/token-counter.ts +0 -52
  97. package/src/util/types.ts +0 -710
  98. package/tests/context/.gitkeep +0 -0
  99. package/tests/integration/.gitkeep +0 -0
  100. package/tests/knowledge/.gitkeep +0 -0
  101. package/tests/learning/.gitkeep +0 -0
  102. package/tests/pipeline/.gitkeep +0 -0
  103. package/tests/tools/.gitkeep +0 -0
  104. package/tsconfig.json +0 -21
  105. package/vitest.config.ts +0 -10
  106. package/vscode-extension/.vscodeignore +0 -7
  107. package/vscode-extension/README.md +0 -43
  108. package/vscode-extension/out/edge-collector.js +0 -274
  109. package/vscode-extension/out/edge-collector.js.map +0 -1
  110. package/vscode-extension/out/extension.js +0 -264
  111. package/vscode-extension/out/extension.js.map +0 -1
  112. package/vscode-extension/out/forge-client.js +0 -318
  113. package/vscode-extension/out/forge-client.js.map +0 -1
  114. package/vscode-extension/package-lock.json +0 -59
  115. package/vscode-extension/package.json +0 -71
  116. package/vscode-extension/src/edge-collector.ts +0 -320
  117. package/vscode-extension/src/extension.ts +0 -269
  118. package/vscode-extension/src/forge-client.ts +0 -364
  119. package/vscode-extension/tsconfig.json +0 -19
@@ -1,295 +0,0 @@
1
- // graph-tools.ts — Swarm Learning Phase 1
2
- // MCP tool handlers for graph-based code analysis.
3
- // Tools: get_impact_graph, search_logic_flow
4
-
5
- import { z } from 'zod';
6
- import type { GraphStore } from '../storage/interfaces.js';
7
- import {
8
- QUERY_INBOUND_IMPACT,
9
- QUERY_OUTBOUND_IMPACT,
10
- QUERY_SHORTEST_PATH,
11
- } from '../query/graph-queries.js';
12
- import { logger } from '../util/logger.js';
13
-
14
- // ---------------------------------------------------------------------------
15
- // Input schemas
16
- // ---------------------------------------------------------------------------
17
-
18
- export const getImpactGraphSchema = z.object({
19
- symbol: z.string().min(1, 'symbol name is required'),
20
- repo_id: z.string().min(1, 'repo_id is required'),
21
- direction: z.enum(['inbound', 'outbound', 'both']).default('both'),
22
- depth: z.number().int().min(1).max(5).default(3),
23
- });
24
-
25
- export const searchLogicFlowSchema = z.object({
26
- from_symbol: z.string().min(1, 'from_symbol is required'),
27
- to_symbol: z.string().min(1, 'to_symbol is required'),
28
- repo_id: z.string().min(1, 'repo_id is required'),
29
- max_depth: z.number().int().min(1).max(10).default(5),
30
- });
31
-
32
- // ---------------------------------------------------------------------------
33
- // Tool factory
34
- // ---------------------------------------------------------------------------
35
-
36
- export function createGraphTools(graphStore: GraphStore | null) {
37
- return {
38
- 'get_impact_graph': {
39
- schema: getImpactGraphSchema,
40
- description: 'Get the impact graph (callers/dependents) for a code symbol. PREFER THIS over manual Grep for "who uses X" questions — it traverses the actual call/import graph with depth control, not just text matches. Requires FalkorDB graph store.',
41
- handler: async (input: z.infer<typeof getImpactGraphSchema>) => {
42
- if (!graphStore) {
43
- return {
44
- error: 'GRAPH_UNAVAILABLE',
45
- message: 'FalkorDB graph store is not available. Impact graph requires a running FalkorDB instance.',
46
- };
47
- }
48
-
49
- const healthy = await graphStore.isHealthy();
50
- if (!healthy) {
51
- return {
52
- error: 'GRAPH_UNAVAILABLE',
53
- message: 'FalkorDB is not healthy. Impact graph unavailable.',
54
- };
55
- }
56
-
57
- try {
58
- const nodes: Array<{ name: string; label: string; file_path?: string }> = [];
59
- const edges: Array<{ from: string; to: string; type: string; depth: number }> = [];
60
-
61
- // Inbound: who calls/imports/uses this symbol?
62
- if (input.direction === 'inbound' || input.direction === 'both') {
63
- try {
64
- const result = await graphStore.query(
65
- QUERY_INBOUND_IMPACT(input.symbol, input.repo_id, input.depth)
66
- );
67
- parseImpactResult(result, nodes, edges, 'inbound');
68
- } catch (err) {
69
- logger.warn('get_impact_graph: inbound query failed', { error: String(err) });
70
- }
71
- }
72
-
73
- // Outbound: what does this symbol depend on?
74
- if (input.direction === 'outbound' || input.direction === 'both') {
75
- try {
76
- const result = await graphStore.query(
77
- QUERY_OUTBOUND_IMPACT(input.symbol, input.repo_id, input.depth)
78
- );
79
- parseImpactResult(result, nodes, edges, 'outbound');
80
- } catch (err) {
81
- logger.warn('get_impact_graph: outbound query failed', { error: String(err) });
82
- }
83
- }
84
-
85
- // Deduplicate nodes by name
86
- const uniqueNodes = deduplicateNodes(nodes);
87
-
88
- logger.debug('graph-tools.get_impact_graph: completed', {
89
- symbol: input.symbol,
90
- direction: input.direction,
91
- nodes: uniqueNodes.length,
92
- edges: edges.length,
93
- });
94
-
95
- return {
96
- symbol: input.symbol,
97
- direction: input.direction,
98
- nodes: uniqueNodes,
99
- edges,
100
- total_nodes: uniqueNodes.length,
101
- total_edges: edges.length,
102
- };
103
- } catch (err) {
104
- logger.error('graph-tools.get_impact_graph: error', { error: String(err) });
105
- return {
106
- error: 'GET_IMPACT_GRAPH_FAILED',
107
- message: String(err),
108
- };
109
- }
110
- },
111
- },
112
-
113
- 'search_logic_flow': {
114
- schema: searchLogicFlowSchema,
115
- description: 'Find the shortest path between two code symbols in the call/dependency graph. Use this to understand how data or control flows from A to B through the codebase — much faster than manually tracing imports. Requires FalkorDB graph store.',
116
- handler: async (input: z.infer<typeof searchLogicFlowSchema>) => {
117
- if (!graphStore) {
118
- return {
119
- error: 'GRAPH_UNAVAILABLE',
120
- message: 'FalkorDB graph store is not available. Logic flow search requires a running FalkorDB instance.',
121
- };
122
- }
123
-
124
- const healthy = await graphStore.isHealthy();
125
- if (!healthy) {
126
- return {
127
- error: 'GRAPH_UNAVAILABLE',
128
- message: 'FalkorDB is not healthy. Logic flow search unavailable.',
129
- };
130
- }
131
-
132
- try {
133
- const result = await graphStore.query(
134
- QUERY_SHORTEST_PATH(input.from_symbol, input.to_symbol, input.repo_id, input.max_depth)
135
- );
136
-
137
- const paths = parsePathResult(result);
138
-
139
- logger.debug('graph-tools.search_logic_flow: completed', {
140
- from: input.from_symbol,
141
- to: input.to_symbol,
142
- pathCount: paths.length,
143
- });
144
-
145
- return {
146
- from_symbol: input.from_symbol,
147
- to_symbol: input.to_symbol,
148
- paths,
149
- connected: paths.length > 0,
150
- };
151
- } catch (err) {
152
- logger.error('graph-tools.search_logic_flow: error', { error: String(err) });
153
- return {
154
- error: 'SEARCH_LOGIC_FLOW_FAILED',
155
- message: String(err),
156
- from_symbol: input.from_symbol,
157
- to_symbol: input.to_symbol,
158
- paths: [],
159
- connected: false,
160
- };
161
- }
162
- },
163
- },
164
- };
165
- }
166
-
167
- // ---------------------------------------------------------------------------
168
- // Result parsers for FalkorDB raw responses
169
- // ---------------------------------------------------------------------------
170
-
171
- interface GraphQueryResult {
172
- nodes: Array<{ label: string; properties: Record<string, unknown> }>;
173
- edges: Array<{ type: string; from: string; to: string; properties?: Record<string, unknown> }>;
174
- raw?: unknown[];
175
- }
176
-
177
- function parseImpactResult(
178
- result: GraphQueryResult,
179
- nodes: Array<{ name: string; label: string; file_path?: string }>,
180
- edges: Array<{ from: string; to: string; type: string; depth: number }>,
181
- _direction: 'inbound' | 'outbound',
182
- ): void {
183
- // Parse nodes from the result
184
- for (const node of result.nodes ?? []) {
185
- const name = String(node.properties?.['name'] ?? '');
186
- const filePath = node.properties?.['file_path'] as string | undefined;
187
- if (name) {
188
- nodes.push({ name, label: node.label, file_path: filePath });
189
- }
190
- }
191
-
192
- // Parse edges from the result
193
- for (const edge of result.edges ?? []) {
194
- edges.push({
195
- from: edge.from,
196
- to: edge.to,
197
- type: edge.type,
198
- depth: 1,
199
- });
200
- }
201
-
202
- // Also try to parse from raw results (FalkorDB returns varying formats)
203
- for (const row of result.raw ?? []) {
204
- if (!row || !Array.isArray(row)) continue;
205
-
206
- // Expected columns: [caller/dep, relationship_array, depth]
207
- const entity = row[0];
208
- const depth = typeof row[2] === 'number' ? row[2] : 1;
209
-
210
- if (entity && typeof entity === 'object') {
211
- const props = (entity as Record<string, unknown>).properties as Record<string, unknown> | undefined;
212
- if (props) {
213
- const name = String(props['name'] ?? '');
214
- const filePath = props['file_path'] as string | undefined;
215
- if (name) {
216
- nodes.push({ name, label: 'unknown', file_path: filePath });
217
- }
218
- }
219
- }
220
- }
221
- }
222
-
223
- function parsePathResult(
224
- result: GraphQueryResult,
225
- ): Array<{ nodes: string[]; edges: string[]; length: number }> {
226
- const paths: Array<{ nodes: string[]; edges: string[]; length: number }> = [];
227
-
228
- // Try to parse from raw results
229
- for (const row of result.raw ?? []) {
230
- if (!row) continue;
231
-
232
- // The QUERY_SHORTEST_PATH returns path objects
233
- // Different FalkorDB parsers represent these differently
234
- if (Array.isArray(row)) {
235
- const pathObj = row[0];
236
- if (pathObj && typeof pathObj === 'object') {
237
- const pathNodes: string[] = [];
238
- const pathEdges: string[] = [];
239
-
240
- // Extract from FalkorDB path representation
241
- const pNodes = (pathObj as Record<string, unknown>).nodes as Array<Record<string, unknown>> | undefined;
242
- const pEdges = (pathObj as Record<string, unknown>).edges as Array<Record<string, unknown>> | undefined;
243
-
244
- if (pNodes) {
245
- for (const n of pNodes) {
246
- const props = n.properties as Record<string, unknown> | undefined;
247
- pathNodes.push(String(props?.['name'] ?? 'unknown'));
248
- }
249
- }
250
-
251
- if (pEdges) {
252
- for (const e of pEdges) {
253
- pathEdges.push(String(e.type ?? e.relation ?? 'unknown'));
254
- }
255
- }
256
-
257
- if (pathNodes.length > 0) {
258
- paths.push({
259
- nodes: pathNodes,
260
- edges: pathEdges,
261
- length: pathNodes.length - 1,
262
- });
263
- }
264
- }
265
- }
266
- }
267
-
268
- // Fallback: build from the standard node/edge arrays
269
- if (paths.length === 0 && (result.nodes.length > 0 || result.edges.length > 0)) {
270
- const nodeNames = result.nodes.map(n => String(n.properties?.['name'] ?? 'unknown'));
271
- const edgeTypes = result.edges.map(e => e.type);
272
-
273
- if (nodeNames.length > 0) {
274
- paths.push({
275
- nodes: nodeNames,
276
- edges: edgeTypes,
277
- length: Math.max(0, nodeNames.length - 1),
278
- });
279
- }
280
- }
281
-
282
- return paths;
283
- }
284
-
285
- function deduplicateNodes(
286
- nodes: Array<{ name: string; label: string; file_path?: string }>,
287
- ): Array<{ name: string; label: string; file_path?: string }> {
288
- const seen = new Map<string, { name: string; label: string; file_path?: string }>();
289
- for (const node of nodes) {
290
- if (!seen.has(node.name)) {
291
- seen.set(node.name, node);
292
- }
293
- }
294
- return Array.from(seen.values());
295
- }
@@ -1,122 +0,0 @@
1
- // ingestion-tools.ts — Swarm Learning Phase 4
2
- // MCP tool handlers for ingesting external data into the graph store.
3
- // Tools: submit_lsp_edges
4
- // Designed for VS Code extension and CI pipelines to push type-resolved
5
- // call edges that supplement the static AST parsing.
6
-
7
- import { z } from 'zod';
8
- import type { GraphStore } from '../storage/interfaces.js';
9
- import { logger } from '../util/logger.js';
10
-
11
- // ---------------------------------------------------------------------------
12
- // Input schemas
13
- // ---------------------------------------------------------------------------
14
-
15
- const lspEdgeSchema = z.object({
16
- from_file: z.string().min(1),
17
- from_symbol: z.string().min(1),
18
- to_file: z.string().min(1),
19
- to_symbol: z.string().min(1),
20
- edge_type: z.string().min(1),
21
- });
22
-
23
- export const submitLspEdgesSchema = z.object({
24
- repo_id: z.string().min(1, 'repo_id is required'),
25
- edges: z.array(lspEdgeSchema).min(1, 'at least one edge is required'),
26
- });
27
-
28
- // ---------------------------------------------------------------------------
29
- // Tool factory
30
- // ---------------------------------------------------------------------------
31
-
32
- export function createIngestionTools(graphStore: GraphStore | null) {
33
- return {
34
- 'submit_lsp_edges': {
35
- schema: submitLspEdgesSchema,
36
- description:
37
- 'Submit LSP-resolved call/reference edges to enrich the code graph. ' +
38
- 'Designed for the VS Code extension and CI pipelines. Each edge describes ' +
39
- 'a typed relationship between two symbols (e.g. calls, imports, implements).',
40
- handler: async (input: z.infer<typeof submitLspEdgesSchema>) => {
41
- if (!graphStore) {
42
- return {
43
- error: 'GRAPH_UNAVAILABLE',
44
- message:
45
- 'FalkorDB graph store is not available. LSP edges require a running FalkorDB instance.',
46
- };
47
- }
48
-
49
- const healthy = await graphStore.isHealthy();
50
- if (!healthy) {
51
- return {
52
- error: 'GRAPH_UNAVAILABLE',
53
- message: 'FalkorDB is not healthy. Cannot ingest LSP edges.',
54
- };
55
- }
56
-
57
- let accepted = 0;
58
- let failed = 0;
59
- const errors: string[] = [];
60
-
61
- for (const edge of input.edges) {
62
- try {
63
- // Upsert the source node (Symbol)
64
- await graphStore.upsertNode(
65
- 'Symbol',
66
- { name: edge.from_symbol, repo_id: input.repo_id },
67
- { file_path: edge.from_file, source: 'lsp' },
68
- );
69
-
70
- // Upsert the target node (Symbol)
71
- await graphStore.upsertNode(
72
- 'Symbol',
73
- { name: edge.to_symbol, repo_id: input.repo_id },
74
- { file_path: edge.to_file, source: 'lsp' },
75
- );
76
-
77
- // Upsert the edge between the two symbols
78
- await graphStore.upsertEdge(
79
- 'Symbol',
80
- { name: edge.from_symbol, repo_id: input.repo_id },
81
- edge.edge_type,
82
- { source: 'lsp', repo_id: input.repo_id },
83
- 'Symbol',
84
- { name: edge.to_symbol, repo_id: input.repo_id },
85
- );
86
-
87
- accepted++;
88
- } catch (err) {
89
- failed++;
90
- const msg = `${edge.from_symbol} -[${edge.edge_type}]-> ${edge.to_symbol}: ${String(err)}`;
91
- errors.push(msg);
92
- logger.warn('ingestion-tools.submit_lsp_edges: edge failed', {
93
- from: edge.from_symbol,
94
- to: edge.to_symbol,
95
- edgeType: edge.edge_type,
96
- error: String(err),
97
- });
98
- }
99
- }
100
-
101
- logger.debug('ingestion-tools.submit_lsp_edges: completed', {
102
- repoId: input.repo_id,
103
- total: input.edges.length,
104
- accepted,
105
- failed,
106
- });
107
-
108
- const result: { accepted: number; failed: number; errors?: string[] } = {
109
- accepted,
110
- failed,
111
- };
112
-
113
- if (errors.length > 0) {
114
- // Cap error messages to avoid oversized responses
115
- result.errors = errors.slice(0, 20);
116
- }
117
-
118
- return result;
119
- },
120
- },
121
- };
122
- }
@@ -1,181 +0,0 @@
1
- // learning-tools.ts — B17
2
- // MCP tool handlers for knowledge retrieval and confidence management.
3
- // Tools: get_patterns, get_gotchas, boost_knowledge, decay_knowledge
4
-
5
- import { z } from 'zod';
6
- import type { KnowledgeSearch } from '../knowledge/search.js';
7
- import type { ConfidenceManager } from '../learning/confidence.js';
8
- import type { QdrantVectorStore } from '../storage/qdrant-store.js';
9
- import { logger } from '../util/logger.js';
10
-
11
- // ---------------------------------------------------------------------------
12
- // Input schemas
13
- // ---------------------------------------------------------------------------
14
-
15
- export const getPatternsSchema = z.object({
16
- domain: z.string().optional(),
17
- min_confidence: z.number().min(0).max(1).default(0.5),
18
- limit: z.number().int().min(1).max(50).default(20),
19
- });
20
-
21
- export const getGotchasSchema = z.object({
22
- stack: z.array(z.string()).optional(),
23
- min_confidence: z.number().min(0).max(1).default(0.3),
24
- limit: z.number().int().min(1).max(50).default(20),
25
- });
26
-
27
- export const boostKnowledgeSchema = z.object({
28
- knowledge_id: z.string().min(1, 'knowledge_id is required'),
29
- amount: z.number().min(0).max(1).default(0.05),
30
- reason: z.string().optional(),
31
- });
32
-
33
- export const decayKnowledgeSchema = z.object({
34
- decay_rate: z.number().min(0).max(1).default(0.01),
35
- });
36
-
37
- // ---------------------------------------------------------------------------
38
- // Tool factory
39
- // ---------------------------------------------------------------------------
40
-
41
- export function createLearningTools(
42
- knowledgeSearch: KnowledgeSearch,
43
- confidenceManager?: ConfidenceManager,
44
- vectorStore?: QdrantVectorStore | null,
45
- ) {
46
- return {
47
- 'get_patterns': {
48
- schema: getPatternsSchema,
49
- description: 'Retrieve established code patterns from the knowledge base. PREFER THIS over ad-hoc code exploration when looking for "how do we do X in this codebase" — it returns curated, high-confidence patterns from past pipeline runs. Optionally filtered by domain/stack.',
50
- handler: async (input: z.infer<typeof getPatternsSchema>) => {
51
- try {
52
- const patterns = await knowledgeSearch.getPatterns(input.domain, input.limit, input.min_confidence);
53
-
54
- logger.debug('learning-tools.get_patterns: completed', {
55
- domain: input.domain,
56
- count: patterns.length,
57
- });
58
-
59
- return {
60
- patterns: patterns.map(p => ({
61
- title: p.title,
62
- content: p.content,
63
- confidence: p.confidence,
64
- stack_tags: Array.isArray(p.stack_tags) ? p.stack_tags : [],
65
- source_repo: (p as { source_repo?: string }).source_repo ?? p.source_agent ?? 'unknown',
66
- times_confirmed: 1, // Qdrant payload doesn't track this yet
67
- })),
68
- };
69
- } catch (err) {
70
- logger.error('learning-tools.get_patterns: error', { error: String(err) });
71
- return {
72
- error: 'GET_PATTERNS_FAILED',
73
- message: String(err),
74
- patterns: [],
75
- };
76
- }
77
- },
78
- },
79
-
80
- 'get_gotchas': {
81
- schema: getGotchasSchema,
82
- description: 'Retrieve known gotchas and pitfalls from the knowledge base. ALWAYS CHECK THIS before starting implementation in an indexed repo — avoids repeating past mistakes. Optionally filtered by stack tags.',
83
- handler: async (input: z.infer<typeof getGotchasSchema>) => {
84
- try {
85
- const gotchas = await knowledgeSearch.getGotchas(input.stack, input.limit, input.min_confidence);
86
-
87
- logger.debug('learning-tools.get_gotchas: completed', {
88
- stack: input.stack,
89
- count: gotchas.length,
90
- });
91
-
92
- return {
93
- gotchas: gotchas.map(g => ({
94
- title: g.title,
95
- content: g.content,
96
- confidence: g.confidence,
97
- stack_tags: Array.isArray(g.stack_tags) ? g.stack_tags : [],
98
- source_repo: (g as { source_repo?: string }).source_repo ?? g.source_agent ?? 'unknown',
99
- })),
100
- };
101
- } catch (err) {
102
- logger.error('learning-tools.get_gotchas: error', { error: String(err) });
103
- return {
104
- error: 'GET_GOTCHAS_FAILED',
105
- message: String(err),
106
- gotchas: [],
107
- };
108
- }
109
- },
110
- },
111
-
112
- 'boost_knowledge': {
113
- schema: boostKnowledgeSchema,
114
- description: 'Boost the confidence score of a specific knowledge item. Used by Knowledge Keeper when a knowledge item proves valuable.',
115
- handler: async (input: z.infer<typeof boostKnowledgeSchema>) => {
116
- if (!confidenceManager || !vectorStore) {
117
- return {
118
- error: 'CONFIDENCE_UNAVAILABLE',
119
- message: 'ConfidenceManager or vector store not available. Qdrant may be down.',
120
- };
121
- }
122
-
123
- try {
124
- await confidenceManager.boost(vectorStore, input.knowledge_id, input.amount);
125
-
126
- logger.debug('learning-tools.boost_knowledge: completed', {
127
- knowledge_id: input.knowledge_id,
128
- amount: input.amount,
129
- reason: input.reason,
130
- });
131
-
132
- return {
133
- knowledge_id: input.knowledge_id,
134
- boosted: true,
135
- amount: input.amount,
136
- reason: input.reason,
137
- };
138
- } catch (err) {
139
- logger.error('learning-tools.boost_knowledge: error', { error: String(err) });
140
- return {
141
- error: 'BOOST_KNOWLEDGE_FAILED',
142
- message: String(err),
143
- };
144
- }
145
- },
146
- },
147
-
148
- 'decay_knowledge': {
149
- schema: decayKnowledgeSchema,
150
- description: 'Apply time-based confidence decay to all knowledge items. Items not accessed within 30 days lose confidence. Used by Knowledge Keeper for maintenance.',
151
- handler: async (input: z.infer<typeof decayKnowledgeSchema>) => {
152
- if (!confidenceManager || !vectorStore) {
153
- return {
154
- error: 'CONFIDENCE_UNAVAILABLE',
155
- message: 'ConfidenceManager or vector store not available. Qdrant may be down.',
156
- };
157
- }
158
-
159
- try {
160
- const decayedCount = await confidenceManager.decayAll(vectorStore, input.decay_rate);
161
-
162
- logger.debug('learning-tools.decay_knowledge: completed', {
163
- decay_rate: input.decay_rate,
164
- decayed_count: decayedCount,
165
- });
166
-
167
- return {
168
- decayed_count: decayedCount,
169
- decay_rate: input.decay_rate,
170
- };
171
- } catch (err) {
172
- logger.error('learning-tools.decay_knowledge: error', { error: String(err) });
173
- return {
174
- error: 'DECAY_KNOWLEDGE_FAILED',
175
- message: String(err),
176
- };
177
- }
178
- },
179
- },
180
- };
181
- }