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,27 +1,14 @@
1
- /**
2
- * Type guard utilities for consistent runtime type checking.
3
- * Use these instead of inline typeof checks across routes.
4
- *
5
- * @module types/guards
6
- */
7
1
 
8
- /**
9
- * Check if value is an array (of any type)
10
- */
11
2
  export function isArray(value: unknown): value is unknown[] {
12
3
  return Array.isArray(value);
13
4
  }
14
5
 
15
- /**
16
- * Check if value is a non-null object
17
- */
6
+
18
7
  export function isObject(value: unknown): value is Record<string, unknown> {
19
8
  return typeof value === 'object' && value !== null && !Array.isArray(value);
20
9
  }
21
10
 
22
- /**
23
- * Check if object has a specific property
24
- */
11
+
25
12
  export function hasProperty<K extends string>(
26
13
  obj: unknown,
27
14
  key: K
@@ -29,9 +16,7 @@ export function hasProperty<K extends string>(
29
16
  return typeof obj === 'object' && obj !== null && key in obj;
30
17
  }
31
18
 
32
- /**
33
- * Check if object has a string property
34
- */
19
+
35
20
  export function hasStringProperty<K extends string>(
36
21
  obj: unknown,
37
22
  key: K
@@ -39,9 +24,7 @@ export function hasStringProperty<K extends string>(
39
24
  return hasProperty(obj, key) && typeof obj[key] === 'string';
40
25
  }
41
26
 
42
- /**
43
- * Check if object has a number property
44
- */
27
+
45
28
  export function hasNumberProperty<K extends string>(
46
29
  obj: unknown,
47
30
  key: K
@@ -49,9 +32,7 @@ export function hasNumberProperty<K extends string>(
49
32
  return hasProperty(obj, key) && typeof obj[key] === 'number';
50
33
  }
51
34
 
52
- /**
53
- * Check if object has a boolean property
54
- */
35
+
55
36
  export function hasBooleanProperty<K extends string>(
56
37
  obj: unknown,
57
38
  key: K
@@ -59,9 +40,7 @@ export function hasBooleanProperty<K extends string>(
59
40
  return hasProperty(obj, key) && typeof obj[key] === 'boolean';
60
41
  }
61
42
 
62
- /**
63
- * Check if object has an array property
64
- */
43
+
65
44
  export function hasArrayProperty<K extends string>(
66
45
  obj: unknown,
67
46
  key: K
@@ -1,7 +1,3 @@
1
- /**
2
- * MCP Protocol utilities for /tools endpoints
3
- */
4
-
5
1
  interface JsonSchemaProperty {
6
2
  type: string;
7
3
  description?: string;
@@ -26,10 +22,7 @@ interface JsonSchema {
26
22
  additionalProperties?: boolean;
27
23
  }
28
24
 
29
- /**
30
- * Transform description-based schema to JSON Schema format.
31
- * Fallback transformer when full JSON schemas are not available.
32
- */
25
+
33
26
  export function transformToJsonSchema(
34
27
  schemaDescriptions: Record<string, string>,
35
28
  toolName: string
@@ -1,14 +1,5 @@
1
- /**
2
- * Response types for MCP tool results.
3
- *
4
- * @module types/responses
5
- */
6
-
7
1
  import { hasProperty, hasArrayProperty } from './guards.js';
8
2
 
9
- // =============================================================================
10
- // File and Search Types
11
- // =============================================================================
12
3
 
13
4
  export interface FileMatch {
14
5
  path: string;
@@ -37,9 +28,6 @@ export interface PaginationInfo {
37
28
  nextCursor?: string;
38
29
  }
39
30
 
40
- // =============================================================================
41
- // Internal helpers
42
- // =============================================================================
43
31
 
44
32
  function isFileMatch(obj: unknown): obj is FileMatch {
45
33
  return (
@@ -56,9 +44,6 @@ function hasValidPagination(obj: unknown): obj is { pagination: PaginationInfo }
56
44
  return typeof p === 'object' && p !== null;
57
45
  }
58
46
 
59
- // =============================================================================
60
- // Public extractors
61
- // =============================================================================
62
47
 
63
48
  export function extractFiles(data: unknown): FileMatch[] {
64
49
  if (!hasArrayProperty(data, 'files')) return [];
@@ -1,45 +1,8 @@
1
1
  import { errorQueue } from './errorQueue.js';
2
2
 
3
- /**
4
- * Timeout wrapper for fire-and-forget async operations.
5
- *
6
- * Prevents unbounded async operations from hanging indefinitely.
7
- * Uses Promise.race with AbortController for clean cancellation.
8
- *
9
- * @module utils/asyncTimeout
10
- */
11
3
 
12
- /**
13
- * Default timeout for fire-and-forget operations (5 seconds).
14
- * Tuned for logging/telemetry operations that should complete quickly.
15
- */
16
4
  const DEFAULT_TIMEOUT_MS = 5000;
17
5
 
18
- /**
19
- * Execute an async operation with timeout protection.
20
- *
21
- * Fire-and-forget pattern with built-in timeout to prevent:
22
- * - Memory leaks from unresolved promises
23
- * - Resource exhaustion from hanging operations
24
- * - Cascading failures from slow logging/telemetry
25
- *
26
- * @param operation - Async operation to execute
27
- * @param timeoutMs - Maximum time to wait (default: 5000ms)
28
- * @param context - Context string for error logging
29
- *
30
- * @example
31
- * ```typescript
32
- * // Instead of:
33
- * logSessionError(tool, code).catch(err => errorQueue.push(err, 'logSessionError'));
34
- *
35
- * // Use:
36
- * fireAndForgetWithTimeout(
37
- * () => logSessionError(tool, code),
38
- * 5000,
39
- * 'logSessionError'
40
- * );
41
- * ```
42
- */
43
6
  export function fireAndForgetWithTimeout(
44
7
  operation: () => Promise<unknown>,
45
8
  timeoutMs = DEFAULT_TIMEOUT_MS,
@@ -48,21 +11,17 @@ export function fireAndForgetWithTimeout(
48
11
  const controller = new AbortController();
49
12
  const { signal } = controller;
50
13
 
51
- // Create timeout promise
52
14
  const timeoutPromise = new Promise<never>((_, reject) => {
53
15
  const timeoutId = setTimeout(() => {
54
16
  controller.abort();
55
17
  reject(new Error(`Operation timed out after ${timeoutMs}ms`));
56
18
  }, timeoutMs);
57
19
 
58
- // Clean up timeout if operation completes first
59
20
  signal.addEventListener('abort', () => clearTimeout(timeoutId), { once: true });
60
21
  });
61
22
 
62
- // Race operation against timeout
63
23
  Promise.race([operation(), timeoutPromise])
64
24
  .catch((err: unknown) => {
65
- // Log to error queue - never throw from fire-and-forget
66
25
  errorQueue.push(
67
26
  err instanceof Error ? err : new Error(String(err)),
68
27
  context
@@ -70,27 +29,6 @@ export function fireAndForgetWithTimeout(
70
29
  });
71
30
  }
72
31
 
73
- /**
74
- * Execute an async operation with timeout and return result.
75
- *
76
- * Unlike fireAndForgetWithTimeout, this returns the result or throws on timeout.
77
- * Use for operations where you need the result but want timeout protection.
78
- *
79
- * @param operation - Async operation to execute
80
- * @param timeoutMs - Maximum time to wait
81
- * @param context - Context string for timeout error message
82
- * @returns Promise resolving to operation result
83
- * @throws Error if operation times out
84
- *
85
- * @example
86
- * ```typescript
87
- * const result = await withTimeout(
88
- * () => fetchData(),
89
- * 3000,
90
- * 'fetchData'
91
- * );
92
- * ```
93
- */
94
32
  export async function withTimeout<T>(
95
33
  operation: () => Promise<T>,
96
34
  timeoutMs: number,
@@ -111,6 +49,6 @@ export async function withTimeout<T>(
111
49
  try {
112
50
  return await Promise.race([operation(), timeoutPromise]);
113
51
  } finally {
114
- controller.abort(); // Clean up if operation completed
52
+ controller.abort();
115
53
  }
116
54
  }
@@ -1,25 +1,14 @@
1
1
  import { agentLog, warnLog, successLog, errorLog } from './colors.js';
2
2
  import { logRateLimit } from '../index.js';
3
3
  import { fireAndForgetWithTimeout } from './asyncTimeout.js';
4
- /**
5
- * Circuit breaker pattern for LSP and external services.
6
- *
7
- * Prevents cascading failures by temporarily stopping calls to failing services.
8
- *
9
- * @module utils/circuitBreaker
10
- */
11
-
12
- /**
13
- * Circuit breaker states
14
- */
4
+
5
+
15
6
  type CircuitState = 'closed' | 'open' | 'half-open';
16
7
 
17
- /**
18
- * Memory management constants
19
- */
20
- const MAX_CIRCUITS = 100; // Maximum number of circuits to prevent unbounded growth
21
- const CIRCUIT_TTL_MS = 3600000; // 1 hour TTL for stale circuits
22
- const CLEANUP_INTERVAL_MS = 600000; // 10 minutes between cleanup runs
8
+
9
+ const MAX_CIRCUITS = 100;
10
+ const CIRCUIT_TTL_MS = 3600000;
11
+ const CLEANUP_INTERVAL_MS = 600000;
23
12
 
24
13
  interface CircuitRecord {
25
14
  failures: number;
@@ -27,55 +16,37 @@ interface CircuitRecord {
27
16
  lastFailure: number;
28
17
  lastAttempt: number;
29
18
  state: CircuitState;
30
- createdAt: number; // Timestamp when circuit was created
19
+ createdAt: number;
31
20
  }
32
21
 
33
- /**
34
- * Circuit breaker configuration
35
- */
22
+
36
23
  export interface CircuitBreakerConfig {
37
- /** Number of failures before opening circuit */
24
+
38
25
  failureThreshold: number;
39
- /** Number of successes in half-open to close circuit */
26
+
40
27
  successThreshold: number;
41
- /** Time in ms before attempting half-open */
28
+
42
29
  resetTimeoutMs: number;
43
30
  }
44
31
 
45
- /**
46
- * Default circuit breaker configuration.
47
- * Tuned for balance between fault tolerance and quick recovery.
48
- */
32
+
49
33
  const DEFAULT_CONFIG: CircuitBreakerConfig = {
50
- // 3 failures: Quick to detect persistent issues,
51
- // but tolerant of occasional transient errors.
52
34
  failureThreshold: 3,
53
35
 
54
- // 2 successes: Requires service to prove stability
55
- // before fully resuming (prevents flapping).
56
36
  successThreshold: 2,
57
37
 
58
- // 30s timeout: Allows services time to recover from
59
- // rate limits or temporary outages before retrying.
60
38
  resetTimeoutMs: 30000,
61
39
  };
62
40
 
63
- /**
64
- * Circuit breaker registry
65
- */
41
+
66
42
  const circuits = new Map<string, CircuitRecord>();
67
43
  const configs = new Map<string, CircuitBreakerConfig>();
68
44
 
69
- /**
70
- * Get or create circuit breaker for a named service.
71
- * Enforces MAX_CIRCUITS limit by cleaning up stale circuits when limit is reached.
72
- */
45
+
73
46
  function getCircuit(name: string): CircuitRecord {
74
47
  if (!circuits.has(name)) {
75
- // Enforce circuit limit by cleaning up stale circuits
76
48
  if (circuits.size >= MAX_CIRCUITS) {
77
49
  cleanupStaleCircuits();
78
- // If still at limit after cleanup, remove oldest circuit
79
50
  if (circuits.size >= MAX_CIRCUITS) {
80
51
  const oldestKey = findOldestCircuit();
81
52
  if (oldestKey) {
@@ -98,9 +69,7 @@ function getCircuit(name: string): CircuitRecord {
98
69
  return circuits.get(name)!;
99
70
  }
100
71
 
101
- /**
102
- * Find the oldest circuit by lastAttempt time
103
- */
72
+
104
73
  function findOldestCircuit(): string | null {
105
74
  let oldest: string | null = null;
106
75
  let oldestTime = Infinity;
@@ -115,16 +84,12 @@ function findOldestCircuit(): string | null {
115
84
  return oldest;
116
85
  }
117
86
 
118
- /**
119
- * Get configuration for a circuit
120
- */
87
+
121
88
  function getConfig(name: string): CircuitBreakerConfig {
122
89
  return configs.get(name) || DEFAULT_CONFIG;
123
90
  }
124
91
 
125
- /**
126
- * Configure a specific circuit breaker
127
- */
92
+
128
93
  export function configureCircuit(
129
94
  name: string,
130
95
  config: Partial<CircuitBreakerConfig>
@@ -132,24 +97,6 @@ export function configureCircuit(
132
97
  configs.set(name, { ...DEFAULT_CONFIG, ...config });
133
98
  }
134
99
 
135
- /**
136
- * Execute operation with circuit breaker protection.
137
- *
138
- * @param name - Circuit breaker name (e.g., 'lsp', 'github')
139
- * @param operation - Async operation to execute
140
- * @param fallback - Optional fallback when circuit is open
141
- * @returns Operation result or fallback
142
- * @throws CircuitOpenError if circuit is open and no fallback provided
143
- *
144
- * @example
145
- * ```typescript
146
- * const result = await withCircuitBreaker(
147
- * 'lsp',
148
- * () => lspGotoDefinition({ queries }),
149
- * () => ({ fallback: true, locations: [] })
150
- * );
151
- * ```
152
- */
153
100
  export async function withCircuitBreaker<T>(
154
101
  name: string,
155
102
  operation: () => Promise<T>,
@@ -159,17 +106,13 @@ export async function withCircuitBreaker<T>(
159
106
  const config = getConfig(name);
160
107
  const now = Date.now();
161
108
 
162
- // Update last attempt time
163
109
  circuit.lastAttempt = now;
164
110
 
165
- // Check circuit state
166
111
  if (circuit.state === 'open') {
167
- // Check if we should try half-open
168
112
  if (now - circuit.lastFailure > config.resetTimeoutMs) {
169
113
  circuit.state = 'half-open';
170
114
  console.log(warnLog(`🟡 Circuit ${name} entering half-open state`));
171
115
  } else {
172
- // Circuit is open - use fallback or throw
173
116
  console.log(
174
117
  `🔴 Circuit ${name} is OPEN - ${Math.ceil((circuit.lastFailure + config.resetTimeoutMs - now) / 1000)}s until retry`
175
118
  );
@@ -183,7 +126,6 @@ export async function withCircuitBreaker<T>(
183
126
  try {
184
127
  const result = await operation();
185
128
 
186
- // Success - handle state transition
187
129
  if (circuit.state === 'half-open') {
188
130
  circuit.successes++;
189
131
  if (circuit.successes >= config.successThreshold) {
@@ -193,22 +135,18 @@ export async function withCircuitBreaker<T>(
193
135
  console.log(successLog(`🟢 Circuit ${name} CLOSED after recovery`));
194
136
  }
195
137
  } else {
196
- // Reset failures on success in closed state
197
138
  circuit.failures = 0;
198
139
  }
199
140
 
200
141
  return result;
201
142
  } catch (error) {
202
- // Failure - handle state transition
203
143
  circuit.failures++;
204
144
  circuit.lastFailure = now;
205
145
  circuit.successes = 0;
206
146
 
207
147
  if (circuit.state === 'half-open') {
208
- // Failed in half-open - back to open
209
148
  circuit.state = 'open';
210
149
  console.log(errorLog(`🔴 Circuit ${name} back to OPEN after half-open failure`));
211
- // Log rate limit/circuit open event to session telemetry
212
150
  fireAndForgetWithTimeout(
213
151
  () => logRateLimit({
214
152
  limit_type: 'secondary',
@@ -220,12 +158,10 @@ export async function withCircuitBreaker<T>(
220
158
  'logRateLimit'
221
159
  );
222
160
  } else if (circuit.failures >= config.failureThreshold) {
223
- // Too many failures - open circuit
224
161
  circuit.state = 'open';
225
162
  console.log(
226
163
  `🔴 Circuit ${name} OPENED after ${circuit.failures} failures`
227
164
  );
228
- // Log rate limit/circuit open event to session telemetry
229
165
  fireAndForgetWithTimeout(
230
166
  () => logRateLimit({
231
167
  limit_type: 'secondary',
@@ -242,9 +178,7 @@ export async function withCircuitBreaker<T>(
242
178
  }
243
179
  }
244
180
 
245
- /**
246
- * Get current state of a circuit breaker
247
- */
181
+
248
182
  export function getCircuitState(name: string): {
249
183
  state: CircuitState;
250
184
  failures: number;
@@ -260,9 +194,7 @@ export function getCircuitState(name: string): {
260
194
  };
261
195
  }
262
196
 
263
- /**
264
- * Reset a circuit breaker to closed state
265
- */
197
+
266
198
  export function resetCircuit(name: string): void {
267
199
  const circuit = getCircuit(name);
268
200
  circuit.state = 'closed';
@@ -272,9 +204,7 @@ export function resetCircuit(name: string): void {
272
204
  console.log(agentLog(`🔄 Circuit ${name} manually reset to CLOSED`));
273
205
  }
274
206
 
275
- /**
276
- * Get all circuit states (for health endpoint)
277
- */
207
+
278
208
  export function getAllCircuitStates(): Record<
279
209
  string,
280
210
  { state: CircuitState; failures: number; isHealthy: boolean }
@@ -295,9 +225,7 @@ export function getAllCircuitStates(): Record<
295
225
  return states;
296
226
  }
297
227
 
298
- /**
299
- * Error thrown when circuit is open
300
- */
228
+
301
229
  export class CircuitOpenError extends Error {
302
230
  readonly circuitName: string;
303
231
  readonly retryAfterMs: number;
@@ -310,107 +238,71 @@ export class CircuitOpenError extends Error {
310
238
  }
311
239
  }
312
240
 
313
- // =============================================================================
314
- // Pre-configured circuits (per-tool granularity)
315
- // =============================================================================
316
241
 
317
- // -----------------------------------------------------------------------------
318
- // GitHub circuits - split by API endpoint (different rate limits)
319
- // -----------------------------------------------------------------------------
320
-
321
- // GitHub Search API - aggressive rate limiting (30 req/min)
322
242
  configureCircuit('github:search', {
323
- failureThreshold: 2, // 2 failures = likely rate limited
324
- successThreshold: 1, // Single success proves API recovered
325
- resetTimeoutMs: 60000, // 60s: Give search rate limits time to reset
243
+ failureThreshold: 2,
244
+ successThreshold: 1,
245
+ resetTimeoutMs: 60000,
326
246
  });
327
247
 
328
- // GitHub Content API - higher limits, separate quota
329
248
  configureCircuit('github:content', {
330
- failureThreshold: 3, // 3 failures = more tolerant
249
+ failureThreshold: 3,
331
250
  successThreshold: 1,
332
- resetTimeoutMs: 30000, // 30s: Content API recovers faster
251
+ resetTimeoutMs: 30000,
333
252
  });
334
253
 
335
- // GitHub PR API - separate quota from search
336
254
  configureCircuit('github:pulls', {
337
- failureThreshold: 2, // 2 failures = likely rate limited
255
+ failureThreshold: 2,
338
256
  successThreshold: 1,
339
- resetTimeoutMs: 60000, // 60s: PR API has similar limits to search
257
+ resetTimeoutMs: 60000,
340
258
  });
341
259
 
342
- // Legacy fallback for any uncategorized github tools
343
260
  configureCircuit('github', {
344
261
  failureThreshold: 2,
345
262
  successThreshold: 1,
346
263
  resetTimeoutMs: 60000,
347
264
  });
348
265
 
349
- // -----------------------------------------------------------------------------
350
- // LSP circuits - split by operation weight
351
- // -----------------------------------------------------------------------------
352
-
353
- // LSP Navigation (definition, references) - fast, single lookup
354
266
  configureCircuit('lsp:navigation', {
355
- failureThreshold: 3, // 3 failures = likely LSP issue
356
- successThreshold: 1, // Single success proves LSP recovered
357
- resetTimeoutMs: 10000, // 10s: Quick recovery expected
267
+ failureThreshold: 3,
268
+ successThreshold: 1,
269
+ resetTimeoutMs: 10000,
358
270
  });
359
271
 
360
- // LSP Call Hierarchy - heavier recursive operation
361
272
  configureCircuit('lsp:hierarchy', {
362
- failureThreshold: 2, // 2 failures = operation too heavy or LSP struggling
273
+ failureThreshold: 2,
363
274
  successThreshold: 1,
364
- resetTimeoutMs: 15000, // 15s: Slightly longer for heavy ops
275
+ resetTimeoutMs: 15000,
365
276
  });
366
277
 
367
- // Legacy fallback
368
278
  configureCircuit('lsp', {
369
279
  failureThreshold: 3,
370
280
  successThreshold: 1,
371
281
  resetTimeoutMs: 10000,
372
282
  });
373
283
 
374
- // -----------------------------------------------------------------------------
375
- // Local circuits - unified (same failure mode: filesystem)
376
- // -----------------------------------------------------------------------------
377
-
378
284
  configureCircuit('local', {
379
- failureThreshold: 5, // 5 failures = likely persistent issue
380
- successThreshold: 1, // Single success proves local ops recovered
381
- resetTimeoutMs: 5000, // 5s: Local ops should recover quickly
285
+ failureThreshold: 5,
286
+ successThreshold: 1,
287
+ resetTimeoutMs: 5000,
382
288
  });
383
289
 
384
- // -----------------------------------------------------------------------------
385
- // Package circuits - unified (npm/PyPI)
386
- // -----------------------------------------------------------------------------
387
-
388
290
  configureCircuit('package', {
389
- failureThreshold: 3, // 3 failures = likely rate limited or down
390
- successThreshold: 1, // Single success proves API recovered
391
- resetTimeoutMs: 45000, // 45s: Slightly faster than GitHub
291
+ failureThreshold: 3,
292
+ successThreshold: 1,
293
+ resetTimeoutMs: 45000,
392
294
  });
393
295
 
394
- // =============================================================================
395
- // Automatic Cleanup (Memory Leak Prevention)
396
- // =============================================================================
397
296
 
398
- /**
399
- * Cleanup interval reference (for graceful shutdown)
400
- */
401
297
  let cleanupIntervalId: ReturnType<typeof setInterval> | null = null;
402
298
 
403
- /**
404
- * Remove stale circuits that haven't been used recently.
405
- * A circuit is stale if it hasn't been attempted in CIRCUIT_TTL_MS.
406
- */
299
+
407
300
  function cleanupStaleCircuits(): void {
408
301
  const now = Date.now();
409
302
  const staleCutoff = now - CIRCUIT_TTL_MS;
410
303
  let removedCount = 0;
411
304
 
412
305
  for (const [name, circuit] of circuits) {
413
- // Don't remove circuits that are currently open (still tracking failure)
414
306
  if (circuit.state !== 'open' && circuit.lastAttempt < staleCutoff) {
415
307
  circuits.delete(name);
416
308
  configs.delete(name);
@@ -423,27 +315,20 @@ function cleanupStaleCircuits(): void {
423
315
  }
424
316
  }
425
317
 
426
- /**
427
- * Start the periodic cleanup interval.
428
- * Called automatically on module load.
429
- */
318
+
430
319
  function startPeriodicCleanup(): void {
431
- if (cleanupIntervalId) return; // Already running
320
+ if (cleanupIntervalId) return;
432
321
 
433
322
  cleanupIntervalId = setInterval(() => {
434
323
  cleanupStaleCircuits();
435
324
  }, CLEANUP_INTERVAL_MS);
436
325
 
437
- // Unref the interval so it doesn't prevent process exit
438
326
  if (cleanupIntervalId.unref) {
439
327
  cleanupIntervalId.unref();
440
328
  }
441
329
  }
442
330
 
443
- /**
444
- * Stop the periodic cleanup interval.
445
- * Call during graceful shutdown.
446
- */
331
+
447
332
  export function stopCircuitCleanup(): void {
448
333
  if (cleanupIntervalId) {
449
334
  clearInterval(cleanupIntervalId);
@@ -451,17 +336,9 @@ export function stopCircuitCleanup(): void {
451
336
  }
452
337
  }
453
338
 
454
- // Start periodic cleanup on module load
455
339
  startPeriodicCleanup();
456
340
 
457
- // =============================================================================
458
- // Manual Cleanup Functions
459
- // =============================================================================
460
341
 
461
- /**
462
- * Clear all circuit breakers.
463
- * Use for testing cleanup or server shutdown.
464
- */
465
342
  export function clearAllCircuits(): void {
466
343
  const count = circuits.size;
467
344
  circuits.clear();
@@ -1,33 +1,19 @@
1
- /**
2
- * ANSI color codes for terminal output
3
- *
4
- * Color scheme:
5
- * - AGENT (purple): Server/agent messages, startup, shutdown
6
- * - RESULT (blue): Tool output, skill results
7
- */
8
-
9
1
  const colors = {
10
- // Reset
11
2
  reset: '\x1b[0m',
12
3
 
13
- // Agent messages - Purple/Magenta
14
4
  agent: '\x1b[35m',
15
5
  agentBright: '\x1b[95m',
16
6
 
17
- // Results/Tool output - Blue
18
7
  result: '\x1b[34m',
19
8
  resultBright: '\x1b[94m',
20
9
 
21
- // Status colors
22
- success: '\x1b[32m', // Green
23
- error: '\x1b[31m', // Red
24
- warn: '\x1b[33m', // Yellow
10
+ success: '\x1b[32m',
11
+ error: '\x1b[31m',
12
+ warn: '\x1b[33m',
25
13
 
26
- // Dim for secondary info
27
14
  dim: '\x1b[2m',
28
15
  };
29
16
 
30
- // Helper functions for consistent formatting
31
17
  export function agentLog(message: string): string {
32
18
  return `${colors.agentBright}${message}${colors.reset}`;
33
19
  }