octocode-cli 1.5.1 → 1.5.3

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 (112) hide show
  1. package/README.md +28 -9
  2. package/out/chunks/chunk-375ZNU3A.js +317 -0
  3. package/out/chunks/{chunk-V64AXUWB.js → chunk-AYILFPX4.js} +1 -1
  4. package/out/chunks/chunk-BRHVQB64.js +10 -0
  5. package/out/chunks/{command-help-specs-JZXVSLZ5.js → chunk-KWJUBVXJ.js} +1 -1
  6. package/out/chunks/chunk-OMXWZJBJ.js +100 -0
  7. package/out/chunks/command-help-specs-RVJK6V6M.js +8 -0
  8. package/out/chunks/commands-JFDCC4Q7.js +8 -0
  9. package/out/chunks/{help-P7TCOYAJ.js → help-H3WGUJO7.js} +1 -1
  10. package/out/chunks/main-help-BUTDD4IR.js +10 -0
  11. package/out/chunks/{spinner-URV2OX6O.js → spinner-DXD4JQIU.js} +1 -1
  12. package/out/chunks/tool-command-L6PVHUNS.js +8 -0
  13. package/out/octocode-cli.js +1 -1
  14. package/package.json +2 -2
  15. package/skills/octocode-brainstorming/scripts/tavily-search.mjs +1 -10
  16. package/skills/octocode-chrome-devtools/scripts/cdp-runner.mjs +0 -2
  17. package/skills/octocode-chrome-devtools/scripts/cdp-sandbox.mjs +1 -5
  18. package/skills/octocode-chrome-devtools/scripts/cdp-template.mjs +1 -10
  19. package/skills/octocode-chrome-devtools/scripts/open-browser.mjs +0 -3
  20. package/skills/octocode-chrome-devtools/scripts/sourcemap-resolver.mjs +1 -4
  21. package/skills/octocode-chrome-devtools/scripts/undercover.mjs +0 -2
  22. package/skills/octocode-engineer/SKILL.md +7 -33
  23. package/skills/octocode-engineer/references/cli-reference.md +5 -11
  24. package/skills/octocode-engineer/references/externals.md +0 -4
  25. package/skills/octocode-engineer/references/quality-indicators.md +10 -50
  26. package/skills/octocode-engineer/references/tool-workflows.md +10 -13
  27. package/skills/octocode-engineer/src/analysis/dependency-summary.test.ts +2 -2
  28. package/skills/octocode-engineer/src/ast/metrics.test.ts +2 -2
  29. package/skills/octocode-engineer/src/ast/search-main.ts +2 -15
  30. package/skills/octocode-engineer/src/ast/search.ts +1 -7
  31. package/skills/octocode-engineer/src/ast/tree-sitter.test.ts +4 -4
  32. package/skills/octocode-engineer/src/ast/tree-sitter.ts +2 -2
  33. package/skills/octocode-engineer/src/collectors/chains.ts +3 -10
  34. package/skills/octocode-engineer/src/collectors/prototype-pollution.ts +3 -3
  35. package/skills/octocode-engineer/src/collectors/security.ts +3 -3
  36. package/skills/octocode-engineer/src/common/ensure-deps.ts +3 -20
  37. package/skills/octocode-engineer/src/detectors/code-quality.ts +0 -1
  38. package/skills/octocode-engineer/src/index.test.ts +1 -1
  39. package/skills/octocode-engineer/src/index.ts +1 -11
  40. package/skills/octocode-engineer/src/pipeline/affected.ts +2 -7
  41. package/skills/octocode-engineer/src/pipeline/baseline.ts +2 -8
  42. package/skills/octocode-engineer/src/pipeline/cache.ts +2 -2
  43. package/skills/octocode-engineer/src/pipeline/config-loader.test.ts +1 -2
  44. package/skills/octocode-engineer/src/pipeline/config-loader.ts +0 -9
  45. package/skills/octocode-engineer/src/pipeline/create-options.ts +2 -15
  46. package/skills/octocode-engineer/src/pipeline/reporters.ts +2 -12
  47. package/skills/octocode-engineer/src/reporting/summary-md.test.ts +2 -2
  48. package/skills/octocode-engineer/src/run.ts +1 -7
  49. package/skills/octocode-engineer/src/types/constants.ts +0 -5
  50. package/skills/octocode-engineer/src/types/core.ts +0 -5
  51. package/skills/octocode-news/eslint.config.mjs +1 -6
  52. package/skills/octocode-news/src/rss-core.ts +2 -4
  53. package/skills/octocode-research/ecosystem.config.cjs +0 -31
  54. package/skills/octocode-research/package.json +1 -1
  55. package/skills/octocode-research/scripts/server.js +272 -230
  56. package/skills/octocode-research/src/__tests__/integration/circuitBreaker.test.ts +0 -24
  57. package/skills/octocode-research/src/__tests__/integration/routes.test.ts +0 -10
  58. package/skills/octocode-research/src/__tests__/integration/serverLifecycle.test.ts +5 -22
  59. package/skills/octocode-research/src/__tests__/integration/toolsRoutes.test.ts +0 -27
  60. package/skills/octocode-research/src/__tests__/unit/circuitBreaker.test.ts +0 -29
  61. package/skills/octocode-research/src/__tests__/unit/errorHandler.test.ts +1 -12
  62. package/skills/octocode-research/src/__tests__/unit/httpPreprocess.test.ts +1 -9
  63. package/skills/octocode-research/src/__tests__/unit/logger.test.ts +0 -7
  64. package/skills/octocode-research/src/__tests__/unit/queryParser.test.ts +1 -7
  65. package/skills/octocode-research/src/__tests__/unit/responseBuilder.test.ts +0 -11
  66. package/skills/octocode-research/src/__tests__/unit/retry.test.ts +1 -18
  67. package/skills/octocode-research/src/__tests__/unit/schemas.test.ts +2 -25
  68. package/skills/octocode-research/src/index.ts +0 -27
  69. package/skills/octocode-research/src/mcpCache.ts +3 -18
  70. package/skills/octocode-research/src/middleware/errorHandler.ts +1 -4
  71. package/skills/octocode-research/src/middleware/logger.ts +1 -6
  72. package/skills/octocode-research/src/middleware/queryParser.ts +3 -22
  73. package/skills/octocode-research/src/routes/github.ts +0 -11
  74. package/skills/octocode-research/src/routes/local.ts +0 -12
  75. package/skills/octocode-research/src/routes/lsp.ts +1 -10
  76. package/skills/octocode-research/src/routes/package.ts +0 -2
  77. package/skills/octocode-research/src/routes/prompts.ts +2 -32
  78. package/skills/octocode-research/src/routes/tools.ts +12 -177
  79. package/skills/octocode-research/src/server-init.ts +0 -32
  80. package/skills/octocode-research/src/server.ts +8 -37
  81. package/skills/octocode-research/src/types/errorGuards.ts +0 -15
  82. package/skills/octocode-research/src/types/guards.ts +6 -27
  83. package/skills/octocode-research/src/types/mcp.ts +1 -8
  84. package/skills/octocode-research/src/types/responses.ts +0 -15
  85. package/skills/octocode-research/src/utils/asyncTimeout.ts +1 -63
  86. package/skills/octocode-research/src/utils/circuitBreaker.ts +43 -166
  87. package/skills/octocode-research/src/utils/colors.ts +3 -17
  88. package/skills/octocode-research/src/utils/errorQueue.ts +6 -28
  89. package/skills/octocode-research/src/utils/logger.ts +11 -93
  90. package/skills/octocode-research/src/utils/resilience.ts +22 -77
  91. package/skills/octocode-research/src/utils/responseBuilder.ts +26 -78
  92. package/skills/octocode-research/src/utils/responseFactory.ts +0 -7
  93. package/skills/octocode-research/src/utils/responseParser.ts +11 -59
  94. package/skills/octocode-research/src/utils/retry.ts +12 -75
  95. package/skills/octocode-research/src/utils/routeFactory.ts +10 -61
  96. package/skills/octocode-research/src/utils/url.ts +0 -5
  97. package/skills/octocode-research/src/validation/httpPreprocess.ts +15 -61
  98. package/skills/octocode-research/src/validation/schemas.ts +14 -101
  99. package/skills/octocode-research/src/validation/toolCallSchema.ts +5 -65
  100. package/skills/octocode-research/tsdown.config.ts +2 -7
  101. package/skills/octocode-search-skill/SKILL.md +9 -2
  102. package/skills/octocode-search-skill/references/discovery-surfaces.md +2 -1
  103. package/skills/octocode-slides/scripts/navbridge.js +1 -14
  104. package/skills/octocode-slides/scripts/presenter.js +2 -61
  105. package/skills/octocode-stats/scripts/build_dashboard.mjs +1 -19
  106. package/out/chunks/chunk-7476PETK.js +0 -309
  107. package/out/chunks/chunk-OQBJTZWK.js +0 -60
  108. package/out/chunks/chunk-UCZCF3BQ.js +0 -9
  109. package/out/chunks/commands-XBFPLHSQ.js +0 -8
  110. package/out/chunks/main-help-ULF5PAQY.js +0 -10
  111. package/out/chunks/tool-command-M6VA7P2F.js +0 -8
  112. package/skills/octocode-research/AGENTS.md +0 -349
