Skip to content

Commit c463e4d

Browse files
Oladotun  OlatunjiOladotun  Olatunji
authored andcommitted
fix: resolve strict typing issues and parameter extraction bugs
- Fixed all TypeScript strict typing violations (no any, no unknown without guards) - Enhanced parameter type definitions with proper union types - Fixed UnifiedCommandGateway parameter override issue with --force flag - Improved context-curator task_type detection with valid enum values - Added force execution bypass in request-processor - Fixed type-safe stderr write override in REPL - Enhanced EnhancedMatchResult interface with proper parameter typing All tools now pass validation with strict typing enabled and work correctly with both CLI and interactive modes.
1 parent 660fff0 commit c463e4d

File tree

4 files changed

+75
-26
lines changed

4 files changed

+75
-26
lines changed

src/cli/index.ts

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,12 +212,33 @@ async function processOneShot(args: string[]): Promise<void> {
212212

213213
let result;
214214

215-
if (processingResult.success && (!processingResult.metadata?.requiresConfirmation || forceExecution)) {
216-
// High confidence or force flag - execute directly
217-
logger.info({ tool: processingResult.selectedTool }, 'Executing tool with force flag or high confidence');
215+
// When using --force, bypass UnifiedCommandGateway and use process-request directly for proper parameter extraction
216+
if (forceExecution) {
217+
// Force flag - use process-request tool directly for better parameter extraction
218+
logger.info('Using process-request tool directly with --force flag');
219+
const context: ToolExecutionContext = {
220+
sessionId,
221+
transportType: 'cli',
222+
metadata: {
223+
startTime: Date.now(),
224+
cliVersion: '1.0.0',
225+
cliConfig: cliConfig,
226+
forceExecution: true
227+
}
228+
};
229+
230+
result = await executeTool(
231+
'process-request',
232+
{ request },
233+
openRouterConfig,
234+
context
235+
);
236+
} else if (processingResult.success && !processingResult.metadata?.requiresConfirmation) {
237+
// High confidence - execute directly using UnifiedCommandGateway
238+
logger.info({ tool: processingResult.selectedTool }, 'Executing tool with high confidence');
218239
const executionResult = await unifiedGateway.executeUnifiedCommand(request, unifiedContext);
219240
result = executionResult.result;
220-
} else if (processingResult.success && processingResult.metadata?.requiresConfirmation && !forceExecution) {
241+
} else if (processingResult.success && processingResult.metadata?.requiresConfirmation) {
221242
// Requires confirmation and no force flag - display processing result for user review
222243
result = {
223244
content: [{

src/cli/interactive/repl.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import chalk from 'chalk';
88
import { OpenRouterConfig } from '../../types/workflow.js';
99
import { executeTool } from '../../services/routing/toolRegistry.js';
1010
import { ToolExecutionContext } from '../../services/routing/toolRegistry.js';
11+
import { CallToolResult } from '@modelcontextprotocol/sdk/types.js';
1112
import { getBanner, getSessionStartMessage, getPrompt } from './ui/banner.js';
1213
import { progress } from './ui/progress.js';
1314
import { ResponseFormatter } from './ui/formatter.js';
@@ -73,16 +74,21 @@ export class VibeInteractiveREPL {
7374
// Override stderr.write to filter out JSON log entries while preserving normal output
7475
// This ensures clean interactive experience while maintaining full file logging
7576
const originalStderrWrite = process.stderr.write.bind(process.stderr);
76-
process.stderr.write = function(chunk: any, ...args: any[]): boolean {
77+
// Type-safe stderr write override
78+
type WriteCallback = (error?: Error | null) => void;
79+
type WriteArgs = [encoding?: BufferEncoding, callback?: WriteCallback] | [callback?: WriteCallback];
80+
81+
process.stderr.write = function(chunk: string | Buffer | Uint8Array, ...args: WriteArgs): boolean {
7782
const str = chunk?.toString() || '';
7883
// Filter out JSON log entries (they start with {" and contain "level":)
7984
if (str.startsWith('{"level":') && str.includes('"pid":') && str.includes('"hostname":')) {
8085
// Silently drop JSON log entries
8186
return true;
8287
}
8388
// Allow all other output (prompts, responses, etc.)
84-
return originalStderrWrite(chunk, ...args);
85-
} as any;
89+
// Cast to the original function's parameters safely
90+
return originalStderrWrite.apply(process.stderr, [chunk, ...args] as Parameters<typeof originalStderrWrite>);
91+
} as typeof process.stderr.write;
8692
// Note: File logging continues at info level to capture all activity in vibe-session.log
8793

8894
// Initialize configuration manager
@@ -1081,8 +1087,9 @@ export class VibeInteractiveREPL {
10811087
logger.info('Waiting for confirmation via existing handler');
10821088

10831089
// CRITICAL: Resume the stream to ensure readline continues listening
1084-
if ((this.rl as any).input && typeof (this.rl as any).input.resume === 'function') {
1085-
(this.rl as any).input.resume();
1090+
const rlWithInput = this.rl as readline.Interface & { input?: NodeJS.ReadableStream };
1091+
if (rlWithInput.input && typeof rlWithInput.input.resume === 'function') {
1092+
rlWithInput.input.resume();
10861093
}
10871094

10881095
// Show prompt to keep readline active
@@ -1111,8 +1118,9 @@ export class VibeInteractiveREPL {
11111118
// Ensure readline is active and listening
11121119
if (this.rl && this.isRunning) {
11131120
// Resume the input stream if it was paused
1114-
if ((this.rl as any).input && typeof (this.rl as any).input.resume === 'function') {
1115-
(this.rl as any).input.resume();
1121+
const rlWithInput = this.rl as readline.Interface & { input?: NodeJS.ReadableStream };
1122+
if (rlWithInput.input && typeof rlWithInput.input.resume === 'function') {
1123+
rlWithInput.input.resume();
11161124
}
11171125

11181126
// Clear the line and show a fresh prompt
@@ -1157,8 +1165,9 @@ export class VibeInteractiveREPL {
11571165
}
11581166

11591167
// Also check for jobStatus field if present
1160-
if ((result as any).jobStatus) {
1161-
responseText += `\nJob Status: ${(result as any).jobStatus.status}`;
1168+
const resultWithJobStatus = result as CallToolResult & { jobStatus?: { status: string } };
1169+
if (resultWithJobStatus.jobStatus) {
1170+
responseText += `\nJob Status: ${resultWithJobStatus.jobStatus.status}`;
11621171
}
11631172

11641173
return responseText.trim() || null;

src/services/hybrid-matcher/index.ts

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// Reconnecting pattern matching for improved NLP accuracy
2-
import { matchRequest, extractParameters } from "../matching-service/index.js";
2+
import { matchRequest } from "../matching-service/index.js";
33
import { MatchResult } from "../../types/tools.js";
44
import { processWithSequentialThinking } from "../../tools/sequential-thinking.js";
55
import { OpenRouterConfig } from "../../types/workflow.js";
@@ -14,7 +14,7 @@ const __filename = fileURLToPath(import.meta.url);
1414
const __dirname = path.dirname(__filename);
1515

1616
// Confidence thresholds
17-
const HIGH_CONFIDENCE = 0.8;
17+
// const HIGH_CONFIDENCE = 0.8; // Currently unused but may be needed for future confidence checks
1818
// const MEDIUM_CONFIDENCE = 0.6; // Removed unused variable
1919
// const LOW_CONFIDENCE = 0.4; // Removed unused variable
2020

@@ -25,7 +25,7 @@ let toolDescriptionsCache: Record<string, string> | null = null;
2525
* Hybrid matching result with additional metadata
2626
*/
2727
export interface EnhancedMatchResult extends MatchResult {
28-
parameters: Record<string, string>;
28+
parameters: Record<string, unknown>;
2929
matchMethod: "rule" | "intent" | "semantic" | "sequential"; // Added "semantic"
3030
requiresConfirmation: boolean;
3131
}
@@ -44,9 +44,14 @@ function loadToolDescriptions(): Record<string, string> {
4444
const configContent = readFileSync(configPath, 'utf-8');
4545
const config = JSON.parse(configContent);
4646

47+
interface ToolConfig {
48+
description?: string;
49+
[key: string]: unknown;
50+
}
51+
4752
const descriptions: Record<string, string> = {};
4853
for (const [name, tool] of Object.entries(config.tools)) {
49-
const toolData = tool as any;
54+
const toolData = tool as ToolConfig;
5055
descriptions[name] = toolData.description || '';
5156
}
5257

@@ -267,7 +272,8 @@ function combineResults(
267272
}
268273

269274
// Extract parameters based on the tool
270-
let parameters: Record<string, any> = {};
275+
// Using 'unknown' for parameters as they vary by tool and will be validated by each tool's schema
276+
let parameters: Record<string, unknown> = {};
271277

272278
// For research-manager: requires 'query'
273279
if (bestTool === 'research-manager') {
@@ -343,7 +349,7 @@ function combineResults(
343349
parameters = {
344350
productDescription,
345351
userStories: '', // Optional
346-
ruleCategories: [] // Optional array
352+
ruleCategories: [] as string[] // Optional array
347353
};
348354
}
349355
// For context curator: requires 'prompt' or 'task_type'
@@ -357,9 +363,21 @@ function combineResults(
357363
prompt = request;
358364
}
359365

366+
// Detect task type based on keywords in the request
367+
let task_type: string = 'auto_detect'; // Default to auto_detect
368+
if (request.match(/\b(bug|fix|error|issue|problem)\b/i)) {
369+
task_type = 'bug_fix';
370+
} else if (request.match(/\b(refactor|clean|improve|restructure)\b/i)) {
371+
task_type = 'refactoring';
372+
} else if (request.match(/\b(performance|optimize|speed|faster)\b/i)) {
373+
task_type = 'performance_optimization';
374+
} else if (request.match(/\b(feature|add|implement|create|new)\b/i)) {
375+
task_type = 'feature_addition';
376+
}
377+
360378
parameters = {
361379
prompt,
362-
task_type: 'general' // Default task type
380+
task_type
363381
};
364382
}
365383
// For fullstack starter kit generator: requires 'use_case'
@@ -378,7 +396,7 @@ function combineResults(
378396
// For map-codebase: optional 'directory' parameter
379397
else if (bestTool === 'map-codebase') {
380398
// Check if a specific directory is mentioned
381-
const dirMatch = request.match(/(?:for|in|of|at)\s+([\/\w\-\.]+)/i);
399+
const dirMatch = request.match(/(?:for|in|of|at)\s+([\w\-./]+)/i);
382400
if (dirMatch) {
383401
parameters = { directory: dirMatch[1] };
384402
} else {
@@ -405,7 +423,7 @@ function combineResults(
405423
if (workflowMatch) {
406424
parameters = {
407425
workflowName: workflowMatch[1],
408-
workflowInput: {} // Empty object as default
426+
workflowInput: {} as Record<string, unknown> // Empty object as default
409427
};
410428
} else {
411429
// If no specific workflow mentioned, look for "run workflow" pattern
@@ -415,13 +433,13 @@ function combineResults(
415433
const nameMatch = request.match(/\b([a-zA-Z0-9-_]+)\b/);
416434
parameters = {
417435
workflowName: nameMatch ? nameMatch[1] : 'default',
418-
workflowInput: {}
436+
workflowInput: {} as Record<string, unknown>
419437
};
420438
} else {
421439
// Fallback - use the full request
422440
parameters = {
423441
workflowName: 'default',
424-
workflowInput: { request }
442+
workflowInput: { request } as Record<string, unknown>
425443
};
426444
}
427445
}

src/services/request-processor/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ export const processUserRequest: ToolExecutor = async (
4646
// Step 1: Use the hybrid matcher to determine the appropriate tool
4747
matchResult = await hybridMatch(request, config);
4848

49-
// Step 2: Check if confirmation is needed
50-
if (matchResult.requiresConfirmation) {
49+
// Step 2: Check if confirmation is needed (unless force flag is set)
50+
const forceExecution = (context?.metadata as { forceExecution?: boolean })?.forceExecution === true;
51+
if (matchResult.requiresConfirmation && !forceExecution) {
5152
logger.info(`Tool execution requires confirmation: ${matchResult.toolName}`);
5253
const explanation = getMatchExplanation(matchResult);
5354
return {

0 commit comments

Comments
 (0)