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,23 +1,8 @@
1
- /**
2
- * Tools Info Routes - Expose tool metadata via HTTP
3
- *
4
- * Routes:
5
- * GET /tools/list - List all tools (concise)
6
- * GET /tools/info - List all tools with details
7
- * GET /tools/info/:toolName - Get specific tool info
8
- * POST /tools/call/:toolName - Execute a tool directly (simplified!)
9
- *
10
- * Query params:
11
- * schema=true - Include schema information
12
- * hints=true - Include hints information
13
- */
14
-
15
1
  import { Router, type Request, type Response, type NextFunction } from 'express';
16
2
  import { getMcpContent } from '../mcpCache.js';
17
3
  import { transformToJsonSchema } from '../types/mcp.js';
18
- import { z } from 'zod/v4';
4
+ import { z } from 'zod';
19
5
 
20
- // Import Zod schemas from octocode-mcp (source of truth)
21
6
  import {
22
7
  GitHubCodeSearchQuerySchema,
23
8
  GitHubViewRepoStructureQuerySchema,
@@ -62,7 +47,6 @@ import { checkReadiness } from '../middleware/readiness.js';
62
47
 
63
48
  export const toolsRoutes = Router();
64
49
 
65
- // Apply readiness check middleware to all tools routes
66
50
  toolsRoutes.use(checkReadiness);
67
51
 
68
52
  declare const __PACKAGE_VERSION__: string;
@@ -73,27 +57,20 @@ interface ToolsInfoQuery {
73
57
  hints?: string;
74
58
  }
75
59
 
76
- // ============================================================================
77
- // Zod Schema Registry - Maps tool names to their Zod schemas (source of truth)
78
- // ============================================================================
79
60
 
80
61
  const TOOL_ZOD_SCHEMAS: Record<string, z.ZodType> = {
81
- // GitHub tools
82
62
  githubSearchCode: GitHubCodeSearchQuerySchema,
83
63
  githubGetFileContent: FileContentQuerySchema,
84
64
  githubViewRepoStructure: GitHubViewRepoStructureQuerySchema,
85
65
  githubSearchRepositories: GitHubReposSearchSingleQuerySchema,
86
66
  githubSearchPullRequests: GitHubPullRequestSearchQuerySchema,
87
- // Local tools
88
67
  localSearchCode: RipgrepQuerySchema,
89
68
  localGetFileContent: FetchContentQuerySchema,
90
69
  localFindFiles: FindFilesQuerySchema,
91
70
  localViewStructure: ViewStructureQuerySchema,
92
- // LSP tools
93
71
  lspGotoDefinition: LSPGotoDefinitionQuerySchema,
94
72
  lspFindReferences: LSPFindReferencesQuerySchema,
95
73
  lspCallHierarchy: LSPCallHierarchyQuerySchema,
96
- // Package tools
97
74
  packageSearch: PackageSearchQuerySchema,
98
75
  };
99
76
 
@@ -110,12 +87,7 @@ function getToolJsonSchema(toolName: string): Record<string, unknown> | null {
110
87
  return zodSchema ? toJsonSchema(zodSchema) : null;
111
88
  }
112
89
 
113
- /**
114
- * GET /tools/list - Static list of all tools (concise discovery)
115
- *
116
- * Returns static JSON with tool names and short descriptions.
117
- * Use /tools/info/:toolName to get full description + schema BEFORE calling a tool.
118
- */
90
+
119
91
  toolsRoutes.get('/list', (_req: Request, res: Response) => {
120
92
  res.json({
121
93
  success: true,
@@ -140,9 +112,7 @@ toolsRoutes.get('/list', (_req: Request, res: Response) => {
140
112
  });
141
113
  });
142
114
 
143
- /**
144
- * GET /tools/info - Get info about all available tools
145
- */
115
+
146
116
  toolsRoutes.get('/info', async (
147
117
  req: Request,
148
118
  res: Response,
@@ -198,21 +168,6 @@ toolsRoutes.get('/info', async (
198
168
  }
199
169
  });
200
170
 
201
- /**
202
- * GET /tools/info/:toolName - Get full info for a specific tool (call before using!)
203
- *
204
- * Returns complete description, JSON schema, and hints for a tool.
205
- * ALWAYS call this before using a tool to understand its parameters.
206
- *
207
- * @example
208
- * GET /tools/info/localSearchCode
209
- *
210
- * Response includes:
211
- * - name: Tool name
212
- * - description: Full description with usage details
213
- * - schema: Complete JSON schema with all parameters
214
- * - hints: Success/empty result hints
215
- */
216
171
  toolsRoutes.get('/info/:toolName', async (
217
172
  req: Request,
218
173
  res: Response,
@@ -223,8 +178,8 @@ toolsRoutes.get('/info/:toolName', async (
223
178
 
224
179
  const { toolName } = req.params;
225
180
  const query = req.query as ToolsInfoQuery;
226
- const includeSchema = query.schema !== 'false'; // Default true for specific tool
227
- const includeHints = query.hints !== 'false'; // Default true for specific tool
181
+ const includeSchema = query.schema !== 'false';
182
+ const includeHints = query.hints !== 'false';
228
183
 
229
184
  const tool = content.tools[toolName];
230
185
 
@@ -248,15 +203,13 @@ toolsRoutes.get('/info/:toolName', async (
248
203
  };
249
204
 
250
205
  if (includeSchema) {
251
- // Try to get the actual Zod schema (source of truth with correct types and required fields)
252
206
  const zodJsonSchema = getToolJsonSchema(toolName);
253
207
  if (zodJsonSchema) {
254
208
  result.inputSchema = zodJsonSchema;
255
- result._schemaSource = 'zod'; // Indicates this is from the authoritative Zod schema
209
+ result._schemaSource = 'zod';
256
210
  } else {
257
- // Fallback to metadata-based schema (less accurate)
258
211
  result.inputSchema = transformToJsonSchema(tool.schema, tool.name);
259
- result._schemaSource = 'metadata'; // Indicates this is a fallback
212
+ result._schemaSource = 'metadata';
260
213
  }
261
214
  }
262
215
 
@@ -277,9 +230,7 @@ toolsRoutes.get('/info/:toolName', async (
277
230
  }
278
231
  });
279
232
 
280
- /**
281
- * GET /tools/metadata - Get raw complete metadata (advanced)
282
- */
233
+
283
234
  toolsRoutes.get('/metadata', async (
284
235
  _req: Request,
285
236
  res: Response,
@@ -303,28 +254,6 @@ toolsRoutes.get('/metadata', async (
303
254
  }
304
255
  });
305
256
 
306
- /**
307
- * GET /tools/schemas - Get all tools with their complete JSON schemas
308
- *
309
- * Returns all tool names with their full JSON schemas (from Zod).
310
- * Useful for bulk schema retrieval without calling /info/:toolName for each.
311
- *
312
- * @example
313
- * GET /tools/schemas
314
- *
315
- * Response:
316
- * {
317
- * "success": true,
318
- * "data": {
319
- * "totalTools": 13,
320
- * "schemas": {
321
- * "localSearchCode": { "type": "object", "properties": {...} },
322
- * "githubSearchCode": { "type": "object", "properties": {...} },
323
- * ...
324
- * }
325
- * }
326
- * }
327
- */
328
257
  toolsRoutes.get('/schemas', async (
329
258
  _req: Request,
330
259
  res: Response,
@@ -354,21 +283,6 @@ toolsRoutes.get('/schemas', async (
354
283
  }
355
284
  });
356
285
 
357
- /**
358
- * GET /tools/system - Get the FULL system instructions
359
- *
360
- * Returns the complete system prompt that should be loaded into context FIRST.
361
- * This defines the agent's behavior, methodology, and best practices.
362
- *
363
- * @example
364
- * GET /tools/system
365
- *
366
- * Response:
367
- * {
368
- * "instructions": "## Expert Code Forensics Agent...",
369
- * "_meta": { "charCount": 5432, "version": "2.2.0" }
370
- * }
371
- */
372
286
  toolsRoutes.get('/system', async (
373
287
  _req: Request,
374
288
  res: Response,
@@ -391,25 +305,6 @@ toolsRoutes.get('/system', async (
391
305
  }
392
306
  });
393
307
 
394
- /**
395
- * GET /tools/initContext - Combined system prompt and all tool schemas
396
- *
397
- * Combines /tools/system + /tools/schemas in one call for faster init.
398
- * Returns:
399
- * - system_prompt: Full instructions
400
- * - tools_schema: All tool JSON schemas from Zod
401
- *
402
- * @example
403
- * GET /tools/initContext
404
- *
405
- * Response:
406
- * {
407
- * "success": true,
408
- * "system_prompt": "## Expert Code Forensics Agent...",
409
- * "tools_schema": { "localSearchCode": {...}, ... },
410
- * "_meta": { "promptCharCount": 5432, "toolsCount": 13, "version": "2.2.0" }
411
- * }
412
- */
413
308
  toolsRoutes.get('/initContext', async (
414
309
  _req: Request,
415
310
  res: Response,
@@ -418,7 +313,6 @@ toolsRoutes.get('/initContext', async (
418
313
  try {
419
314
  const content = getMcpContent();
420
315
 
421
- // Build schemas (same as /tools/schemas)
422
316
  const schemas: Record<string, Record<string, unknown>> = {};
423
317
  for (const toolName of Object.keys(TOOL_ZOD_SCHEMAS)) {
424
318
  const schema = getToolJsonSchema(toolName);
@@ -440,16 +334,10 @@ toolsRoutes.get('/initContext', async (
440
334
  }
441
335
  });
442
336
 
443
- // ============================================================================
444
- // Tool Registry - Maps tool names to functions and resilience wrappers
445
- // ============================================================================
446
337
 
447
338
  type ResilienceFn = <T>(fn: () => Promise<T>, toolName: string) => Promise<T>;
448
339
 
449
- /**
450
- * Tool registry entry for dynamic dispatch.
451
- * Tool functions are dispatched by name at runtime; Zod schemas validate inputs.
452
- */
340
+
453
341
  interface ToolEntry {
454
342
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
455
343
  fn: (params: any) => Promise<any>;
@@ -458,54 +346,43 @@ interface ToolEntry {
458
346
  }
459
347
 
460
348
  const TOOL_REGISTRY: Record<string, ToolEntry> = {
461
- // GitHub tools
462
349
  githubSearchCode: { fn: githubSearchCode, resilience: withGitHubResilience, category: 'github' },
463
350
  githubGetFileContent: { fn: githubGetFileContent, resilience: withGitHubResilience, category: 'github' },
464
351
  githubViewRepoStructure: { fn: githubViewRepoStructure, resilience: withGitHubResilience, category: 'github' },
465
352
  githubSearchRepositories: { fn: githubSearchRepositories, resilience: withGitHubResilience, category: 'github' },
466
353
  githubSearchPullRequests: { fn: githubSearchPullRequests, resilience: withGitHubResilience, category: 'github' },
467
354
 
468
- // Local tools
469
355
  localSearchCode: { fn: localSearchCode, resilience: withLocalResilience, category: 'local' },
470
356
  localGetFileContent: { fn: localGetFileContent, resilience: withLocalResilience, category: 'local' },
471
357
  localFindFiles: { fn: localFindFiles, resilience: withLocalResilience, category: 'local' },
472
358
  localViewStructure: { fn: localViewStructure, resilience: withLocalResilience, category: 'local' },
473
359
 
474
- // LSP tools
475
360
  lspGotoDefinition: { fn: lspGotoDefinition, resilience: withLspResilience, category: 'lsp' },
476
361
  lspFindReferences: { fn: lspFindReferences, resilience: withLspResilience, category: 'lsp' },
477
362
  lspCallHierarchy: { fn: lspCallHierarchy, resilience: withLspResilience, category: 'lsp' },
478
363
 
479
- // Package tools
480
364
  packageSearch: { fn: packageSearch, resilience: withPackageResilience, category: 'package' },
481
365
  };
482
366
 
483
- /**
484
- * Extract repository identifiers from query parameters for session logging
485
- */
367
+
486
368
  function extractReposFromQueries(queries: unknown[]): string[] {
487
369
  const repos: string[] = [];
488
370
  for (const query of queries) {
489
371
  const q = query as Record<string, unknown>;
490
- // GitHub repos: owner/repo format
491
372
  if (q.owner && q.repo) {
492
373
  repos.push(`${q.owner}/${q.repo}`);
493
374
  }
494
- // Local paths
495
375
  if (q.path && typeof q.path === 'string') {
496
376
  repos.push(q.path);
497
377
  }
498
- // LSP uris
499
378
  if (q.uri && typeof q.uri === 'string') {
500
379
  repos.push(q.uri);
501
380
  }
502
381
  }
503
- return [...new Set(repos)]; // Deduplicate
382
+ return [...new Set(repos)];
504
383
  }
505
384
 
506
- /**
507
- * Extract research parameters from the first query for logging
508
- */
385
+
509
386
  function extractResearchParams(queries: unknown[]): {
510
387
  mainResearchGoal?: string;
511
388
  researchGoal?: string;
@@ -520,48 +397,15 @@ function extractResearchParams(queries: unknown[]): {
520
397
  };
521
398
  }
522
399
 
523
- /**
524
- * POST /tools/call/:toolName - Execute a tool directly with JSON body
525
- *
526
- * This is the SIMPLIFIED way to call tools. Instead of URL-encoded query params,
527
- * just POST a JSON body with your queries array.
528
- *
529
- * @example
530
- * POST /tools/call/localSearchCode
531
- * Content-Type: application/json
532
- *
533
- * {
534
- * "queries": [{
535
- * "mainResearchGoal": "Find authentication handlers",
536
- * "researchGoal": "Locate auth middleware",
537
- * "reasoning": "Understanding auth flow",
538
- * "pattern": "authenticate",
539
- * "path": "/Users/me/project",
540
- * "type": "ts"
541
- * }]
542
- * }
543
- *
544
- * Response:
545
- * {
546
- * "tool": "localSearchCode",
547
- * "success": true,
548
- * "data": { ... parsed tool response ... },
549
- * "hints": ["Use lineHint for LSP tools", ...]
550
- * }
551
- */
552
400
  toolsRoutes.post('/call/:toolName', async (
553
401
  req: Request,
554
402
  res: Response,
555
403
  next: NextFunction
556
404
  ) => {
557
- // Set up AbortController for client disconnection handling
558
- // NOTE: req.on('close') fires when request body is consumed, not client disconnect
559
- // Use res.on('close') + socket.destroyed to detect actual client disconnection
560
405
  const abortController = new AbortController();
561
406
  let isAborted = false;
562
407
 
563
408
  res.on('close', () => {
564
- // Only abort if client disconnected before we finished responding
565
409
  if (!res.writableEnded && req.socket?.destroyed) {
566
410
  isAborted = true;
567
411
  abortController.abort();
@@ -571,7 +415,6 @@ toolsRoutes.post('/call/:toolName', async (
571
415
  try {
572
416
  const { toolName } = req.params;
573
417
 
574
- // Validate tool exists
575
418
  const toolEntry = TOOL_REGISTRY[toolName];
576
419
  if (!toolEntry) {
577
420
  const availableTools = Object.keys(TOOL_REGISTRY);
@@ -588,7 +431,6 @@ toolsRoutes.post('/call/:toolName', async (
588
431
  return;
589
432
  }
590
433
 
591
- // Validate request body using schema
592
434
  const validation = validateToolCallBody(req.body);
593
435
  if (!validation.success) {
594
436
  res.status(400).json({
@@ -602,19 +444,15 @@ toolsRoutes.post('/call/:toolName', async (
602
444
 
603
445
  const { queries } = validation.data!;
604
446
 
605
- // Check if request was aborted before execution
606
447
  if (isAborted) return;
607
448
 
608
- // Execute tool with resilience (includes timeout + circuit breaker + retry)
609
449
  const rawResult = await toolEntry.resilience(
610
450
  () => toolEntry.fn({ queries }),
611
451
  toolName
612
452
  );
613
453
 
614
- // Check if request was aborted after execution
615
454
  if (isAborted) return;
616
455
 
617
- // Log tool call for session telemetry
618
456
  const repos = extractReposFromQueries(queries);
619
457
  const researchParams = extractResearchParams(queries);
620
458
  fireAndForgetWithTimeout(
@@ -631,7 +469,6 @@ toolsRoutes.post('/call/:toolName', async (
631
469
 
632
470
  const mcpResponse = rawResult as { content: Array<{ type: string; text: string }> };
633
471
 
634
- // For multiple queries, return bulk response format
635
472
  if (queries.length > 1) {
636
473
  const bulkParsed = parseToolResponseBulk(mcpResponse);
637
474
 
@@ -647,7 +484,6 @@ toolsRoutes.post('/call/:toolName', async (
647
484
  return;
648
485
  }
649
486
 
650
- // Single query - use existing response format for backward compatibility
651
487
  const parsed = parseToolResponse(mcpResponse);
652
488
 
653
489
  res.status(parsed.isError ? 500 : 200).json({
@@ -658,7 +494,6 @@ toolsRoutes.post('/call/:toolName', async (
658
494
  research: parsed.research,
659
495
  });
660
496
  } catch (error) {
661
- // Skip error handling if request was aborted
662
497
  if (isAborted) return;
663
498
  next(error);
664
499
  }
@@ -1,24 +1,6 @@
1
- /**
2
- * Server Init Script — Detached Daemon Launcher
3
- *
4
- * 1. Checks if server is already running (health check)
5
- * 2. If running → prints "ok" and exits
6
- * 3. If not → starts server as **detached** process, waits for health, prints "ok", exits
7
- *
8
- * Lifecycle: The server runs as an independent daemon (detached, unref'd).
9
- * No client owns the server — it self-terminates after 30 minutes of idle.
10
- * Every invocation of this script exits after confirming the server is healthy.
11
- *
12
- * Usage: npm run server-init
13
- * Exit codes: 0 = success, 1 = error
14
- */
15
-
16
1
  import { spawn } from 'child_process';
17
2
  import { join } from 'path';
18
3
 
19
- // =============================================================================
20
- // Configuration (Environment Variables)
21
- // =============================================================================
22
4
 
23
5
  const PORT = parseInt(process.env.OCTOCODE_RESEARCH_PORT || process.env.OCTOCODE_PORT || '1987', 10);
24
6
  const HOST = process.env.OCTOCODE_RESEARCH_HOST || 'localhost';
@@ -30,9 +12,6 @@ interface HealthResponse {
30
12
  status: 'ok' | 'initializing' | string;
31
13
  }
32
14
 
33
- // =============================================================================
34
- // Health Check
35
- // =============================================================================
36
15
 
37
16
  async function checkHealth(): Promise<HealthResponse | null> {
38
17
  try {
@@ -53,9 +32,6 @@ async function checkHealth(): Promise<HealthResponse | null> {
53
32
  }
54
33
  }
55
34
 
56
- // =============================================================================
57
- // Server Start — detached daemon (survives parent exit)
58
- // =============================================================================
59
35
 
60
36
  function startServer(): Promise<void> {
61
37
  return new Promise((resolve, reject) => {
@@ -82,9 +58,6 @@ function startServer(): Promise<void> {
82
58
  });
83
59
  }
84
60
 
85
- // =============================================================================
86
- // Wait for Ready (with exponential backoff)
87
- // =============================================================================
88
61
 
89
62
  async function waitForReady(): Promise<boolean> {
90
63
  const startTime = Date.now();
@@ -108,12 +81,8 @@ async function waitForReady(): Promise<boolean> {
108
81
  return false;
109
82
  }
110
83
 
111
- // =============================================================================
112
- // Main
113
- // =============================================================================
114
84
 
115
85
  async function main(): Promise<void> {
116
- // Fast path: server already running → exit immediately
117
86
  const initialHealth = await checkHealth();
118
87
 
119
88
  if (initialHealth?.status === 'ok') {
@@ -133,7 +102,6 @@ async function main(): Promise<void> {
133
102
  }
134
103
  }
135
104
 
136
- // Server not running — start it (detached)
137
105
  console.log('[server-init] Server not running, starting detached daemon...');
138
106
 
139
107
  try {
@@ -25,7 +25,6 @@ const getPort = (raw?: string): number => {
25
25
 
26
26
  const port = Number(raw);
27
27
 
28
- // Strictly allow only the non-privileged, user-registered range
29
28
  if (!Number.isInteger(port) || port < 1024 || port > 65535) {
30
29
  throw new Error(
31
30
  `Invalid OCTOCODE_RESEARCH_PORT: "${raw}". ` +
@@ -38,15 +37,14 @@ const getPort = (raw?: string): number => {
38
37
 
39
38
  const PORT = getPort(process.env.OCTOCODE_RESEARCH_PORT || process.env.OCTOCODE_PORT);
40
39
 
41
- const MAX_IDLE_TIME_MS = 30 * 60 * 1000; // 30 minutes idle before restart
42
- const IDLE_CHECK_INTERVAL_MS = 120 * 1000; // Check every 2 minute
40
+ const MAX_IDLE_TIME_MS = 30 * 60 * 1000;
41
+ const IDLE_CHECK_INTERVAL_MS = 120 * 1000;
43
42
 
44
43
  let server: Server | null = null;
45
44
  let lastRequestTime: number = Date.now();
46
45
  let idleCheckInterval: NodeJS.Timeout | null = null;
47
46
  let isShuttingDown = false;
48
47
 
49
- // PID file for daemon lifecycle management
50
48
  const OCTOCODE_DIR = process.env.OCTOCODE_HOME || join(homedir(), '.octocode');
51
49
  export const PID_FILE = join(OCTOCODE_DIR, `research-server-${PORT}.pid`);
52
50
 
@@ -55,7 +53,7 @@ function writePidFile(): void {
55
53
  mkdirSync(OCTOCODE_DIR, { recursive: true, mode: 0o700 });
56
54
  writeFileSync(PID_FILE, String(process.pid), { mode: 0o600 });
57
55
  } catch {
58
- // Non-fatal — server works without PID file
56
+ void 0;
59
57
  }
60
58
  }
61
59
 
@@ -63,13 +61,11 @@ function removePidFile(): void {
63
61
  try {
64
62
  unlinkSync(PID_FILE);
65
63
  } catch {
66
- // Already removed or never written
64
+ void 0;
67
65
  }
68
66
  }
69
67
 
70
- /**
71
- * Check if server has been idle and should restart
72
- */
68
+
73
69
  function checkIdleRestart(): void {
74
70
  const idleTime = Date.now() - lastRequestTime;
75
71
  const idleSeconds = Math.floor(idleTime / 1000);
@@ -82,23 +78,18 @@ function checkIdleRestart(): void {
82
78
  }
83
79
  }
84
80
 
85
- /**
86
- * Start periodic idle checking
87
- */
81
+
88
82
  function startIdleCheck(): void {
89
83
  if (idleCheckInterval) return;
90
84
 
91
85
  idleCheckInterval = setInterval(checkIdleRestart, IDLE_CHECK_INTERVAL_MS);
92
86
 
93
- // Unref so it doesn't prevent process exit
94
87
  idleCheckInterval.unref();
95
88
 
96
89
  console.log(dimLog(`⏱️ Idle check started (${IDLE_CHECK_INTERVAL_MS / 1000}s interval, ${MAX_IDLE_TIME_MS / 1000}s threshold)`));
97
90
  }
98
91
 
99
- /**
100
- * Stop idle checking
101
- */
92
+
102
93
  function stopIdleCheck(): void {
103
94
  if (idleCheckInterval) {
104
95
  clearInterval(idleCheckInterval);
@@ -108,16 +99,13 @@ function stopIdleCheck(): void {
108
99
  }
109
100
 
110
101
  export async function createServer(): Promise<Express> {
111
- // Initialize logger first (sync for startup, async after)
112
102
  initializeLogger();
113
103
 
114
- // Initialize session for telemetry tracking
115
104
  initializeSession();
116
105
 
117
106
  const app = express();
118
107
  app.use(express.json());
119
108
 
120
- // Track activity for idle restart
121
109
  app.use((_req: Request, _res: Response, next: NextFunction) => {
122
110
  lastRequestTime = Date.now();
123
111
  next();
@@ -139,7 +127,6 @@ export async function createServer(): Promise<Express> {
139
127
  uptime: Math.floor(process.uptime()),
140
128
  processManager: 'self (detached daemon)',
141
129
  pid: process.pid,
142
- // Idle tracking info
143
130
  idle: {
144
131
  currentMs: idleTimeMs,
145
132
  thresholdMs: MAX_IDLE_TIME_MS,
@@ -163,11 +150,9 @@ export async function createServer(): Promise<Express> {
163
150
  });
164
151
  });
165
152
 
166
- // All tool execution via /tools/call/:toolName (readiness check applied in route files)
167
153
  app.use('/tools', toolsRoutes);
168
154
  app.use('/prompts', promptsRoutes);
169
155
 
170
- // 404 handler for undefined routes
171
156
  app.use((_req: Request, res: Response) => {
172
157
  res.status(404).json({
173
158
  success: false,
@@ -197,11 +182,7 @@ export async function createServer(): Promise<Express> {
197
182
  return app;
198
183
  }
199
184
 
200
- /**
201
- * Graceful shutdown handler.
202
- * Triggered by SIGTERM, SIGINT, or idle timeout. Cleans up all resources
203
- * including PID file, circuit breakers, and HTTP connections.
204
- */
185
+
205
186
  function gracefulShutdown(signal: string): void {
206
187
  if (isShuttingDown) {
207
188
  console.log(dimLog(`Already shutting down, ignoring ${signal}`));
@@ -217,21 +198,16 @@ function gracefulShutdown(signal: string): void {
217
198
  process.exit(1);
218
199
  }, FORCE_EXIT_TIMEOUT_MS).unref();
219
200
 
220
- // 1. Remove PID file immediately
221
201
  removePidFile();
222
202
 
223
- // 2. Stop idle check interval
224
203
  stopIdleCheck();
225
204
 
226
- // 3. Stop circuit cleanup interval
227
205
  stopCircuitCleanup();
228
206
  console.log(successLog('✅ Circuit cleanup interval stopped'));
229
207
 
230
- // 4. Clear circuit breakers
231
208
  clearAllCircuits();
232
209
  console.log(successLog('✅ Circuit breakers cleared'));
233
210
 
234
- // 5. Close HTTP server (waits for connections to drain)
235
211
  if (server) {
236
212
  console.log(dimLog('⏳ Waiting for connections to drain...'));
237
213
  server.close((err) => {
@@ -257,16 +233,13 @@ export async function startServer(): Promise<void> {
257
233
  console.log(agentLog(`🔍 Octocode Research Server running on http://${HOST}:${PORT} (pid: ${process.pid})`));
258
234
  console.log(dimLog(`⏳ initializing context...`));
259
235
 
260
- // Start background initialization (Warm Start)
261
236
  initializeMcpContent()
262
237
  .then(() => initializeProviders())
263
238
  .then(() => {
264
239
  console.log(successLog('✅ Context initialized - Server Ready'));
265
240
 
266
- // Reset idle timer after init (prevents early timeout)
267
241
  lastRequestTime = Date.now();
268
242
 
269
- // Start idle check after initialization
270
243
  startIdleCheck();
271
244
 
272
245
  console.log(agentLog(`📁 Logs: ${getLogsPath()}`));
@@ -283,7 +256,6 @@ export async function startServer(): Promise<void> {
283
256
  console.log(dimLog(` GET /prompts/list - List prompts`));
284
257
  console.log(dimLog(` GET /prompts/info/:name - Get prompt content`));
285
258
 
286
- // Log session initialization after server is ready
287
259
  fireAndForgetWithTimeout(
288
260
  () => logSessionInit(),
289
261
  5000,
@@ -299,7 +271,6 @@ export async function startServer(): Promise<void> {
299
271
  });
300
272
  }
301
273
 
302
- // Signal handlers — server is a detached daemon, handles its own signals
303
274
  process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
304
275
  process.on('SIGINT', () => gracefulShutdown('SIGINT'));
305
276
 
@@ -1,15 +1,3 @@
1
- /**
2
- * Type guards for safe error handling.
3
- *
4
- * Replaces unsafe type coercion (err as { status?: number }) with
5
- * proper runtime type checking for better type safety.
6
- *
7
- * @module types/errorGuards
8
- */
9
-
10
- // =============================================================================
11
- // Internal Interfaces & Guards
12
- // =============================================================================
13
1
 
14
2
  interface ErrorWithStatus {
15
3
  status: number;
@@ -48,9 +36,6 @@ function hasMessage(err: unknown): err is { message: string } {
48
36
  );
49
37
  }
50
38
 
51
- // =============================================================================
52
- // Public API
53
- // =============================================================================
54
39
 
55
40
  export function getErrorStatus(err: unknown): number | undefined {
56
41
  return isErrorWithStatus(err) ? err.status : undefined;