octocode-cli 1.5.0 → 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 (113) hide show
  1. package/README.md +167 -90
  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/assets/example.png +0 -0
  107. package/out/chunks/chunk-7476PETK.js +0 -309
  108. package/out/chunks/chunk-OQBJTZWK.js +0 -60
  109. package/out/chunks/chunk-UCZCF3BQ.js +0 -9
  110. package/out/chunks/commands-XBFPLHSQ.js +0 -8
  111. package/out/chunks/main-help-ULF5PAQY.js +0 -10
  112. package/out/chunks/tool-command-M6VA7P2F.js +0 -8
  113. package/skills/octocode-research/AGENTS.md +0 -349
@@ -1,9 +1,3 @@
1
- /**
2
- * Integration tests for circuit breaker functionality.
3
- *
4
- * @module tests/integration/circuitBreaker
5
- */
6
-
7
1
  import { describe, it, expect, beforeEach, vi } from 'vitest';
8
2
  import {
9
3
  withCircuitBreaker,
@@ -15,7 +9,6 @@ import {
15
9
 
16
10
  describe('Circuit Breaker', () => {
17
11
  beforeEach(() => {
18
- // Reset all circuits before each test
19
12
  resetCircuit('test');
20
13
  resetCircuit('lsp');
21
14
  resetCircuit('github');
@@ -45,7 +38,6 @@ describe('Circuit Breaker', () => {
45
38
 
46
39
  describe('State Transitions', () => {
47
40
  it('opens after reaching failure threshold', async () => {
48
- // Configure test circuit with low threshold
49
41
  configureCircuit('test', {
50
42
  failureThreshold: 2,
51
43
  successThreshold: 1,
@@ -54,17 +46,14 @@ describe('Circuit Breaker', () => {
54
46
 
55
47
  const failingFn = () => Promise.reject(new Error('fail'));
56
48
 
57
- // First failure
58
49
  await withCircuitBreaker('test', failingFn).catch(() => {});
59
50
  expect(getCircuitState('test').state).toBe('closed');
60
51
 
61
- // Second failure - should open circuit
62
52
  await withCircuitBreaker('test', failingFn).catch(() => {});
63
53
  expect(getCircuitState('test').state).toBe('open');
64
54
  });
65
55
 
66
56
  it('rejects immediately when circuit is open', async () => {
67
- // Force circuit open
68
57
  configureCircuit('test', {
69
58
  failureThreshold: 1,
70
59
  successThreshold: 1,
@@ -73,7 +62,6 @@ describe('Circuit Breaker', () => {
73
62
 
74
63
  await withCircuitBreaker('test', () => Promise.reject(new Error('fail'))).catch(() => {});
75
64
 
76
- // Next call should be rejected immediately
77
65
  await expect(
78
66
  withCircuitBreaker('test', () => Promise.resolve('ok'))
79
67
  ).rejects.toThrow(CircuitOpenError);
@@ -88,14 +76,11 @@ describe('Circuit Breaker', () => {
88
76
  resetTimeoutMs: 1000,
89
77
  });
90
78
 
91
- // Open the circuit
92
79
  await withCircuitBreaker('test', () => Promise.reject(new Error())).catch(() => {});
93
80
  expect(getCircuitState('test').state).toBe('open');
94
81
 
95
- // Advance time past reset timeout
96
82
  vi.advanceTimersByTime(1100);
97
83
 
98
- // Next call should be allowed (half-open state)
99
84
  const result = await withCircuitBreaker('test', () => Promise.resolve('recovered'));
100
85
  expect(result).toBe('recovered');
101
86
  expect(getCircuitState('test').state).toBe('closed');
@@ -112,13 +97,10 @@ describe('Circuit Breaker', () => {
112
97
  resetTimeoutMs: 100,
113
98
  });
114
99
 
115
- // Open circuit
116
100
  await withCircuitBreaker('test', () => Promise.reject(new Error())).catch(() => {});
117
101
 
118
- // Wait for reset
119
102
  vi.advanceTimersByTime(150);
120
103
 
121
- // Successful call should close circuit
122
104
  await withCircuitBreaker('test', () => Promise.resolve('ok'));
123
105
  expect(getCircuitState('test').state).toBe('closed');
124
106
 
@@ -134,13 +116,10 @@ describe('Circuit Breaker', () => {
134
116
  resetTimeoutMs: 100,
135
117
  });
136
118
 
137
- // Open circuit
138
119
  await withCircuitBreaker('test', () => Promise.reject(new Error())).catch(() => {});
139
120
 
140
- // Wait for reset
141
121
  vi.advanceTimersByTime(150);
142
122
 
143
- // Another failure should reopen circuit
144
123
  await withCircuitBreaker('test', () => Promise.reject(new Error())).catch(() => {});
145
124
  expect(getCircuitState('test').state).toBe('open');
146
125
 
@@ -189,15 +168,12 @@ describe('Circuit Breaker', () => {
189
168
  resetTimeoutMs: 10000,
190
169
  });
191
170
 
192
- // Open circuit
193
171
  await withCircuitBreaker('test', () => Promise.reject(new Error())).catch(() => {});
194
172
  expect(getCircuitState('test').state).toBe('open');
195
173
 
196
- // Reset
197
174
  resetCircuit('test');
198
175
  expect(getCircuitState('test').state).toBe('closed');
199
176
 
200
- // Should allow requests again
201
177
  const result = await withCircuitBreaker('test', () => Promise.resolve('ok'));
202
178
  expect(result).toBe('ok');
203
179
  });
@@ -1,9 +1,3 @@
1
- /**
2
- * Integration tests for route validation and response handling.
3
- *
4
- * @module tests/integration/routes
5
- */
6
-
7
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
8
2
 
9
3
  import { describe, it, expect, vi } from 'vitest';
@@ -14,7 +8,6 @@ import { githubRoutes } from '../../routes/github.js';
14
8
  import { lspRoutes } from '../../routes/lsp.js';
15
9
  import { packageRoutes } from '../../routes/package.js';
16
10
 
17
- // Mock the MCP cache
18
11
  vi.mock('../../mcpCache.js', () => ({
19
12
  getMcpContent: vi.fn().mockReturnValue({
20
13
  tools: {},
@@ -27,7 +20,6 @@ vi.mock('../../mcpCache.js', () => ({
27
20
  isMcpInitialized: vi.fn().mockReturnValue(true),
28
21
  }));
29
22
 
30
- // Mock the MCP tools
31
23
  vi.mock('../../index.js', () => ({
32
24
  localSearchCode: vi.fn().mockResolvedValue({
33
25
  content: [{ type: 'text', text: 'results:\n - status: hasResults\n data:\n files: []\n totalMatches: 0' }],
@@ -70,11 +62,9 @@ vi.mock('../../index.js', () => ({
70
62
  }),
71
63
  }));
72
64
 
73
- // Create app factory with routes (matching actual server.ts configuration)
74
65
  function createApp(): any {
75
66
  const app = express();
76
67
  app.use(express.json());
77
- // Routes are mounted at root, matching server.ts
78
68
  app.use('/', localRoutes);
79
69
  app.use('/', githubRoutes);
80
70
  app.use('/', lspRoutes);
@@ -1,15 +1,3 @@
1
- /**
2
- * Integration test: server lifecycle (startup, PID file, shutdown).
3
- *
4
- * All external calls (network, process spawning, fs) are mocked.
5
- * Tests verify:
6
- * 1. Server creates PID file on startup
7
- * 2. Health endpoint reports ok after initialization
8
- * 3. PID file is removed on graceful shutdown
9
- * 4. server-init detects an already-running server (fast path)
10
- * 5. server-init starts server when not running, waits for health
11
- */
12
-
13
1
  /* eslint-disable @typescript-eslint/no-explicit-any */
14
2
 
15
3
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
@@ -17,7 +5,6 @@ import request from 'supertest';
17
5
  import { join } from 'path';
18
6
  import { homedir } from 'os';
19
7
 
20
- // ── Mocks (hoisted before any imports) ──────────────────────────────────────
21
8
 
22
9
  vi.mock('../../mcpCache.js', () => ({
23
10
  getMcpContent: vi.fn().mockReturnValue({
@@ -107,7 +94,6 @@ vi.mock('fs', async (importOriginal) => {
107
94
  };
108
95
  });
109
96
 
110
- // ── Tests ───────────────────────────────────────────────────────────────────
111
97
 
112
98
  import { createServer } from '../../server.js';
113
99
 
@@ -159,8 +145,7 @@ describe('Server Lifecycle', () => {
159
145
  }) as any
160
146
  );
161
147
 
162
- // startServer creates its own app internally, so we test writePidFile via
163
- // the PID_FILE export and fs mock instead
148
+
164
149
  const OCTOCODE_DIR = process.env.OCTOCODE_HOME || join(homedir(), '.octocode');
165
150
  const expectedPidFile = PID_FILE;
166
151
 
@@ -188,7 +173,6 @@ describe('Server Lifecycle', () => {
188
173
  globalThis.fetch = mockFetch as any;
189
174
 
190
175
  try {
191
- // Simulate checkHealth logic from server-init.ts
192
176
  const response = await globalThis.fetch('http://localhost:1987/health', {
193
177
  signal: AbortSignal.timeout(5000),
194
178
  });
@@ -218,7 +202,6 @@ describe('Server Lifecycle', () => {
218
202
  globalThis.fetch = mockFetch as any;
219
203
 
220
204
  try {
221
- // Simulate waitForReady polling logic
222
205
  let ready = false;
223
206
  for (let i = 0; i < 5 && !ready; i++) {
224
207
  try {
@@ -228,8 +211,8 @@ describe('Server Lifecycle', () => {
228
211
  const body = await (res as any).json();
229
212
  if (body.status === 'ok') ready = true;
230
213
  } catch {
231
- // retry
232
- }
214
+ void 0;
215
+ }
233
216
  }
234
217
 
235
218
  expect(ready).toBe(true);
@@ -268,8 +251,8 @@ describe('Server Lifecycle', () => {
268
251
  const body = await (res as any).json();
269
252
  if (body.status === 'ok') ready = true;
270
253
  } catch {
271
- // retry
272
- }
254
+ void 0;
255
+ }
273
256
  }
274
257
 
275
258
  expect(ready).toBe(true);
@@ -108,9 +108,6 @@ describe('Tools Routes', () => {
108
108
  vi.clearAllMocks();
109
109
  });
110
110
 
111
- // =========================================================================
112
- // GET /tools/list
113
- // =========================================================================
114
111
  describe('GET /tools/list', () => {
115
112
  it('returns 200 with success true', async () => {
116
113
  const res = await request(app).get('/tools/list');
@@ -149,9 +146,6 @@ describe('Tools Routes', () => {
149
146
  });
150
147
  });
151
148
 
152
- // =========================================================================
153
- // GET /tools/info
154
- // =========================================================================
155
149
  describe('GET /tools/info', () => {
156
150
  it('returns all tools from MCP content', async () => {
157
151
  const res = await request(app).get('/tools/info');
@@ -188,9 +182,6 @@ describe('Tools Routes', () => {
188
182
  });
189
183
  });
190
184
 
191
- // =========================================================================
192
- // GET /tools/info/:toolName
193
- // =========================================================================
194
185
  describe('GET /tools/info/:toolName', () => {
195
186
  it('returns tool info for valid tool', async () => {
196
187
  const res = await request(app).get('/tools/info/localSearchCode');
@@ -238,9 +229,6 @@ describe('Tools Routes', () => {
238
229
  });
239
230
  });
240
231
 
241
- // =========================================================================
242
- // GET /tools/metadata
243
- // =========================================================================
244
232
  describe('GET /tools/metadata', () => {
245
233
  it('returns metadata summary', async () => {
246
234
  const res = await request(app).get('/tools/metadata');
@@ -259,9 +247,6 @@ describe('Tools Routes', () => {
259
247
  });
260
248
  });
261
249
 
262
- // =========================================================================
263
- // GET /tools/schemas
264
- // =========================================================================
265
250
  describe('GET /tools/schemas', () => {
266
251
  it('returns all tool schemas', async () => {
267
252
  const res = await request(app).get('/tools/schemas');
@@ -288,9 +273,6 @@ describe('Tools Routes', () => {
288
273
  });
289
274
  });
290
275
 
291
- // =========================================================================
292
- // GET /tools/system
293
- // =========================================================================
294
276
  describe('GET /tools/system', () => {
295
277
  it('returns system instructions', async () => {
296
278
  const res = await request(app).get('/tools/system');
@@ -307,9 +289,6 @@ describe('Tools Routes', () => {
307
289
  });
308
290
  });
309
291
 
310
- // =========================================================================
311
- // GET /tools/initContext
312
- // =========================================================================
313
292
  describe('GET /tools/initContext', () => {
314
293
  it('returns combined system prompt and schemas', async () => {
315
294
  const res = await request(app).get('/tools/initContext');
@@ -339,9 +318,6 @@ describe('Tools Routes', () => {
339
318
  });
340
319
  });
341
320
 
342
- // =========================================================================
343
- // POST /tools/call/:toolName
344
- // =========================================================================
345
321
  describe('POST /tools/call/:toolName', () => {
346
322
  const validLocalQuery = {
347
323
  queries: [{
@@ -461,9 +437,6 @@ describe('Tools Routes', () => {
461
437
  });
462
438
  });
463
439
 
464
- // =========================================================================
465
- // Readiness (503 when not initialized)
466
- // =========================================================================
467
440
  describe('Readiness gate', () => {
468
441
  it('returns 503 for /tools/list when not initialized', async () => {
469
442
  const { isMcpInitialized } = await import('../../mcpCache.js');
@@ -1,9 +1,3 @@
1
- /**
2
- * Unit tests for circuit breaker utilities.
3
- *
4
- * @module tests/unit/circuitBreaker
5
- */
6
-
7
1
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
8
2
  import {
9
3
  withCircuitBreaker,
@@ -16,7 +10,6 @@ import {
16
10
 
17
11
  describe('withCircuitBreaker', () => {
18
12
  beforeEach(() => {
19
- // Reset test circuit before each test
20
13
  resetCircuit('test');
21
14
  vi.useFakeTimers();
22
15
  });
@@ -39,7 +32,6 @@ describe('withCircuitBreaker', () => {
39
32
  configureCircuit('test', { failureThreshold: 3 });
40
33
  const operation = vi.fn().mockRejectedValue(new Error('fail'));
41
34
 
42
- // First 3 failures should open the circuit
43
35
  for (let i = 0; i < 3; i++) {
44
36
  await expect(withCircuitBreaker('test', operation)).rejects.toThrow('fail');
45
37
  }
@@ -52,10 +44,8 @@ describe('withCircuitBreaker', () => {
52
44
  const operation = vi.fn().mockRejectedValue(new Error('fail'));
53
45
  const fallback = vi.fn().mockReturnValue('fallback');
54
46
 
55
- // Open the circuit
56
47
  await expect(withCircuitBreaker('test', operation)).rejects.toThrow();
57
48
 
58
- // Should use fallback
59
49
  const result = await withCircuitBreaker('test', operation, fallback);
60
50
 
61
51
  expect(result).toBe('fallback');
@@ -66,10 +56,8 @@ describe('withCircuitBreaker', () => {
66
56
  configureCircuit('test', { failureThreshold: 1, resetTimeoutMs: 10000 });
67
57
  const operation = vi.fn().mockRejectedValue(new Error('fail'));
68
58
 
69
- // Open the circuit
70
59
  await expect(withCircuitBreaker('test', operation)).rejects.toThrow();
71
60
 
72
- // Should throw CircuitOpenError
73
61
  await expect(withCircuitBreaker('test', operation)).rejects.toBeInstanceOf(
74
62
  CircuitOpenError
75
63
  );
@@ -82,14 +70,11 @@ describe('withCircuitBreaker', () => {
82
70
  .mockRejectedValueOnce(new Error('fail'))
83
71
  .mockResolvedValue('success');
84
72
 
85
- // Open the circuit
86
73
  await expect(withCircuitBreaker('test', operation)).rejects.toThrow();
87
74
  expect(getCircuitState('test').state).toBe('open');
88
75
 
89
- // Advance time past reset timeout
90
76
  vi.advanceTimersByTime(1001);
91
77
 
92
- // Next attempt should try (half-open)
93
78
  const result = await withCircuitBreaker('test', operation);
94
79
  expect(result).toBe('success');
95
80
  });
@@ -104,17 +89,13 @@ describe('withCircuitBreaker', () => {
104
89
  const failOp = vi.fn().mockRejectedValue(new Error('fail'));
105
90
  const successOp = vi.fn().mockResolvedValue('success');
106
91
 
107
- // Open the circuit
108
92
  await expect(withCircuitBreaker('test', failOp)).rejects.toThrow();
109
93
 
110
- // Advance time to half-open
111
94
  vi.advanceTimersByTime(1001);
112
95
 
113
- // First success - still half-open
114
96
  await withCircuitBreaker('test', successOp);
115
97
  expect(getCircuitState('test').state).toBe('half-open');
116
98
 
117
- // Second success - should close
118
99
  await withCircuitBreaker('test', successOp);
119
100
  expect(getCircuitState('test').state).toBe('closed');
120
101
  });
@@ -124,13 +105,10 @@ describe('withCircuitBreaker', () => {
124
105
 
125
106
  const operation = vi.fn().mockRejectedValue(new Error('fail'));
126
107
 
127
- // Open the circuit
128
108
  await expect(withCircuitBreaker('test', operation)).rejects.toThrow();
129
109
 
130
- // Advance time to half-open
131
110
  vi.advanceTimersByTime(1001);
132
111
 
133
- // Fail in half-open - should go back to open
134
112
  await expect(withCircuitBreaker('test', operation)).rejects.toThrow();
135
113
  expect(getCircuitState('test').state).toBe('open');
136
114
  });
@@ -141,16 +119,13 @@ describe('withCircuitBreaker', () => {
141
119
  const failOp = vi.fn().mockRejectedValue(new Error('fail'));
142
120
  const successOp = vi.fn().mockResolvedValue('success');
143
121
 
144
- // 2 failures
145
122
  await expect(withCircuitBreaker('test', failOp)).rejects.toThrow();
146
123
  await expect(withCircuitBreaker('test', failOp)).rejects.toThrow();
147
124
  expect(getCircuitState('test').failures).toBe(2);
148
125
 
149
- // 1 success resets failures
150
126
  await withCircuitBreaker('test', successOp);
151
127
  expect(getCircuitState('test').failures).toBe(0);
152
128
 
153
- // Still closed
154
129
  expect(getCircuitState('test').state).toBe('closed');
155
130
  });
156
131
  });
@@ -195,11 +170,9 @@ describe('resetCircuit', () => {
195
170
  configureCircuit('test', { failureThreshold: 1 });
196
171
  const operation = vi.fn().mockRejectedValue(new Error('fail'));
197
172
 
198
- // Open the circuit
199
173
  await expect(withCircuitBreaker('test', operation)).rejects.toThrow();
200
174
  expect(getCircuitState('test').state).toBe('open');
201
175
 
202
- // Reset
203
176
  resetCircuit('test');
204
177
 
205
178
  expect(getCircuitState('test').state).toBe('closed');
@@ -218,10 +191,8 @@ describe('getAllCircuitStates', () => {
218
191
  const failOp = vi.fn().mockRejectedValue(new Error('fail'));
219
192
  const successOp = vi.fn().mockResolvedValue('success');
220
193
 
221
- // Open circuit-a
222
194
  await expect(withCircuitBreaker('circuit-a', failOp)).rejects.toThrow();
223
195
 
224
- // Keep circuit-b closed
225
196
  await withCircuitBreaker('circuit-b', successOp);
226
197
 
227
198
  const states = getAllCircuitStates();
@@ -1,28 +1,19 @@
1
- /**
2
- * Unit tests for error handler middleware.
3
- *
4
- * @module tests/unit/errorHandler
5
- */
6
-
7
1
  import { describe, it, expect, vi, beforeEach } from 'vitest';
8
2
  import type { Request, Response, NextFunction } from 'express';
9
3
  import { errorHandler, type ApiError } from '../../middleware/errorHandler.js';
10
- import type { z } from 'zod/v4';
4
+ import type { z } from 'zod';
11
5
  import { fireAndForgetWithTimeout } from '../../utils/asyncTimeout.js';
12
6
 
13
- // Mock the logger
14
7
  vi.mock('../../utils/logger.js', () => ({
15
8
  logError: vi.fn(),
16
9
  logWarn: vi.fn(),
17
10
  sanitizeQueryParams: vi.fn((q) => q),
18
11
  }));
19
12
 
20
- // Mock the session error logging
21
13
  vi.mock('../../index.js', () => ({
22
14
  logSessionError: vi.fn().mockResolvedValue(undefined),
23
15
  }));
24
16
 
25
- // Mock the async timeout
26
17
  vi.mock('../../utils/asyncTimeout.js', () => ({
27
18
  fireAndForgetWithTimeout: vi.fn(),
28
19
  }));
@@ -166,7 +157,6 @@ describe('errorHandler', () => {
166
157
 
167
158
  errorHandler(error, mockReq as Request, mockRes as Response, mockNext);
168
159
 
169
- // Verify fireAndForgetWithTimeout was called (tool name is passed to logSessionError)
170
160
  expect(fireAndForgetWithTimeout).toHaveBeenCalled();
171
161
  });
172
162
 
@@ -176,7 +166,6 @@ describe('errorHandler', () => {
176
166
 
177
167
  errorHandler(error, mockReq as Request, mockRes as Response, mockNext);
178
168
 
179
- // Tool name should be 'unknown' for non-tool paths
180
169
  expect(fireAndForgetWithTimeout).toHaveBeenCalled();
181
170
  });
182
171
  });
@@ -1,11 +1,3 @@
1
- /**
2
- * Unit tests for HTTP preprocessing utilities.
3
- *
4
- * Tests type coercion, path safety, and array handling for HTTP query strings.
5
- *
6
- * @module tests/unit/httpPreprocess
7
- */
8
-
9
1
  import { describe, it, expect } from 'vitest';
10
2
  import { toNumber, toBoolean, toArray, safePath, numericString, booleanString, stringArray } from '../../validation/httpPreprocess.js';
11
3
 
@@ -23,7 +15,7 @@ describe('toNumber', () => {
23
15
 
24
16
  it('returns non-numeric strings as-is', () => {
25
17
  expect(toNumber('abc')).toBe('abc');
26
- expect(toNumber('12.5')).toBe('12.5'); // floats not supported
18
+ expect(toNumber('12.5')).toBe('12.5');
27
19
  expect(toNumber('12abc')).toBe('12abc');
28
20
  });
29
21
 
@@ -1,9 +1,3 @@
1
- /**
2
- * Unit tests for logger utilities.
3
- *
4
- * @module tests/unit/logger
5
- */
6
-
7
1
  import { describe, it, expect } from 'vitest';
8
2
  import { sanitizeQueryParams } from '../../utils/logger.js';
9
3
 
@@ -125,7 +119,6 @@ describe('sanitizeQueryParams', () => {
125
119
  };
126
120
  const result = sanitizeQueryParams(query);
127
121
 
128
- // Only top-level keys are checked
129
122
  expect(result.config).toEqual({ token: 'nested' });
130
123
  expect(result.path).toBe('/src');
131
124
  });
@@ -1,12 +1,6 @@
1
- /**
2
- * Unit tests for query parser and validation utilities.
3
- *
4
- * @module tests/unit/queryParser
5
- */
6
-
7
1
  import { describe, it, expect } from 'vitest';
8
2
  import { validateToolCallBody, getValidationHints, MAX_QUERIES } from '../../validation/toolCallSchema.js';
9
- import type { z } from 'zod/v4';
3
+ import type { z } from 'zod';
10
4
 
11
5
  describe('validateToolCallBody', () => {
12
6
  describe('valid inputs', () => {
@@ -1,13 +1,6 @@
1
- /**
2
- * Unit tests for response builder utilities.
3
- *
4
- * @module tests/unit/responseBuilder
5
- */
6
-
7
1
  import { describe, it, expect, vi } from 'vitest';
8
2
  import { ResearchResponse, detectLanguageFromPath } from '../../utils/responseBuilder.js';
9
3
 
10
- // Mock octocode-mcp public module
11
4
  vi.mock('octocode-mcp/public', () => ({
12
5
  createRoleBasedResult: vi.fn((opts) => ({
13
6
  content: [
@@ -169,7 +162,6 @@ describe('ResearchResponse.searchResults', () => {
169
162
  });
170
163
 
171
164
  it('handles non-string preview gracefully (no crash)', () => {
172
- // preview could be a number or other non-string type from malformed data
173
165
  const result = ResearchResponse.searchResults({
174
166
  files: [
175
167
  { path: 'src/utils.ts', matches: 1, preview: 42 as unknown as string },
@@ -177,7 +169,6 @@ describe('ResearchResponse.searchResults', () => {
177
169
  totalMatches: 1,
178
170
  });
179
171
 
180
- // Should not crash and should not include garbled output
181
172
  expect(result.content[0].text).toContain('src/utils.ts');
182
173
  expect(result.content[0].text).not.toContain('.slice');
183
174
  });
@@ -355,7 +346,6 @@ describe('ResearchResponse.repoStructure', () => {
355
346
  structure: { files, folders: [] },
356
347
  });
357
348
 
358
- // Only first 20 files shown
359
349
  expect(result.content[0].text).not.toContain('file24.ts');
360
350
  });
361
351
  });
@@ -406,7 +396,6 @@ describe('ResearchResponse.packageSearch', () => {
406
396
  registry: 'npm',
407
397
  });
408
398
 
409
- // Should use 'No description' fallback for non-string
410
399
  expect(result.content[0].text).toContain('No description');
411
400
  });
412
401
  });
@@ -1,9 +1,3 @@
1
- /**
2
- * Unit tests for retry utilities.
3
- *
4
- * @module tests/unit/retry
5
- */
6
-
7
1
  import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
8
2
  import { withRetry, RETRY_CONFIGS } from '../../utils/retry.js';
9
3
 
@@ -34,10 +28,8 @@ describe('withRetry', () => {
34
28
 
35
29
  const resultPromise = withRetry(operation, RETRY_CONFIGS.local);
36
30
 
37
- // First call fails immediately
38
31
  await vi.advanceTimersByTimeAsync(0);
39
32
 
40
- // Wait for delay and retry
41
33
  await vi.advanceTimersByTimeAsync(100);
42
34
 
43
35
  const result = await resultPromise;
@@ -72,14 +64,11 @@ describe('withRetry', () => {
72
64
 
73
65
  const resultPromise = withRetry(operation, config);
74
66
 
75
- // First attempt fails
76
67
  await vi.advanceTimersByTimeAsync(0);
77
68
 
78
- // First retry after 100ms
79
69
  await vi.advanceTimersByTimeAsync(100);
80
70
  expect(operation).toHaveBeenCalledTimes(2);
81
71
 
82
- // Second retry after 200ms (100 * 2)
83
72
  await vi.advanceTimersByTimeAsync(200);
84
73
 
85
74
  const result = await resultPromise;
@@ -97,20 +86,17 @@ describe('withRetry', () => {
97
86
  const config = {
98
87
  maxAttempts: 3,
99
88
  initialDelayMs: 500,
100
- maxDelayMs: 600, // Cap at 600ms
89
+ maxDelayMs: 600,
101
90
  backoffMultiplier: 2,
102
91
  retryOn: (err: unknown) => (err as { code?: string })?.code === 'ETIMEDOUT',
103
92
  };
104
93
 
105
94
  const resultPromise = withRetry(operation, config);
106
95
 
107
- // First attempt fails
108
96
  await vi.advanceTimersByTimeAsync(0);
109
97
 
110
- // First retry after 500ms
111
98
  await vi.advanceTimersByTimeAsync(500);
112
99
 
113
- // Second retry should be capped at 600ms (not 1000ms)
114
100
  await vi.advanceTimersByTimeAsync(600);
115
101
 
116
102
  const result = await resultPromise;
@@ -130,13 +116,10 @@ describe('withRetry', () => {
130
116
  retryOn: () => true,
131
117
  };
132
118
 
133
- // Start the operation and immediately handle the promise
134
119
  const resultPromise = withRetry(operation, config).catch((e) => e);
135
120
 
136
- // Run all timers to completion
137
121
  await vi.runAllTimersAsync();
138
122
 
139
- // Now check the result
140
123
  const result = await resultPromise;
141
124
  expect(result).toBe(finalError);
142
125
  expect(operation).toHaveBeenCalledTimes(2);