@syrin/cli 1.3.2 → 1.4.1
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.
- package/README.md +184 -152
- package/dist/cli/commands/config.d.ts +47 -0
- package/dist/cli/commands/config.js +360 -0
- package/dist/cli/commands/dev.d.ts +6 -0
- package/dist/cli/commands/dev.js +67 -15
- package/dist/cli/commands/doctor.js +49 -13
- package/dist/cli/commands/init.d.ts +2 -0
- package/dist/cli/commands/init.js +89 -18
- package/dist/cli/commands/status.d.ts +10 -0
- package/dist/cli/commands/status.js +162 -0
- package/dist/cli/index.js +211 -12
- package/dist/cli/prompts/init-prompt.d.ts +18 -0
- package/dist/cli/prompts/init-prompt.js +159 -99
- package/dist/cli/utils/command-error-handler.js +2 -5
- package/dist/config/env-checker.d.ts +12 -2
- package/dist/config/env-checker.js +88 -38
- package/dist/config/env-templates.d.ts +15 -0
- package/dist/config/env-templates.js +49 -0
- package/dist/config/generator.js +17 -0
- package/dist/config/global-loader.d.ts +50 -0
- package/dist/config/global-loader.js +244 -0
- package/dist/config/loader.d.ts +28 -0
- package/dist/config/loader.js +95 -9
- package/dist/config/merger.d.ts +37 -0
- package/dist/config/merger.js +68 -0
- package/dist/config/schema.d.ts +26 -1
- package/dist/config/schema.js +73 -8
- package/dist/config/types.d.ts +19 -0
- package/dist/config/types.js +26 -1
- package/dist/constants/messages.d.ts +7 -0
- package/dist/constants/messages.js +8 -0
- package/dist/constants/paths.d.ts +6 -0
- package/dist/constants/paths.js +10 -0
- package/dist/events/emitter.js +7 -7
- package/dist/index.js +0 -0
- package/dist/presentation/config-ui.d.ts +34 -0
- package/dist/presentation/config-ui.js +139 -0
- package/dist/presentation/doctor-ui.d.ts +11 -0
- package/dist/presentation/doctor-ui.js +52 -1
- package/dist/presentation/init-ui.d.ts +9 -0
- package/dist/presentation/init-ui.js +33 -0
- package/dist/runtime/analysis/analyser.js +2 -2
- package/dist/runtime/analysis/rules/warnings/w104-generic-description.d.ts +1 -1
- package/dist/runtime/analysis/rules/warnings/w104-generic-description.js +1 -1
- package/dist/runtime/dev/event-mapper.js +19 -3
- package/dist/runtime/dev/session.d.ts +4 -0
- package/dist/runtime/dev/session.js +52 -3
- package/dist/runtime/llm/ollama.js +4 -4
- package/dist/runtime/mcp/client/manager.js +3 -3
- package/dist/runtime/sandbox/executor.js +5 -5
- package/dist/runtime/test/orchestrator.js +4 -4
- package/dist/utils/editor.d.ts +37 -0
- package/dist/utils/editor.js +137 -0
- package/dist/utils/logger.d.ts +24 -6
- package/dist/utils/logger.js +51 -8
- package/package.json +23 -23
- package/dist/runtime/analysis/rules/errors/e001-missing-output-schema.d.ts +0 -22
- package/dist/runtime/analysis/rules/errors/e001-missing-output-schema.js +0 -30
- package/dist/runtime/analysis/rules/errors/e002-underspecified-input.d.ts +0 -24
- package/dist/runtime/analysis/rules/errors/e002-underspecified-input.js +0 -52
- package/dist/runtime/analysis/rules/errors/e003-type-mismatch.d.ts +0 -23
- package/dist/runtime/analysis/rules/errors/e003-type-mismatch.js +0 -73
- package/dist/runtime/analysis/rules/errors/e004-free-text-propagation.d.ts +0 -23
- package/dist/runtime/analysis/rules/errors/e004-free-text-propagation.js +0 -47
- package/dist/runtime/analysis/rules/errors/e005-tool-ambiguity.d.ts +0 -25
- package/dist/runtime/analysis/rules/errors/e005-tool-ambiguity.js +0 -73
- package/dist/runtime/analysis/rules/errors/e006-param-not-in-description.d.ts +0 -22
- package/dist/runtime/analysis/rules/errors/e006-param-not-in-description.js +0 -57
- package/dist/runtime/analysis/rules/errors/e007-output-not-guaranteed.d.ts +0 -23
- package/dist/runtime/analysis/rules/errors/e007-output-not-guaranteed.js +0 -56
- package/dist/runtime/analysis/rules/errors/e008-circular-dependency.d.ts +0 -22
- package/dist/runtime/analysis/rules/errors/e008-circular-dependency.js +0 -84
- package/dist/runtime/analysis/rules/errors/e009-implicit-user-input.d.ts +0 -23
- package/dist/runtime/analysis/rules/errors/e009-implicit-user-input.js +0 -89
- package/dist/runtime/analysis/rules/errors/e010-non-serializable.d.ts +0 -25
- package/dist/runtime/analysis/rules/errors/e010-non-serializable.js +0 -46
- package/dist/runtime/analysis/rules/errors/e011-missing-tool-description.d.ts +0 -24
- package/dist/runtime/analysis/rules/errors/e011-missing-tool-description.js +0 -33
- package/dist/runtime/analysis/rules/errors/e012-side-effect-detected.d.ts +0 -39
- package/dist/runtime/analysis/rules/errors/e012-side-effect-detected.js +0 -40
- package/dist/runtime/analysis/rules/errors/e013-non-deterministic-output.d.ts +0 -37
- package/dist/runtime/analysis/rules/errors/e013-non-deterministic-output.js +0 -34
- package/dist/runtime/analysis/rules/errors/e013-output-explosion.d.ts +0 -39
- package/dist/runtime/analysis/rules/errors/e013-output-explosion.js +0 -36
- package/dist/runtime/analysis/rules/errors/e014-hidden-dependency.d.ts +0 -42
- package/dist/runtime/analysis/rules/errors/e014-hidden-dependency.js +0 -46
- package/dist/runtime/analysis/rules/errors/e014-output-explosion.d.ts +0 -39
- package/dist/runtime/analysis/rules/errors/e014-output-explosion.js +0 -36
- package/dist/runtime/analysis/rules/errors/e015-hidden-dependency.d.ts +0 -42
- package/dist/runtime/analysis/rules/errors/e015-hidden-dependency.js +0 -46
- package/dist/runtime/analysis/rules/errors/e015-unbounded-execution.d.ts +0 -44
- package/dist/runtime/analysis/rules/errors/e015-unbounded-execution.js +0 -66
- package/dist/runtime/analysis/rules/errors/e016-output-validation-failed.d.ts +0 -43
- package/dist/runtime/analysis/rules/errors/e016-output-validation-failed.js +0 -42
- package/dist/runtime/analysis/rules/errors/e016-unbounded-execution.d.ts +0 -44
- package/dist/runtime/analysis/rules/errors/e016-unbounded-execution.js +0 -66
- package/dist/runtime/analysis/rules/errors/e017-input-validation-failed.d.ts +0 -57
- package/dist/runtime/analysis/rules/errors/e017-input-validation-failed.js +0 -80
- package/dist/runtime/analysis/rules/errors/e017-output-validation-failed.d.ts +0 -43
- package/dist/runtime/analysis/rules/errors/e017-output-validation-failed.js +0 -42
- package/dist/runtime/analysis/rules/errors/e018-input-validation-failed.d.ts +0 -57
- package/dist/runtime/analysis/rules/errors/e018-input-validation-failed.js +0 -80
- package/dist/runtime/analysis/rules/errors/e018-tool-execution-failed.d.ts +0 -38
- package/dist/runtime/analysis/rules/errors/e018-tool-execution-failed.js +0 -37
- package/dist/runtime/analysis/rules/errors/e019-tool-execution-failed.d.ts +0 -38
- package/dist/runtime/analysis/rules/errors/e019-tool-execution-failed.js +0 -37
- package/dist/runtime/analysis/rules/errors/e019-unexpected-test-result.d.ts +0 -65
- package/dist/runtime/analysis/rules/errors/e019-unexpected-test-result.js +0 -109
- package/dist/runtime/analysis/rules/errors/e020-unexpected-test-result.d.ts +0 -65
- package/dist/runtime/analysis/rules/errors/e020-unexpected-test-result.js +0 -109
- package/dist/runtime/analysis/rules/warnings/w001-implicit-dependency.d.ts +0 -22
- package/dist/runtime/analysis/rules/warnings/w001-implicit-dependency.js +0 -39
- package/dist/runtime/analysis/rules/warnings/w002-free-text-without-normalization.d.ts +0 -24
- package/dist/runtime/analysis/rules/warnings/w002-free-text-without-normalization.js +0 -40
- package/dist/runtime/analysis/rules/warnings/w003-missing-examples.d.ts +0 -22
- package/dist/runtime/analysis/rules/warnings/w003-missing-examples.js +0 -84
- package/dist/runtime/analysis/rules/warnings/w004-overloaded-responsibility.d.ts +0 -23
- package/dist/runtime/analysis/rules/warnings/w004-overloaded-responsibility.js +0 -96
- package/dist/runtime/analysis/rules/warnings/w005-generic-description.d.ts +0 -53
- package/dist/runtime/analysis/rules/warnings/w005-generic-description.js +0 -108
- package/dist/runtime/analysis/rules/warnings/w006-optional-as-required.d.ts +0 -22
- package/dist/runtime/analysis/rules/warnings/w006-optional-as-required.js +0 -44
- package/dist/runtime/analysis/rules/warnings/w007-broad-output-schema.d.ts +0 -23
- package/dist/runtime/analysis/rules/warnings/w007-broad-output-schema.js +0 -37
- package/dist/runtime/analysis/rules/warnings/w008-multiple-entry-points.d.ts +0 -22
- package/dist/runtime/analysis/rules/warnings/w008-multiple-entry-points.js +0 -97
- package/dist/runtime/analysis/rules/warnings/w009-hidden-side-effects.d.ts +0 -23
- package/dist/runtime/analysis/rules/warnings/w009-hidden-side-effects.js +0 -88
- package/dist/runtime/analysis/rules/warnings/w010-output-not-reusable.d.ts +0 -22
- package/dist/runtime/analysis/rules/warnings/w010-output-not-reusable.js +0 -81
- package/dist/runtime/analysis/rules/warnings/w021-weak-schema.d.ts +0 -40
- package/dist/runtime/analysis/rules/warnings/w021-weak-schema.js +0 -32
- package/dist/runtime/analysis/rules/warnings/w022-high-entropy-output.d.ts +0 -39
- package/dist/runtime/analysis/rules/warnings/w022-high-entropy-output.js +0 -36
- package/dist/runtime/analysis/rules/warnings/w023-unstable-defaults.d.ts +0 -38
- package/dist/runtime/analysis/rules/warnings/w023-unstable-defaults.js +0 -36
- package/dist/runtime/test/dependency-tracker.d.ts +0 -66
- package/dist/runtime/test/dependency-tracker.js +0 -80
- package/dist/runtime/test/formatters.d.ts +0 -18
- package/dist/runtime/test/formatters.js +0 -172
- package/dist/runtime/test/input-generator.d.ts +0 -33
- package/dist/runtime/test/input-generator.js +0 -498
- package/dist/runtime/test/mcp-root-detector.d.ts +0 -31
- package/dist/runtime/test/mcp-root-detector.js +0 -105
- package/dist/runtime/test/retry-tester.d.ts +0 -44
- package/dist/runtime/test/retry-tester.js +0 -103
- package/dist/runtime/test/synthetic-input-generator.d.ts +0 -11
- package/dist/runtime/test/synthetic-input-generator.js +0 -154
- package/dist/runtime/test/test-runner.d.ts +0 -28
- package/dist/runtime/test/test-runner.js +0 -55
|
@@ -148,10 +148,26 @@ export class DevEventMapper {
|
|
|
148
148
|
*/
|
|
149
149
|
handleValidationFailed(event) {
|
|
150
150
|
const payload = event.payload;
|
|
151
|
+
// Format each validation error with better structure
|
|
151
152
|
const errors = payload.validation_errors
|
|
152
|
-
.map(err =>
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
.map(err => {
|
|
154
|
+
// If the message contains newlines, it's already formatted with details
|
|
155
|
+
// Otherwise, format it simply
|
|
156
|
+
if (err.message.includes('\n')) {
|
|
157
|
+
// Multi-line error message - format with proper indentation
|
|
158
|
+
const lines = err.message.split('\n');
|
|
159
|
+
const firstLine = lines[0];
|
|
160
|
+
const details = lines
|
|
161
|
+
.slice(1)
|
|
162
|
+
.map(line => ` ${line}`)
|
|
163
|
+
.join('\n');
|
|
164
|
+
return ` • ${firstLine}\n${details}`;
|
|
165
|
+
}
|
|
166
|
+
// Simple error message
|
|
167
|
+
return ` • ${err.message} (${err.code})`;
|
|
168
|
+
})
|
|
169
|
+
.join('\n\n');
|
|
170
|
+
this.chatUI.addMessage('system', `❌ Validation failed for **${payload.tool_name}**:\n\n${errors}`);
|
|
155
171
|
}
|
|
156
172
|
/**
|
|
157
173
|
* Handle tool execution started event.
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { SessionLifecycleEventType, LLMContextEventType, LLMProposalEventType, ValidationEventType, } from '../../events/event-type.js';
|
|
6
6
|
import { makePromptID } from '../../types/factories.js';
|
|
7
7
|
import { v4 as uuidv4 } from 'uuid';
|
|
8
|
-
import {
|
|
8
|
+
import { log } from '../../utils/logger.js';
|
|
9
9
|
import { DataManager } from './data-manager.js';
|
|
10
10
|
/**
|
|
11
11
|
* Dev Mode Session implementation.
|
|
@@ -216,7 +216,7 @@ you can request the full data if needed.`;
|
|
|
216
216
|
reason: `Duplicate tool call detected: ${toolCall.name} with identical arguments was already executed`,
|
|
217
217
|
});
|
|
218
218
|
// Use logger instead of console.warn for proper event tracking
|
|
219
|
-
|
|
219
|
+
log.warn(`Skipping duplicate tool call: ${toolCall.name} with arguments ${normalizedArgs}`);
|
|
220
220
|
continue;
|
|
221
221
|
}
|
|
222
222
|
seenKeys.add(key);
|
|
@@ -354,7 +354,27 @@ you can request the full data if needed.`;
|
|
|
354
354
|
if (schema.required) {
|
|
355
355
|
for (const requiredField of schema.required) {
|
|
356
356
|
if (!(requiredField in toolCall.arguments)) {
|
|
357
|
-
|
|
357
|
+
// Extract field information from schema
|
|
358
|
+
const fieldDef = schema.properties?.[requiredField];
|
|
359
|
+
const fieldType = this.formatFieldType(fieldDef);
|
|
360
|
+
const fieldDescription = fieldDef?.description;
|
|
361
|
+
// Build comprehensive error message
|
|
362
|
+
let error = `Missing required field: "${requiredField}"`;
|
|
363
|
+
if (fieldType) {
|
|
364
|
+
error += `\n Expected type: ${fieldType}`;
|
|
365
|
+
}
|
|
366
|
+
if (fieldDescription) {
|
|
367
|
+
error += `\n Description: ${fieldDescription}`;
|
|
368
|
+
}
|
|
369
|
+
if (fieldDef?.enum) {
|
|
370
|
+
const enumValues = fieldDef.enum
|
|
371
|
+
.map(v => (typeof v === 'string' ? `"${v}"` : String(v)))
|
|
372
|
+
.join(', ');
|
|
373
|
+
error += `\n Allowed values: ${enumValues}`;
|
|
374
|
+
}
|
|
375
|
+
if (fieldDef?.format) {
|
|
376
|
+
error += `\n Format: ${fieldDef.format}`;
|
|
377
|
+
}
|
|
358
378
|
await this.config.eventEmitter.emit(ValidationEventType.TOOL_CALL_VALIDATION_FAILED, {
|
|
359
379
|
tool_name: toolCall.name,
|
|
360
380
|
arguments: toolCall.arguments,
|
|
@@ -379,6 +399,35 @@ you can request the full data if needed.`;
|
|
|
379
399
|
});
|
|
380
400
|
return { valid: true };
|
|
381
401
|
}
|
|
402
|
+
/**
|
|
403
|
+
* Format field type information for error messages.
|
|
404
|
+
*/
|
|
405
|
+
formatFieldType(fieldDef) {
|
|
406
|
+
if (!fieldDef) {
|
|
407
|
+
return null;
|
|
408
|
+
}
|
|
409
|
+
const type = fieldDef.type;
|
|
410
|
+
if (!type) {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
// Handle array types
|
|
414
|
+
if (Array.isArray(type)) {
|
|
415
|
+
return type.join(' | ');
|
|
416
|
+
}
|
|
417
|
+
// Handle object type (could be a complex object)
|
|
418
|
+
if (type === 'object') {
|
|
419
|
+
return 'object';
|
|
420
|
+
}
|
|
421
|
+
// Handle array of items
|
|
422
|
+
if (type === 'array') {
|
|
423
|
+
const itemsType = fieldDef.items?.type;
|
|
424
|
+
if (itemsType) {
|
|
425
|
+
return `array<${itemsType}>`;
|
|
426
|
+
}
|
|
427
|
+
return 'array';
|
|
428
|
+
}
|
|
429
|
+
return type;
|
|
430
|
+
}
|
|
382
431
|
/**
|
|
383
432
|
* Build prompt after tool execution.
|
|
384
433
|
*/
|
|
@@ -6,7 +6,7 @@ import { Ollama } from 'ollama';
|
|
|
6
6
|
import * as childProcess from 'child_process';
|
|
7
7
|
import { v4 as uuidv4 } from 'uuid';
|
|
8
8
|
import { ConfigurationError } from '../../utils/errors.js';
|
|
9
|
-
import {
|
|
9
|
+
import { log } from '../../utils/logger.js';
|
|
10
10
|
import { checkCommandExists, checkEnvVar } from '../../config/env-checker.js';
|
|
11
11
|
/**
|
|
12
12
|
* Static process manager for Ollama service.
|
|
@@ -89,12 +89,12 @@ class OllamaProcessManager {
|
|
|
89
89
|
const message = data.toString().trim();
|
|
90
90
|
// Only log errors, not normal startup messages
|
|
91
91
|
if (message && /error|fatal|critical/i.test(message)) {
|
|
92
|
-
|
|
92
|
+
log.warn(`Ollama: ${message}`);
|
|
93
93
|
}
|
|
94
94
|
});
|
|
95
95
|
// Handle process errors
|
|
96
96
|
this.ollamaProcess.on('error', (error) => {
|
|
97
|
-
|
|
97
|
+
log.error(`Error: ${error.message}`, error);
|
|
98
98
|
this.ollamaProcess = null;
|
|
99
99
|
this.isStarting = false;
|
|
100
100
|
throw new ConfigurationError(`Failed to start Ollama service: ${error.message}`);
|
|
@@ -212,7 +212,7 @@ export class OllamaProvider {
|
|
|
212
212
|
const modelsResponse = await this.client.list();
|
|
213
213
|
const modelExists = modelsResponse.models?.some((m) => m.name === this.modelName || m.name.startsWith(`${this.modelName}:`)) ?? false;
|
|
214
214
|
if (!modelExists) {
|
|
215
|
-
|
|
215
|
+
log.info(`Model ${this.modelName} not found. Downloading...`);
|
|
216
216
|
log.blank();
|
|
217
217
|
log.info(`📥 Downloading model: ${this.modelName}`);
|
|
218
218
|
log.plain('This may take a few minutes depending on your internet connection...');
|
|
@@ -7,7 +7,7 @@ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js'
|
|
|
7
7
|
import { TransportEventType } from '../../../events/event-type.js';
|
|
8
8
|
import { getConnectedClient } from '../connection.js';
|
|
9
9
|
import { ConfigurationError } from '../../../utils/errors.js';
|
|
10
|
-
import {
|
|
10
|
+
import { log } from '../../../utils/logger.js';
|
|
11
11
|
import { parseCommand, setupProcessLogCapture, spawnProcess } from './process.js';
|
|
12
12
|
import { BaseMCPClientManager } from './base.js';
|
|
13
13
|
/**
|
|
@@ -106,7 +106,7 @@ export class HTTPMCPClientManager extends BaseMCPClientManager {
|
|
|
106
106
|
catch (error) {
|
|
107
107
|
// Log but don't throw - cleanup errors are non-critical
|
|
108
108
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
109
|
-
|
|
109
|
+
log.error(`Error: ${err.message}`, err);
|
|
110
110
|
await this.eventEmitter.emit(TransportEventType.TRANSPORT_ERROR, {
|
|
111
111
|
transport_type: 'http',
|
|
112
112
|
error_message: err.message,
|
|
@@ -213,7 +213,7 @@ export class StdioMCPClientManager extends BaseMCPClientManager {
|
|
|
213
213
|
}
|
|
214
214
|
catch (error) {
|
|
215
215
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
216
|
-
|
|
216
|
+
log.error(`Error: ${err.message}`, err);
|
|
217
217
|
await this.emitTransportError(err.message, 'DISCONNECT_ERROR', false);
|
|
218
218
|
}
|
|
219
219
|
}
|
|
@@ -12,7 +12,7 @@ import { parseCommand } from '../../runtime/mcp/client/process.js';
|
|
|
12
12
|
import { ConfigurationError } from '../../utils/errors.js';
|
|
13
13
|
import { formatTimeString } from './time-parser.js';
|
|
14
14
|
import { ToolExecutionErrorType } from './types.js';
|
|
15
|
-
import {
|
|
15
|
+
import { log } from '../../utils/logger.js';
|
|
16
16
|
// Re-export types for convenience
|
|
17
17
|
export * from './types.js';
|
|
18
18
|
/**
|
|
@@ -51,7 +51,7 @@ export class SandboxExecutor {
|
|
|
51
51
|
if (isNodeCommand) {
|
|
52
52
|
// For Node.js: inject --max-old-space-size flag
|
|
53
53
|
args = ['--max-old-space-size', String(memoryMB), ...args];
|
|
54
|
-
|
|
54
|
+
log.debug(`Memory limit: ${memoryMB}MB enforced via --max-old-space-size for Node.js`);
|
|
55
55
|
}
|
|
56
56
|
else if (process.platform !== 'win32') {
|
|
57
57
|
// For POSIX systems: wrap with ulimit -v (virtual memory limit)
|
|
@@ -64,12 +64,12 @@ export class SandboxExecutor {
|
|
|
64
64
|
executable = '/bin/sh';
|
|
65
65
|
args = ['-c', `ulimit -v ${memoryKB} && exec ${originalCommand}`];
|
|
66
66
|
isWrapped = true;
|
|
67
|
-
|
|
67
|
+
log.debug(`Memory limit: ${memoryMB}MB (${memoryKB}KB) enforced via ulimit -v`);
|
|
68
68
|
}
|
|
69
69
|
else {
|
|
70
70
|
// Windows: Memory limits require different approach (SetProcessWorkingSetSize, Job Objects)
|
|
71
71
|
// For now, log warning that limit is not enforced on Windows
|
|
72
|
-
|
|
72
|
+
log.warn(`Memory limit ${memoryMB}MB specified but not enforced on Windows platform`);
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
// Suppress stderr output if requested (for CI mode)
|
|
@@ -137,7 +137,7 @@ export class SandboxExecutor {
|
|
|
137
137
|
: typeof event === 'object' && event !== null && 'message' in event
|
|
138
138
|
? String(event.message)
|
|
139
139
|
: 'Unknown MCP client error';
|
|
140
|
-
|
|
140
|
+
log.error(`Error: ${errorMessage}`, event instanceof Error ? event : new Error(errorMessage));
|
|
141
141
|
// Errors will also be caught in try-catch blocks during tool execution
|
|
142
142
|
};
|
|
143
143
|
// Connect client to server (StdioClientTransport spawns the process here)
|
|
@@ -27,7 +27,7 @@ import { E600UnexpectedTestResult } from '../../runtime/analysis/rules/errors/e6
|
|
|
27
27
|
// Centralized error code constants - single source of truth for error codes
|
|
28
28
|
import { ERROR_CODES, ERROR_TYPE_TO_CODE, } from '../../runtime/analysis/rules/error-codes.js';
|
|
29
29
|
import { applyStrictMode, computeVerdict, } from '../../runtime/analysis/strict-mode.js';
|
|
30
|
-
import {
|
|
30
|
+
import { log } from '../../utils/logger.js';
|
|
31
31
|
import { ConfigurationError } from '../../utils/errors.js';
|
|
32
32
|
/**
|
|
33
33
|
* Test orchestrator for tool validation.
|
|
@@ -158,7 +158,7 @@ export class TestOrchestrator {
|
|
|
158
158
|
const mcpCommand = this.options.mcpCommand || config.script;
|
|
159
159
|
const scriptName = mcpCommand ? String(mcpCommand) : 'unknown';
|
|
160
160
|
if (!this.options.ci) {
|
|
161
|
-
|
|
161
|
+
log.warn(`Tool "${toolName}" not found in MCP server. Running: ${scriptName}`);
|
|
162
162
|
}
|
|
163
163
|
// Create a diagnostic error for missing tool using E000 rule
|
|
164
164
|
const diagnostics = E000ToolNotFound.checkWithRuntimeContext({
|
|
@@ -190,12 +190,12 @@ export class TestOrchestrator {
|
|
|
190
190
|
try {
|
|
191
191
|
toolTimeoutMs = parseTimeString(contract.guarantees.max_execution_time);
|
|
192
192
|
if (!this.options.ci) {
|
|
193
|
-
|
|
193
|
+
log.info(`Tool "${toolName}" declared max_execution_time: ${contract.guarantees.max_execution_time} (${toolTimeoutMs}ms)`);
|
|
194
194
|
}
|
|
195
195
|
}
|
|
196
196
|
catch (_error) {
|
|
197
197
|
if (!this.options.ci) {
|
|
198
|
-
|
|
198
|
+
log.warn(`Invalid max_execution_time for tool "${toolName}": ${contract.guarantees.max_execution_time}. Using global default.`);
|
|
199
199
|
}
|
|
200
200
|
}
|
|
201
201
|
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Editor utility for opening files in system editor.
|
|
3
|
+
* Supports nano (Unix) and notepad (Windows).
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Get the appropriate editor command for the current platform.
|
|
7
|
+
* Checks EDITOR and VISUAL environment variables first, then falls back to platform defaults.
|
|
8
|
+
* @returns Editor command
|
|
9
|
+
*/
|
|
10
|
+
export declare function getEditor(): string;
|
|
11
|
+
/**
|
|
12
|
+
* Check file permissions and return security status.
|
|
13
|
+
* @param filePath - Path to file to check
|
|
14
|
+
* @returns Permission check result
|
|
15
|
+
*/
|
|
16
|
+
export declare function checkFilePermissions(filePath: string): {
|
|
17
|
+
isSecure: boolean;
|
|
18
|
+
currentMode: string;
|
|
19
|
+
recommendedMode: string;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Ensure a file exists, creating it with a template if needed.
|
|
23
|
+
* @param filePath - Path to file
|
|
24
|
+
* @param template - Template content to use if file doesn't exist
|
|
25
|
+
* @param secure - Whether to set secure permissions (default: true for .env files)
|
|
26
|
+
*/
|
|
27
|
+
export declare function ensureFileExists(filePath: string, template: string, secure?: boolean): void;
|
|
28
|
+
/**
|
|
29
|
+
* Open a file in the system editor.
|
|
30
|
+
* @param filePath - Path to file to open
|
|
31
|
+
* @param createIfMissing - Whether to create file if it doesn't exist (default: false)
|
|
32
|
+
* @param template - Template content if creating file (optional)
|
|
33
|
+
* @returns Promise that resolves when editor closes
|
|
34
|
+
* @throws {Error} If editor fails to open
|
|
35
|
+
*/
|
|
36
|
+
export declare function openInEditor(filePath: string, createIfMissing?: boolean, template?: string): Promise<void>;
|
|
37
|
+
//# sourceMappingURL=editor.d.ts.map
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Editor utility for opening files in system editor.
|
|
3
|
+
* Supports nano (Unix) and notepad (Windows).
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import { spawn } from 'child_process';
|
|
8
|
+
/**
|
|
9
|
+
* Get the appropriate editor command for the current platform.
|
|
10
|
+
* Checks EDITOR and VISUAL environment variables first, then falls back to platform defaults.
|
|
11
|
+
* @returns Editor command
|
|
12
|
+
*/
|
|
13
|
+
export function getEditor() {
|
|
14
|
+
// Check environment variables first (user preference)
|
|
15
|
+
if (process.env.EDITOR && process.env.EDITOR.trim() !== '') {
|
|
16
|
+
return process.env.EDITOR.trim();
|
|
17
|
+
}
|
|
18
|
+
if (process.env.VISUAL && process.env.VISUAL.trim() !== '') {
|
|
19
|
+
return process.env.VISUAL.trim();
|
|
20
|
+
}
|
|
21
|
+
// Fall back to platform defaults
|
|
22
|
+
if (process.platform === 'win32') {
|
|
23
|
+
return 'notepad';
|
|
24
|
+
}
|
|
25
|
+
return 'nano';
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get editor arguments for opening a file.
|
|
29
|
+
* @param editor - Editor command
|
|
30
|
+
* @param filePath - Path to file to open
|
|
31
|
+
* @returns Array of arguments for the editor
|
|
32
|
+
*/
|
|
33
|
+
function getEditorArgs(editor, filePath) {
|
|
34
|
+
// For notepad and nano, just pass the file path
|
|
35
|
+
return [filePath];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Check file permissions and return security status.
|
|
39
|
+
* @param filePath - Path to file to check
|
|
40
|
+
* @returns Permission check result
|
|
41
|
+
*/
|
|
42
|
+
export function checkFilePermissions(filePath) {
|
|
43
|
+
if (!fs.existsSync(filePath)) {
|
|
44
|
+
return {
|
|
45
|
+
isSecure: true, // Non-existent files are considered secure
|
|
46
|
+
currentMode: 'N/A',
|
|
47
|
+
recommendedMode: '600',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
try {
|
|
51
|
+
const stats = fs.statSync(filePath);
|
|
52
|
+
// Normalize octal string to 3 digits for consistent comparison
|
|
53
|
+
const mode = stats.mode.toString(8).slice(-3).padStart(3, '0');
|
|
54
|
+
const isSecure = mode === '600' || mode === '400'; // Read/write owner only, or read-only owner
|
|
55
|
+
return {
|
|
56
|
+
isSecure,
|
|
57
|
+
currentMode: mode,
|
|
58
|
+
recommendedMode: '600',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// If we can't check permissions, assume insecure
|
|
63
|
+
return {
|
|
64
|
+
isSecure: false,
|
|
65
|
+
currentMode: 'unknown',
|
|
66
|
+
recommendedMode: '600',
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Ensure a file exists, creating it with a template if needed.
|
|
72
|
+
* @param filePath - Path to file
|
|
73
|
+
* @param template - Template content to use if file doesn't exist
|
|
74
|
+
* @param secure - Whether to set secure permissions (default: true for .env files)
|
|
75
|
+
*/
|
|
76
|
+
export function ensureFileExists(filePath, template, secure = true) {
|
|
77
|
+
if (fs.existsSync(filePath)) {
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
// Create directory if needed
|
|
81
|
+
const dir = path.dirname(filePath);
|
|
82
|
+
if (!fs.existsSync(dir)) {
|
|
83
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
84
|
+
}
|
|
85
|
+
// Create file with template
|
|
86
|
+
fs.writeFileSync(filePath, template, 'utf-8');
|
|
87
|
+
// Set secure permissions if requested
|
|
88
|
+
if (secure) {
|
|
89
|
+
try {
|
|
90
|
+
fs.chmodSync(filePath, 0o600);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Ignore permission errors (may not work on all systems)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Open a file in the system editor.
|
|
99
|
+
* @param filePath - Path to file to open
|
|
100
|
+
* @param createIfMissing - Whether to create file if it doesn't exist (default: false)
|
|
101
|
+
* @param template - Template content if creating file (optional)
|
|
102
|
+
* @returns Promise that resolves when editor closes
|
|
103
|
+
* @throws {Error} If editor fails to open
|
|
104
|
+
*/
|
|
105
|
+
export async function openInEditor(filePath, createIfMissing = false, template) {
|
|
106
|
+
// Ensure file exists if requested
|
|
107
|
+
if (createIfMissing && !fs.existsSync(filePath) && template) {
|
|
108
|
+
ensureFileExists(filePath, template, filePath.endsWith('.env'));
|
|
109
|
+
}
|
|
110
|
+
// Check if file exists
|
|
111
|
+
if (!fs.existsSync(filePath)) {
|
|
112
|
+
throw new Error(`File does not exist: ${filePath}`);
|
|
113
|
+
}
|
|
114
|
+
const editor = getEditor();
|
|
115
|
+
const args = getEditorArgs(editor, filePath);
|
|
116
|
+
return new Promise((resolve, reject) => {
|
|
117
|
+
const proc = spawn(editor, args, {
|
|
118
|
+
stdio: 'inherit',
|
|
119
|
+
shell: true,
|
|
120
|
+
});
|
|
121
|
+
proc.on('exit', code => {
|
|
122
|
+
if (code === 0 || code === null) {
|
|
123
|
+
// Exit code 0 or null (normal exit) - user saved and closed
|
|
124
|
+
resolve();
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
// Non-zero exit code - might be user cancellation or error
|
|
128
|
+
// We'll treat it as success (user may have cancelled, which is fine)
|
|
129
|
+
resolve();
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
proc.on('error', error => {
|
|
133
|
+
reject(new Error(`Failed to open editor "${editor}": ${error.message}. Make sure the editor is installed and available in your PATH.`));
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
//# sourceMappingURL=editor.js.map
|
package/dist/utils/logger.d.ts
CHANGED
|
@@ -54,6 +54,14 @@ export declare class Log {
|
|
|
54
54
|
* Set the log level.
|
|
55
55
|
*/
|
|
56
56
|
setLevel(level: LogLevel): void;
|
|
57
|
+
/**
|
|
58
|
+
* Get the current log level.
|
|
59
|
+
*/
|
|
60
|
+
getLevel(): LogLevel;
|
|
61
|
+
/**
|
|
62
|
+
* Check if the logger is in quiet mode (only errors shown).
|
|
63
|
+
*/
|
|
64
|
+
isQuiet(): boolean;
|
|
57
65
|
/**
|
|
58
66
|
* Enable or disable structured logging.
|
|
59
67
|
*/
|
|
@@ -67,7 +75,8 @@ export declare class Log {
|
|
|
67
75
|
*/
|
|
68
76
|
private structuredLog;
|
|
69
77
|
/**
|
|
70
|
-
* Print styled text to console.
|
|
78
|
+
* Print styled text to console at INFO level.
|
|
79
|
+
* Respects log level - won't print if level is higher than INFO.
|
|
71
80
|
*/
|
|
72
81
|
private print;
|
|
73
82
|
/**
|
|
@@ -92,6 +101,7 @@ export declare class Log {
|
|
|
92
101
|
* Log/print error message (red) with error icon.
|
|
93
102
|
* For structured logging: logs at ERROR level (can include Error object)
|
|
94
103
|
* For styled output: prints with red color and error icon
|
|
104
|
+
* Note: Errors always show regardless of log level (for quiet mode).
|
|
95
105
|
*/
|
|
96
106
|
error(message: string, errorOrIcon?: Error | IconType, contextOrIcon?: Record<string, unknown> | IconType): void;
|
|
97
107
|
/**
|
|
@@ -124,6 +134,7 @@ export declare class Log {
|
|
|
124
134
|
debug(text: string, icon?: IconType): void;
|
|
125
135
|
/**
|
|
126
136
|
* Print a line with label and value.
|
|
137
|
+
* Respects log level.
|
|
127
138
|
*/
|
|
128
139
|
labelValue(labelText: string, valueText: string): void;
|
|
129
140
|
/**
|
|
@@ -154,10 +165,12 @@ export declare class Log {
|
|
|
154
165
|
warnSymbol(text: string): void;
|
|
155
166
|
/**
|
|
156
167
|
* Print plain text (no styling).
|
|
168
|
+
* Respects log level.
|
|
157
169
|
*/
|
|
158
170
|
plain(text: string): void;
|
|
159
171
|
/**
|
|
160
172
|
* Print an empty line.
|
|
173
|
+
* Respects log level.
|
|
161
174
|
*/
|
|
162
175
|
blank(): void;
|
|
163
176
|
/**
|
|
@@ -167,15 +180,20 @@ export declare class Log {
|
|
|
167
180
|
styleText(text: string, ...styles: ColorKey[]): string;
|
|
168
181
|
}
|
|
169
182
|
/**
|
|
170
|
-
* Default logger instance for styled output
|
|
171
|
-
* Use this for
|
|
183
|
+
* Default logger instance for styled output.
|
|
184
|
+
* Use this for all logging throughout the application.
|
|
172
185
|
*/
|
|
173
186
|
export declare const log: Log;
|
|
174
187
|
/**
|
|
175
|
-
*
|
|
176
|
-
*
|
|
188
|
+
* Set quiet mode (errors only).
|
|
189
|
+
* @param enabled - Whether to enable quiet mode
|
|
190
|
+
*/
|
|
191
|
+
export declare function setQuietMode(enabled: boolean): void;
|
|
192
|
+
/**
|
|
193
|
+
* Set verbose mode (debug level).
|
|
194
|
+
* @param enabled - Whether to enable verbose mode
|
|
177
195
|
*/
|
|
178
|
-
export declare
|
|
196
|
+
export declare function setVerboseMode(enabled: boolean): void;
|
|
179
197
|
/**
|
|
180
198
|
* Create a logger with a specific context.
|
|
181
199
|
*/
|
package/dist/utils/logger.js
CHANGED
|
@@ -56,6 +56,18 @@ export class Log {
|
|
|
56
56
|
setLevel(level) {
|
|
57
57
|
this.level = level;
|
|
58
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Get the current log level.
|
|
61
|
+
*/
|
|
62
|
+
getLevel() {
|
|
63
|
+
return this.level;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if the logger is in quiet mode (only errors shown).
|
|
67
|
+
*/
|
|
68
|
+
isQuiet() {
|
|
69
|
+
return this.level >= LogLevel.ERROR;
|
|
70
|
+
}
|
|
59
71
|
/**
|
|
60
72
|
* Enable or disable structured logging.
|
|
61
73
|
*/
|
|
@@ -117,9 +129,13 @@ export class Log {
|
|
|
117
129
|
}
|
|
118
130
|
}
|
|
119
131
|
/**
|
|
120
|
-
* Print styled text to console.
|
|
132
|
+
* Print styled text to console at INFO level.
|
|
133
|
+
* Respects log level - won't print if level is higher than INFO.
|
|
121
134
|
*/
|
|
122
135
|
print(text, ...styles) {
|
|
136
|
+
if (LogLevel.INFO < this.level) {
|
|
137
|
+
return; // Skip if log level is higher than INFO (e.g., quiet mode)
|
|
138
|
+
}
|
|
123
139
|
if (this.useStructuredLogging) {
|
|
124
140
|
this.structuredLog(LogLevel.INFO, text);
|
|
125
141
|
}
|
|
@@ -167,6 +183,7 @@ export class Log {
|
|
|
167
183
|
* Log/print error message (red) with error icon.
|
|
168
184
|
* For structured logging: logs at ERROR level (can include Error object)
|
|
169
185
|
* For styled output: prints with red color and error icon
|
|
186
|
+
* Note: Errors always show regardless of log level (for quiet mode).
|
|
170
187
|
*/
|
|
171
188
|
error(message, errorOrIcon, contextOrIcon) {
|
|
172
189
|
if (this.useStructuredLogging) {
|
|
@@ -179,9 +196,10 @@ export class Log {
|
|
|
179
196
|
}
|
|
180
197
|
else {
|
|
181
198
|
// Styled output: errorOrIcon is optional Icon
|
|
199
|
+
// Errors always print regardless of log level (critical for quiet mode)
|
|
182
200
|
const icon = errorOrIcon;
|
|
183
201
|
const iconText = icon ? `${icon} ` : `${Icon.ERROR} `;
|
|
184
|
-
this.
|
|
202
|
+
console.log(this.style(`${iconText}${message}`, 'red'));
|
|
185
203
|
}
|
|
186
204
|
}
|
|
187
205
|
warning(text, contextOrIcon) {
|
|
@@ -238,8 +256,12 @@ export class Log {
|
|
|
238
256
|
}
|
|
239
257
|
/**
|
|
240
258
|
* Print a line with label and value.
|
|
259
|
+
* Respects log level.
|
|
241
260
|
*/
|
|
242
261
|
labelValue(labelText, valueText) {
|
|
262
|
+
if (LogLevel.INFO < this.level) {
|
|
263
|
+
return; // Skip if log level is higher than INFO
|
|
264
|
+
}
|
|
243
265
|
const output = `${this.style(labelText, 'dim')} ${this.style(valueText, 'cyan')}`;
|
|
244
266
|
if (this.useStructuredLogging) {
|
|
245
267
|
this.structuredLog(LogLevel.INFO, `${labelText} ${valueText}`);
|
|
@@ -288,8 +310,12 @@ export class Log {
|
|
|
288
310
|
}
|
|
289
311
|
/**
|
|
290
312
|
* Print plain text (no styling).
|
|
313
|
+
* Respects log level.
|
|
291
314
|
*/
|
|
292
315
|
plain(text) {
|
|
316
|
+
if (LogLevel.INFO < this.level) {
|
|
317
|
+
return; // Skip if log level is higher than INFO
|
|
318
|
+
}
|
|
293
319
|
if (this.useStructuredLogging) {
|
|
294
320
|
this.structuredLog(LogLevel.INFO, text);
|
|
295
321
|
}
|
|
@@ -299,8 +325,12 @@ export class Log {
|
|
|
299
325
|
}
|
|
300
326
|
/**
|
|
301
327
|
* Print an empty line.
|
|
328
|
+
* Respects log level.
|
|
302
329
|
*/
|
|
303
330
|
blank() {
|
|
331
|
+
if (LogLevel.INFO < this.level) {
|
|
332
|
+
return; // Skip if log level is higher than INFO
|
|
333
|
+
}
|
|
304
334
|
console.log('');
|
|
305
335
|
}
|
|
306
336
|
/**
|
|
@@ -312,19 +342,32 @@ export class Log {
|
|
|
312
342
|
}
|
|
313
343
|
}
|
|
314
344
|
/**
|
|
315
|
-
* Default logger instance for styled output
|
|
316
|
-
* Use this for
|
|
345
|
+
* Default logger instance for styled output.
|
|
346
|
+
* Use this for all logging throughout the application.
|
|
317
347
|
*/
|
|
318
348
|
export const log = new Log(LogLevel.INFO, {}, false);
|
|
319
349
|
/**
|
|
320
|
-
*
|
|
321
|
-
*
|
|
350
|
+
* Set quiet mode (errors only).
|
|
351
|
+
* @param enabled - Whether to enable quiet mode
|
|
352
|
+
*/
|
|
353
|
+
export function setQuietMode(enabled) {
|
|
354
|
+
if (enabled) {
|
|
355
|
+
log.setLevel(LogLevel.ERROR);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Set verbose mode (debug level).
|
|
360
|
+
* @param enabled - Whether to enable verbose mode
|
|
322
361
|
*/
|
|
323
|
-
export
|
|
362
|
+
export function setVerboseMode(enabled) {
|
|
363
|
+
if (enabled) {
|
|
364
|
+
log.setLevel(LogLevel.DEBUG);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
324
367
|
/**
|
|
325
368
|
* Create a logger with a specific context.
|
|
326
369
|
*/
|
|
327
370
|
export function createLogger(context) {
|
|
328
|
-
return
|
|
371
|
+
return log.child(context);
|
|
329
372
|
}
|
|
330
373
|
//# sourceMappingURL=logger.js.map
|