@@ -1,29 +1,12 @@
1
- /**
2
- * Research-specific response builders for octocode-research skill.
3
- *
4
- * Provides high-level response helpers that wrap the MCP role-based response API
5
- * with research-specific patterns and formatting.
6
- *
7
- * NOTE: The MCP tools already provide rich reasoning context via:
8
- * - mainResearchGoal, researchGoal, reasoning in each result
9
- * - hasResultsStatusHints, emptyStatusHints, errorStatusHints for guidance
10
- * - Dynamic per-tool hints based on context
11
- *
12
- * This module focuses on formatting and presentation, NOT duplicating reasoning.
13
- */
14
-
15
1
  import {
16
2
  createRoleBasedResult,
17
3
  QuickResult,
18
4
  StatusEmoji,
19
5
  } from 'octocode-mcp/public';
20
6
 
21
- // CallToolResult type from MCP SDK (re-exported via octocode-mcp)
22
7
  type CallToolResult = ReturnType<typeof createRoleBasedResult>;
23
8
 
24
- /**
25
- * Pagination info for paginated responses
26
- */
9
+
27
10
  interface PaginationInfo {
28
11
  page: number;
29
12
  total: number;
@@ -32,9 +15,7 @@ interface PaginationInfo {
32
15
  totalItems?: number;
33
16
  }
34
17
 
35
- /**
36
- * Detailed match location info (preserved from MCP response)
37
- */
18
+
38
19
  interface MatchLocation {
39
20
  line: number;
40
21
  column?: number;
@@ -43,49 +24,40 @@ interface MatchLocation {
43
24
  charOffset?: number;
44
25
  }
45
26
 
46
- /**
47
- * File match info for search results
48
- */
27
+
49
28
  interface FileMatch {
50
29
  path: string;
51
30
  matches?: number;
52
31
  line?: number;
53
32
  preview?: string;
54
33
  repo?: string;
55
- /** All match locations - preserved from MCP for detailed analysis */
34
+
56
35
  allMatches?: MatchLocation[];
57
36
  }
58
37
 
59
- /**
60
- * Research context from MCP response
61
- */
38
+
62
39
  interface ResearchContext {
63
40
  mainResearchGoal?: string;
64
41
  researchGoal?: string;
65
42
  reasoning?: string;
66
43
  }
67
44
 
68
- /**
69
- * Research-specific response builders
70
- */
45
+
71
46
  export const ResearchResponse = {
72
- /**
73
- * Search results with navigation hints
74
- */
47
+
75
48
  searchResults(results: {
76
49
  files: FileMatch[];
77
50
  totalMatches: number;
78
51
  pagination?: PaginationInfo;
79
52
  searchPattern?: string;
80
53
  isLocal?: boolean;
81
- /** MCP workflow hints - passed through from tool response */
54
+
82
55
  mcpHints?: string[];
83
- /** Research context - preserved from the query */
56
+
84
57
  research?: ResearchContext;
85
58
  }): CallToolResult {
86
59
  const { files, totalMatches, pagination, searchPattern, mcpHints = [] } = results;
87
60
 
88
- // Build summary
89
61
  const patternInfo = searchPattern ? ` for "${searchPattern}"` : '';
90
62
  const summary =
91
63
  files.length > 0
@@ -100,7 +72,6 @@ export const ResearchResponse = {
100
72
  (files.length > 10 ? `\n... and ${files.length - 10} more files` : '')
101
73
  : `No matches found${patternInfo}`;
102
74
 
103
- // Build hints - start with MCP hints, add pagination info
104
75
  const hints: string[] = [...mcpHints];
105
76
  if (files.length > 10) {
106
77
  hints.push(`Showing 10 of ${files.length} files`);
@@ -109,9 +80,7 @@ export const ResearchResponse = {
109
80
  hints.push(`Next page: page=${pagination.page + 1}`);
110
81
  }
111
82
 
112
- // Return appropriate response type
113
83
  if (files.length === 0) {
114
- // For empty results, MCP already provides relevant hints
115
84
  const emptyHints = mcpHints.length > 0 ? mcpHints : [
116
85
  'Try broader search terms',
117
86
  'Check spelling and case sensitivity',
@@ -127,9 +96,7 @@ export const ResearchResponse = {
127
96
  return QuickResult.success(summary, results, hints);
128
97
  },
129
98
 
130
- /**
131
- * File content with context
132
- */
99
+
133
100
  fileContent(result: {
134
101
  path: string;
135
102
  content: string;
@@ -137,9 +104,9 @@ export const ResearchResponse = {
137
104
  language?: string;
138
105
  totalLines?: number;
139
106
  isPartial?: boolean;
140
- /** MCP workflow hints - passed through from tool response */
107
+
141
108
  mcpHints?: string[];
142
- /** Research context - preserved from the query */
109
+
143
110
  research?: ResearchContext;
144
111
  }): CallToolResult {
145
112
  const { path, content, lines, language, totalLines, isPartial, mcpHints = [] } = result;
@@ -147,10 +114,8 @@ export const ResearchResponse = {
147
114
  const lineInfo = lines ? ` (lines ${lines.start}-${lines.end})` : '';
148
115
  const lang = language || detectLanguage(path);
149
116
 
150
- // Format content with code fence
151
117
  const formattedContent = `📄 ${path}${lineInfo}\n\n\`\`\`${lang}\n${content}\n\`\`\``;
152
118
 
153
- // Build hints - start with MCP hints, add contextual info
154
119
  const hints: string[] = [...mcpHints];
155
120
  hints.push('Content retrieved successfully');
156
121
  if (lines) {
@@ -169,16 +134,14 @@ export const ResearchResponse = {
169
134
  });
170
135
  },
171
136
 
172
- /**
173
- * LSP definition/reference/call hierarchy results
174
- */
137
+
175
138
  lspResult(result: {
176
139
  symbol: string;
177
140
  locations: Array<{ uri: string; line: number; preview?: string }>;
178
141
  type: 'definition' | 'references' | 'calls' | 'incoming' | 'outgoing';
179
- /** MCP workflow hints - passed through from tool response */
142
+
180
143
  mcpHints?: string[];
181
- /** Research context - preserved from the query */
144
+
182
145
  research?: ResearchContext;
183
146
  }): CallToolResult {
184
147
  const { symbol, locations, type, mcpHints = [] } = result;
@@ -212,7 +175,6 @@ export const ResearchResponse = {
212
175
  .join('\n')
213
176
  : `No ${type} found for "${symbol}"`;
214
177
 
215
- // Build hints - start with MCP hints, add contextual info
216
178
  const hints: string[] = [...mcpHints];
217
179
  if (locations.length > 0) {
218
180
  hints.push('Use returned line numbers for further navigation');
@@ -235,9 +197,7 @@ export const ResearchResponse = {
235
197
  });
236
198
  },
237
199
 
238
- /**
239
- * Repository structure view
240
- */
200
+
241
201
  repoStructure(result: {
242
202
  path: string;
243
203
  structure: { files: string[]; folders: string[] };
@@ -246,9 +206,9 @@ export const ResearchResponse = {
246
206
  totalFolders?: number;
247
207
  owner?: string;
248
208
  repo?: string;
249
- /** MCP workflow hints - passed through from tool response */
209
+
250
210
  mcpHints?: string[];
251
- /** Research context - preserved from the query */
211
+
252
212
  research?: ResearchContext;
253
213
  }): CallToolResult {
254
214
  const { path, structure, depth, totalFiles, totalFolders, owner, repo, mcpHints = [] } =
@@ -269,7 +229,6 @@ export const ResearchResponse = {
269
229
  ? `Files:\n${fileList.map(f => ` 📄 ${f}`).join('\n')}`
270
230
  : 'No files in this directory');
271
231
 
272
- // Build hints - start with MCP hints, add contextual info
273
232
  const hints: string[] = [...mcpHints];
274
233
  if (depth === 1) {
275
234
  hints.push('Use depth=2 to see nested contents');
@@ -301,9 +260,7 @@ export const ResearchResponse = {
301
260
  });
302
261
  },
303
262
 
304
- /**
305
- * Package search results
306
- */
263
+
307
264
  packageSearch(result: {
308
265
  packages: Array<{
309
266
  name: string;
@@ -313,9 +270,9 @@ export const ResearchResponse = {
313
270
  }>;
314
271
  registry: 'npm' | 'pypi';
315
272
  query?: string;
316
- /** MCP workflow hints - passed through from tool response */
273
+
317
274
  mcpHints?: string[];
318
- /** Research context - preserved from the query */
275
+
319
276
  research?: ResearchContext;
320
277
  }): CallToolResult {
321
278
  const { packages, registry, query, mcpHints = [] } = result;
@@ -333,7 +290,6 @@ export const ResearchResponse = {
333
290
  .join('\n')
334
291
  : `No packages found${queryInfo} on ${registry.toUpperCase()}`;
335
292
 
336
- // Build hints - start with MCP hints, add contextual info
337
293
  const hints: string[] = [...mcpHints];
338
294
  if (packages.length > 0) {
339
295
  hints.push('Use repository URL with githubViewRepoStructure to explore source');
@@ -350,9 +306,7 @@ export const ResearchResponse = {
350
306
  return QuickResult.success(summary, result, hints);
351
307
  },
352
308
 
353
- /**
354
- * Pull request search results
355
- */
309
+
356
310
  pullRequests(result: {
357
311
  prs: Array<{
358
312
  number: number;
@@ -363,9 +317,9 @@ export const ResearchResponse = {
363
317
  }>;
364
318
  repo?: string;
365
319
  pagination?: PaginationInfo;
366
- /** MCP workflow hints - passed through from tool response */
320
+
367
321
  mcpHints?: string[];
368
- /** Research context - preserved from the query */
322
+
369
323
  research?: ResearchContext;
370
324
  }): CallToolResult {
371
325
  const { prs, repo, pagination, mcpHints = [] } = result;
@@ -383,7 +337,6 @@ export const ResearchResponse = {
383
337
  .join('\n')
384
338
  : `No pull requests found${repoInfo}`;
385
339
 
386
- // Build hints - start with MCP hints, add contextual info
387
340
  const hints: string[] = [...mcpHints];
388
341
  if (prs.length > 0) {
389
342
  hints.push('Use prNumber with type="fullContent" to see full diff');
@@ -404,9 +357,7 @@ export const ResearchResponse = {
404
357
  return QuickResult.success(summary, result, hints);
405
358
  },
406
359
 
407
- /**
408
- * Generic bulk operation result
409
- */
360
+
410
361
  bulkResult(result: {
411
362
  results: Array<{ status: string; data?: unknown; error?: string }>;
412
363
  operation: string;
@@ -452,9 +403,7 @@ export const ResearchResponse = {
452
403
  },
453
404
  };
454
405
 
455
- /**
456
- * Detect language from file extension
457
- */
406
+
458
407
  function detectLanguage(path: string): string {
459
408
  const ext = path.split('.').pop()?.toLowerCase() || '';
460
409
  const langMap: Record<string, string> = {
@@ -491,5 +440,4 @@ function detectLanguage(path: string): string {
491
440
  return langMap[ext] || '';
492
441
  }
493
442
 
494
- // Re-export utilities for convenience
495
443
  export { QuickResult, detectLanguage as detectLanguageFromPath };
@@ -1,10 +1,3 @@
1
- /**
2
- * Response utilities for route handlers.
3
- * Provides type-safe extractors and helpers for processing MCP responses.
4
- *
5
- * @module utils/responseFactory
6
- */
7
-
8
1
  import { isObject, hasProperty, isArray, hasStringProperty, hasNumberProperty } from '../types/guards.js';
9
2
 
10
3
  export function safeString(obj: unknown, key: string, fallback = ''): string {
@@ -1,21 +1,3 @@
1
- /**
2
- * Parse MCP tool responses to extract structured data
3
- *
4
- * MCP tools return responses in this format:
5
- * {
6
- * content: [{ type: 'text', text: yamlString }],
7
- * structuredContent?: object, // Not always present
8
- * isError: boolean
9
- * }
10
- *
11
- * The YAML contains:
12
- * - instructions: string
13
- * - results: [{ id, status, data, mainResearchGoal, researchGoal, reasoning }]
14
- * - hasResultsStatusHints / emptyStatusHints / errorStatusHints: string[]
15
- *
16
- * This utility extracts data AND preserves the valuable MCP hints.
17
- */
18
-
19
1
  import yaml from 'js-yaml';
20
2
 
21
3
  interface McpToolResponse {
@@ -37,34 +19,32 @@ interface BulkResultItem {
37
19
  research: ResearchContext;
38
20
  }
39
21
 
40
- /**
41
- * Parsed response with data, hints, and research context
42
- */
22
+
43
23
  export interface ParsedResponse {
44
24
  data: Record<string, unknown>;
45
25
  isError: boolean;
46
- /** MCP workflow hints - critical for agent guidance */
26
+
47
27
  hints: string[];
48
- /** Research context preserved from the query */
28
+
49
29
  research: ResearchContext;
50
- /** Raw status from MCP (hasResults, empty, error) */
30
+
51
31
  status: 'hasResults' | 'empty' | 'error' | 'unknown';
52
32
  }
53
33
 
54
34
  interface ParsedBulkResponse {
55
- /** All results from bulk query */
35
+
56
36
  results: BulkResultItem[];
57
- /** Categorized hints by status */
37
+
58
38
  hints: {
59
39
  hasResults: string[];
60
40
  empty: string[];
61
41
  error: string[];
62
42
  };
63
- /** Bulk operation instructions */
43
+
64
44
  instructions: string;
65
- /** True if all queries failed */
45
+
66
46
  isError: boolean;
67
- /** Count of results by status */
47
+
68
48
  counts: {
69
49
  total: number;
70
50
  hasResults: number;
@@ -73,17 +53,7 @@ interface ParsedBulkResponse {
73
53
  };
74
54
  }
75
55
 
76
- /**
77
- * Extract structured data from an MCP tool response
78
- *
79
- * Priority:
80
- * 1. Use structuredContent if available (direct object access)
81
- * 2. Parse YAML from content[0].text and extract results[0].data
82
- *
83
- * Also extracts:
84
- * - MCP hints (hasResultsStatusHints, emptyStatusHints, errorStatusHints)
85
- * - Research context (mainResearchGoal, researchGoal, reasoning)
86
- */
56
+
87
57
  export function parseToolResponse(response: McpToolResponse): ParsedResponse {
88
58
  const emptyResult: ParsedResponse = {
89
59
  data: {},
@@ -93,23 +63,20 @@ export function parseToolResponse(response: McpToolResponse): ParsedResponse {
93
63
  status: 'unknown',
94
64
  };
95
65
 
96
- // Option 1: structuredContent is available (preferred for data, but no hints)
97
66
  if (response.structuredContent && typeof response.structuredContent === 'object') {
98
67
  return {
99
68
  data: response.structuredContent,
100
69
  isError: Boolean(response.isError),
101
- hints: [], // structuredContent doesn't include hints
70
+ hints: [],
102
71
  research: {},
103
72
  status: 'unknown',
104
73
  };
105
74
  }
106
75
 
107
- // Option 2: Parse YAML from content text (includes hints!)
108
76
  if (response.content && response.content[0]?.text) {
109
77
  try {
110
78
  const parsed = yaml.load(response.content[0].text) as Record<string, unknown>;
111
79
 
112
- // Extract hints based on status
113
80
  let hints: string[] = [];
114
81
  if (Array.isArray(parsed.hasResultsStatusHints)) {
115
82
  hints = parsed.hasResultsStatusHints as string[];
@@ -119,12 +86,10 @@ export function parseToolResponse(response: McpToolResponse): ParsedResponse {
119
86
  hints = parsed.errorStatusHints as string[];
120
87
  }
121
88
 
122
- // Extract data from results[0].data (bulk response format)
123
89
  if (parsed && Array.isArray(parsed.results) && parsed.results.length > 0) {
124
90
  const firstResult = parsed.results[0] as Record<string, unknown>;
125
91
  const resultStatus = String(firstResult.status || 'unknown');
126
92
 
127
- // Extract research context
128
93
  const research: ResearchContext = {
129
94
  mainResearchGoal: typeof firstResult.mainResearchGoal === 'string'
130
95
  ? firstResult.mainResearchGoal : undefined,
@@ -145,7 +110,6 @@ export function parseToolResponse(response: McpToolResponse): ParsedResponse {
145
110
  }
146
111
  }
147
112
 
148
- // Fallback: return parsed object directly
149
113
  return {
150
114
  data: parsed || {},
151
115
  isError: Boolean(response.isError),
@@ -154,22 +118,13 @@ export function parseToolResponse(response: McpToolResponse): ParsedResponse {
154
118
  status: 'unknown',
155
119
  };
156
120
  } catch {
157
- // YAML parsing failed, return empty
158
121
  return emptyResult;
159
122
  }
160
123
  }
161
124
 
162
- // No data found
163
125
  return emptyResult;
164
126
  }
165
127
 
166
- /**
167
- * Parse ALL results from a bulk MCP tool response.
168
- * Use this when handling multiple queries to get all results.
169
- *
170
- * @param response - Raw MCP tool response
171
- * @returns ParsedBulkResponse with all results and categorized hints
172
- */
173
128
  export function parseToolResponseBulk(response: McpToolResponse): ParsedBulkResponse {
174
129
  const emptyResult: ParsedBulkResponse = {
175
130
  results: [],
@@ -179,7 +134,6 @@ export function parseToolResponseBulk(response: McpToolResponse): ParsedBulkResp
179
134
  counts: { total: 0, hasResults: 0, empty: 0, error: 0 },
180
135
  };
181
136
 
182
- // Parse YAML from content text
183
137
  if (!response.content || !response.content[0]?.text) {
184
138
  return emptyResult;
185
139
  }
@@ -191,7 +145,6 @@ export function parseToolResponseBulk(response: McpToolResponse): ParsedBulkResp
191
145
  return emptyResult;
192
146
  }
193
147
 
194
- // Extract all results
195
148
  const results: BulkResultItem[] = [];
196
149
  let hasResultsCount = 0;
197
150
  let emptyCount = 0;
@@ -219,7 +172,6 @@ export function parseToolResponseBulk(response: McpToolResponse): ParsedBulkResp
219
172
  });
220
173
  }
221
174
 
222
- // Extract categorized hints
223
175
  const hints = {
224
176
  hasResults: Array.isArray(parsed.hasResultsStatusHints)
225
177
  ? (parsed.hasResultsStatusHints as string[])
@@ -1,9 +1,3 @@
1
- /**
2
- * Retry utilities with exponential backoff for resilient tool calls.
3
- *
4
- * @module utils/retry
5
- */
6
-
7
1
  import {
8
2
  getErrorStatus,
9
3
  hasStatusIn,
@@ -19,13 +13,9 @@ export interface RetryConfig {
19
13
  retryOn: (error: unknown) => boolean;
20
14
  }
21
15
 
22
- /**
23
- * Pre-configured retry strategies for different tool categories
24
- */
16
+
25
17
  export const RETRY_CONFIGS = {
26
- /**
27
- * LSP tools - may need warm-up time
28
- */
18
+
29
19
  lsp: {
30
20
  maxAttempts: 3,
31
21
  initialDelayMs: 500,
@@ -35,9 +25,7 @@ export const RETRY_CONFIGS = {
35
25
  isLspNotReady(err) || isTimeout(err) || isConnectionRefused(err),
36
26
  },
37
27
 
38
- /**
39
- * GitHub API - rate limits and server errors
40
- */
28
+
41
29
  github: {
42
30
  maxAttempts: 3,
43
31
  initialDelayMs: 1000,
@@ -47,9 +35,7 @@ export const RETRY_CONFIGS = {
47
35
  isRateLimited(err) || isServerError(err) || isTimeout(err),
48
36
  },
49
37
 
50
- /**
51
- * Package APIs (npm/PyPI) - similar to GitHub
52
- */
38
+
53
39
  package: {
54
40
  maxAttempts: 3,
55
41
  initialDelayMs: 1000,
@@ -59,9 +45,7 @@ export const RETRY_CONFIGS = {
59
45
  isRateLimited(err) || isServerError(err) || isTimeout(err),
60
46
  },
61
47
 
62
- /**
63
- * Local file operations - quick retries
64
- */
48
+
65
49
  local: {
66
50
  maxAttempts: 2,
67
51
  initialDelayMs: 100,
@@ -71,32 +55,12 @@ export const RETRY_CONFIGS = {
71
55
  },
72
56
  } as const satisfies Record<string, RetryConfig>;
73
57
 
74
- /**
75
- * Context for retry logging
76
- */
58
+
77
59
  interface RetryContext {
78
60
  tool: string;
79
61
  params?: unknown;
80
62
  }
81
63
 
82
- /**
83
- * Execute an operation with retry logic and exponential backoff.
84
- *
85
- * @param operation - Async function to execute
86
- * @param config - Retry configuration
87
- * @param context - Optional context for logging
88
- * @returns Result of the operation
89
- * @throws Last error if all retries exhausted
90
- *
91
- * @example
92
- * ```typescript
93
- * const result = await withRetry(
94
- * () => lspGotoDefinition({ queries }),
95
- * RETRY_CONFIGS.lsp,
96
- * { tool: 'lspGotoDefinition' }
97
- * );
98
- * ```
99
- */
100
64
  export async function withRetry<T>(
101
65
  operation: () => Promise<T>,
102
66
  config: RetryConfig,
@@ -111,12 +75,10 @@ export async function withRetry<T>(
111
75
  } catch (error) {
112
76
  lastError = error;
113
77
 
114
- // Don't retry if error type doesn't match or last attempt
115
78
  if (!config.retryOn(error) || attempt === config.maxAttempts) {
116
79
  throw error;
117
80
  }
118
81
 
119
- // Log retry attempt
120
82
  const toolName = context?.tool || 'operation';
121
83
  console.log(
122
84
  `⟳ Retry ${attempt}/${config.maxAttempts} for ${toolName} in ${delay}ms`
@@ -130,11 +92,7 @@ export async function withRetry<T>(
130
92
  throw lastError;
131
93
  }
132
94
 
133
- // =============================================================================
134
- // Error Type Detection
135
- // =============================================================================
136
95
 
137
- // Error codes for reliable detection (check these first)
138
96
  const RATE_LIMIT_CODES = [403, 429] as const;
139
97
  const RATE_LIMIT_PATTERNS = [/rate\s*limit/i, /too\s*many\s*requests/i] as const;
140
98
 
@@ -148,70 +106,49 @@ const FILE_BUSY_CODES = ['EBUSY', 'EAGAIN', 'ENOTEMPTY'] as const;
148
106
 
149
107
  const CONNECTION_REFUSED_CODES = ['ECONNREFUSED', 'ENOTFOUND', 'EHOSTUNREACH'] as const;
150
108
 
151
- /**
152
- * Check if error indicates GitHub rate limiting
153
- */
109
+
154
110
  function isRateLimited(err: unknown): boolean {
155
- // Check status codes first (more reliable)
156
111
  if (hasStatusIn(err, RATE_LIMIT_CODES)) {
157
112
  return true;
158
113
  }
159
114
 
160
- // Fall back to message patterns
161
115
  return messageMatches(err, RATE_LIMIT_PATTERNS);
162
116
  }
163
117
 
164
- /**
165
- * Check if error indicates LSP server not ready
166
- */
118
+
167
119
  function isLspNotReady(err: unknown): boolean {
168
- // Check error codes first (more reliable)
169
120
  if (hasCodeIn(err, LSP_ERROR_CODES)) {
170
121
  return true;
171
122
  }
172
123
 
173
- // Fall back to message patterns
174
124
  return messageMatches(err, LSP_ERROR_PATTERNS);
175
125
  }
176
126
 
177
- /**
178
- * Check if error is a timeout
179
- */
127
+
180
128
  function isTimeout(err: unknown): boolean {
181
- // Check error codes first (more reliable)
182
129
  if (hasCodeIn(err, TIMEOUT_CODES)) {
183
130
  return true;
184
131
  }
185
132
 
186
- // Fall back to message patterns
187
133
  return messageMatches(err, TIMEOUT_PATTERNS);
188
134
  }
189
135
 
190
- /**
191
- * Check if error is a server error (5xx)
192
- */
136
+
193
137
  function isServerError(err: unknown): boolean {
194
138
  const status = getErrorStatus(err);
195
139
  return status !== undefined && status >= 500 && status < 600;
196
140
  }
197
141
 
198
- /**
199
- * Check if file is busy/locked
200
- */
142
+
201
143
  function isFileBusy(err: unknown): boolean {
202
144
  return hasCodeIn(err, FILE_BUSY_CODES);
203
145
  }
204
146
 
205
- /**
206
- * Check if connection was refused
207
- */
147
+
208
148
  function isConnectionRefused(err: unknown): boolean {
209
149
  return hasCodeIn(err, CONNECTION_REFUSED_CODES);
210
150
  }
211
151
 
212
- // =============================================================================
213
- // Utilities
214
- // =============================================================================
215
152
 
216
153
  const sleep = (ms: number): Promise<void> =>
217
154
  new Promise((resolve) => setTimeout(resolve, ms));