@syrin/cli 1.3.1 → 1.4.0
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 +36 -0
- 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 +4 -4
- 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
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Presentation layer for config command UI.
|
|
3
|
+
* Separates display logic from business logic.
|
|
4
|
+
*/
|
|
5
|
+
import { log } from '../utils/logger.js';
|
|
6
|
+
import { Icons } from '../constants/index.js';
|
|
7
|
+
import { getGlobalConfigPath } from '../config/global-loader.js';
|
|
8
|
+
/**
|
|
9
|
+
* Mask an API key to show only first 4 and last 4 characters.
|
|
10
|
+
*/
|
|
11
|
+
function maskApiKey(apiKey) {
|
|
12
|
+
if (!apiKey || apiKey.length <= 8) {
|
|
13
|
+
return '****';
|
|
14
|
+
}
|
|
15
|
+
// If it looks like an env var reference (e.g., OPENAI_API_KEY), don't mask
|
|
16
|
+
if (/^[A-Z][A-Z0-9_]*$/.test(apiKey)) {
|
|
17
|
+
return apiKey;
|
|
18
|
+
}
|
|
19
|
+
return `${apiKey.slice(0, 4)}...${apiKey.slice(-4)}`;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Display configuration list/show output.
|
|
23
|
+
*/
|
|
24
|
+
export function displayConfigList(config, context, configPath) {
|
|
25
|
+
log.blank();
|
|
26
|
+
log.heading(`${context === 'local' ? 'Local' : 'Global'} Configuration`);
|
|
27
|
+
log.blank();
|
|
28
|
+
log.labelValue(`${Icons.FOLDER} Path`, configPath);
|
|
29
|
+
log.blank();
|
|
30
|
+
if (context === 'local' && 'transport' in config) {
|
|
31
|
+
log.heading('Project Settings');
|
|
32
|
+
log.labelValue(' project_name', String(config.project_name));
|
|
33
|
+
log.labelValue(' agent_name', String(config.agent_name));
|
|
34
|
+
log.labelValue(' transport', config.transport);
|
|
35
|
+
if (config.mcp_url) {
|
|
36
|
+
log.labelValue(' mcp_url', String(config.mcp_url));
|
|
37
|
+
}
|
|
38
|
+
if (config.script) {
|
|
39
|
+
log.labelValue(' script', String(config.script));
|
|
40
|
+
}
|
|
41
|
+
log.blank();
|
|
42
|
+
}
|
|
43
|
+
log.heading('LLM Providers');
|
|
44
|
+
const providers = Object.entries(config.llm);
|
|
45
|
+
if (providers.length === 0) {
|
|
46
|
+
log.plain(' No providers configured');
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
for (const [provider, settings] of providers) {
|
|
50
|
+
const isDefault = settings.default || false;
|
|
51
|
+
const defaultMarker = isDefault ? ' (default)' : '';
|
|
52
|
+
log.label(` ${provider}${defaultMarker}`);
|
|
53
|
+
if (settings.API_KEY) {
|
|
54
|
+
log.labelValue(' API_KEY', maskApiKey(String(settings.API_KEY)));
|
|
55
|
+
}
|
|
56
|
+
if (settings.MODEL_NAME) {
|
|
57
|
+
log.labelValue(' MODEL_NAME', String(settings.MODEL_NAME));
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
log.blank();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Display success message for config update.
|
|
65
|
+
*/
|
|
66
|
+
export function displayConfigUpdated(key, value, context, configPath) {
|
|
67
|
+
log.blank();
|
|
68
|
+
log.success(`${Icons.CHECK} Configuration updated successfully`);
|
|
69
|
+
log.blank();
|
|
70
|
+
log.labelValue(' Key', key);
|
|
71
|
+
log.labelValue(' Value', value);
|
|
72
|
+
log.labelValue(' Config', `${context} (${configPath})`);
|
|
73
|
+
log.blank();
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Display message for opening editor.
|
|
77
|
+
*/
|
|
78
|
+
export function displayEditorOpening(context, filePath, editorName) {
|
|
79
|
+
log.blank();
|
|
80
|
+
log.info(`Opening ${context} config in editor...`);
|
|
81
|
+
log.labelValue(` ${Icons.FOLDER} File`, filePath);
|
|
82
|
+
log.labelValue(` ${Icons.DOCUMENT} Editor`, editorName);
|
|
83
|
+
log.blank();
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Display message after editor closes.
|
|
87
|
+
*/
|
|
88
|
+
export function displayEditorClosed(fileType) {
|
|
89
|
+
log.blank();
|
|
90
|
+
if (fileType === 'config') {
|
|
91
|
+
log.success(`Config file updated`);
|
|
92
|
+
log.blank();
|
|
93
|
+
log.info(`Run \`syrin doctor\` to validate your changes`);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
log.success(`${Icons.CHECK} Environment file updated`);
|
|
97
|
+
}
|
|
98
|
+
log.blank();
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Display setup completion message.
|
|
102
|
+
*/
|
|
103
|
+
export function displaySetupComplete(isGlobal) {
|
|
104
|
+
log.blank();
|
|
105
|
+
if (isGlobal) {
|
|
106
|
+
log.success(`${Icons.CHECK} Global configuration created`);
|
|
107
|
+
log.blank();
|
|
108
|
+
log.labelValue(` ${Icons.FOLDER} Config`, getGlobalConfigPath());
|
|
109
|
+
log.blank();
|
|
110
|
+
log.info(`${Icons.DOCUMENT} Next steps:`);
|
|
111
|
+
log.plain(' 1. Set your API keys in ~/.syrin/.env or as environment variables');
|
|
112
|
+
log.plain(' 2. Run `syrin config edit-env --global` to edit the environment file');
|
|
113
|
+
log.plain(' 3. Run `syrin doctor` to validate your setup');
|
|
114
|
+
}
|
|
115
|
+
log.blank();
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Display default provider set message.
|
|
119
|
+
*/
|
|
120
|
+
export function displayDefaultProviderSet(provider, context) {
|
|
121
|
+
log.blank();
|
|
122
|
+
log.success(`${Icons.CHECK} Default provider updated`);
|
|
123
|
+
log.blank();
|
|
124
|
+
log.labelValue(' Provider', provider);
|
|
125
|
+
log.labelValue(' Config', context);
|
|
126
|
+
log.blank();
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Display provider removed message.
|
|
130
|
+
*/
|
|
131
|
+
export function displayProviderRemoved(provider, context) {
|
|
132
|
+
log.blank();
|
|
133
|
+
log.success(`${Icons.CHECK} Provider removed`);
|
|
134
|
+
log.blank();
|
|
135
|
+
log.labelValue(' Provider', provider);
|
|
136
|
+
log.labelValue(' Config', context);
|
|
137
|
+
log.blank();
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=config-ui.js.map
|
|
@@ -17,6 +17,9 @@ interface DoctorReport {
|
|
|
17
17
|
mcp_url?: unknown;
|
|
18
18
|
script?: unknown;
|
|
19
19
|
};
|
|
20
|
+
configSource?: 'local' | 'global';
|
|
21
|
+
configPath?: string;
|
|
22
|
+
envSource?: 'local.env' | 'global.env' | 'process.env';
|
|
20
23
|
transportCheck: CheckResult;
|
|
21
24
|
scriptCheck: CheckResult | null;
|
|
22
25
|
llmChecks: Array<{
|
|
@@ -31,6 +34,14 @@ interface DoctorReport {
|
|
|
31
34
|
modelName?: string;
|
|
32
35
|
}>;
|
|
33
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Display global config validation results.
|
|
39
|
+
*/
|
|
40
|
+
export declare function displayGlobalConfigValidation(globalConfigPath: string, globalEnvPath: string, globalEnvExists: boolean, llmChecks: Array<{
|
|
41
|
+
provider: string;
|
|
42
|
+
apiKeyCheck: CheckResult;
|
|
43
|
+
modelCheck: CheckResult;
|
|
44
|
+
}>): void;
|
|
34
45
|
/**
|
|
35
46
|
* Display doctor report using plain console output.
|
|
36
47
|
* This avoids Ink taking control of stdin, which disables terminal history.
|
|
@@ -1,11 +1,49 @@
|
|
|
1
1
|
import { getVersionDisplayString } from '../utils/version-display.js';
|
|
2
2
|
import { log } from '../utils/logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* Display global config validation results.
|
|
5
|
+
*/
|
|
6
|
+
export function displayGlobalConfigValidation(globalConfigPath, globalEnvPath, globalEnvExists, llmChecks) {
|
|
7
|
+
log.blank();
|
|
8
|
+
log.heading('Validating Syrin configuration...');
|
|
9
|
+
log.blank();
|
|
10
|
+
log.label('No local config found at ./syrin.yaml');
|
|
11
|
+
log.label('Using global configuration...');
|
|
12
|
+
log.blank();
|
|
13
|
+
log.labelValue('Global Config:', globalConfigPath);
|
|
14
|
+
if (globalEnvExists) {
|
|
15
|
+
log.labelValue('Global Environment:', globalEnvPath);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
log.labelValue('Global Environment:', `${globalEnvPath} (not found)`);
|
|
19
|
+
}
|
|
20
|
+
log.blank();
|
|
21
|
+
const hasErrors = llmChecks.some(check => !check.apiKeyCheck.isValid || !check.modelCheck.isValid);
|
|
22
|
+
if (hasErrors) {
|
|
23
|
+
log.heading('Global configuration has issues:');
|
|
24
|
+
log.blank();
|
|
25
|
+
for (const check of llmChecks) {
|
|
26
|
+
if (!check.apiKeyCheck.isValid) {
|
|
27
|
+
log.plain(` • ${check.provider} API Key: ${check.apiKeyCheck.fix || 'Missing'}`);
|
|
28
|
+
}
|
|
29
|
+
if (!check.modelCheck.isValid) {
|
|
30
|
+
log.plain(` • ${check.provider} Model: ${check.modelCheck.fix || 'Missing'}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
log.blank();
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
log.success('Global configuration is valid!');
|
|
37
|
+
log.label('To create a local config, run: syrin init');
|
|
38
|
+
log.blank();
|
|
39
|
+
}
|
|
40
|
+
}
|
|
3
41
|
/**
|
|
4
42
|
* Display doctor report using plain console output.
|
|
5
43
|
* This avoids Ink taking control of stdin, which disables terminal history.
|
|
6
44
|
*/
|
|
7
45
|
export async function displayDoctorReport(report) {
|
|
8
|
-
const { config, transportCheck, scriptCheck, llmChecks, localLlmChecks } = report;
|
|
46
|
+
const { config, configSource, configPath, envSource, transportCheck, scriptCheck, llmChecks, localLlmChecks, } = report;
|
|
9
47
|
const allValid = transportCheck.isValid &&
|
|
10
48
|
(scriptCheck === null || scriptCheck.isValid) &&
|
|
11
49
|
llmChecks.every(l => l.apiKeyCheck.isValid && l.modelCheck.isValid) &&
|
|
@@ -19,6 +57,19 @@ export async function displayDoctorReport(report) {
|
|
|
19
57
|
log.label('═══════════════════');
|
|
20
58
|
log.labelValue('Version:', versionDisplayString);
|
|
21
59
|
log.blank();
|
|
60
|
+
// Config Source Info
|
|
61
|
+
if (configSource && configPath) {
|
|
62
|
+
log.labelValue(`${configSource === 'local' ? 'Local' : 'Global'} Config:`, configPath);
|
|
63
|
+
if (envSource) {
|
|
64
|
+
const envSourceLabel = envSource === 'local.env'
|
|
65
|
+
? 'Local project .env file'
|
|
66
|
+
: envSource === 'global.env'
|
|
67
|
+
? 'Global .env file'
|
|
68
|
+
: 'process.env';
|
|
69
|
+
log.labelValue('Environment Source:', envSourceLabel);
|
|
70
|
+
}
|
|
71
|
+
log.blank();
|
|
72
|
+
}
|
|
22
73
|
// Project Info
|
|
23
74
|
log.labelValue('Project:', String(config.project_name));
|
|
24
75
|
log.labelValue('Agent:', String(config.agent_name));
|
|
@@ -11,4 +11,13 @@ export declare function displayAlreadyInitialized(): void;
|
|
|
11
11
|
* @param configPath - Path to the created config file
|
|
12
12
|
*/
|
|
13
13
|
export declare function displayInitSuccess(configPath: string): void;
|
|
14
|
+
/**
|
|
15
|
+
* Display the "global already initialized" message.
|
|
16
|
+
*/
|
|
17
|
+
export declare function displayGlobalAlreadyInitialized(): void;
|
|
18
|
+
/**
|
|
19
|
+
* Display the global initialization success message.
|
|
20
|
+
* @param configPath - Path to the created global config file
|
|
21
|
+
*/
|
|
22
|
+
export declare function displayGlobalInitSuccess(configPath: string): void;
|
|
14
23
|
//# sourceMappingURL=init-ui.d.ts.map
|
|
@@ -38,4 +38,37 @@ export function displayInitSuccess(configPath) {
|
|
|
38
38
|
log.plain(` 4. ${Messages.INIT_RUN_DEV(ToolCommands.DEV)}`);
|
|
39
39
|
log.blank();
|
|
40
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Display the "global already initialized" message.
|
|
43
|
+
*/
|
|
44
|
+
export function displayGlobalAlreadyInitialized() {
|
|
45
|
+
log.blank();
|
|
46
|
+
log.warning(` ${Messages.GLOBAL_INIT_ALREADY_INITIALIZED}`);
|
|
47
|
+
log.blank();
|
|
48
|
+
log.plain(`${Icons.FOLDER} ${Messages.GLOBAL_INIT_CONFIG_FILE(Paths.GLOBAL_CONFIG_FILE)}`);
|
|
49
|
+
log.blank();
|
|
50
|
+
log.plain(`${Icons.DOCUMENT} ${Messages.INIT_NEXT_STEPS_HEADER}`);
|
|
51
|
+
log.plain(` ${Messages.GLOBAL_INIT_EDIT_CONFIG}`);
|
|
52
|
+
log.plain(` ${Messages.GLOBAL_INIT_EDIT_ENV}`);
|
|
53
|
+
log.blank();
|
|
54
|
+
log.info(Messages.GLOBAL_INIT_REINITIALIZE_TIP);
|
|
55
|
+
log.plain(` ${Messages.GLOBAL_INIT_REINITIALIZE_INSTRUCTION}`);
|
|
56
|
+
log.blank();
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Display the global initialization success message.
|
|
60
|
+
* @param configPath - Path to the created global config file
|
|
61
|
+
*/
|
|
62
|
+
export function displayGlobalInitSuccess(configPath) {
|
|
63
|
+
log.blank();
|
|
64
|
+
log.success(`${Icons.CHECK} ${Messages.GLOBAL_INIT_SUCCESS}`);
|
|
65
|
+
log.blank();
|
|
66
|
+
log.plain(`${Icons.FOLDER} Configuration file: ${configPath}`);
|
|
67
|
+
log.blank();
|
|
68
|
+
log.plain(`${Icons.DOCUMENT} ${Messages.INIT_NEXT_STEPS_HEADER}`);
|
|
69
|
+
log.plain(` 1. Set your API keys in ~/.syrin/.env or as environment variables`);
|
|
70
|
+
log.plain(` 2. Run \`syrin config edit-env --global\` to edit the environment file`);
|
|
71
|
+
log.plain(` 3. ${Messages.INIT_RUN_DOCTOR(ToolCommands.DOCTOR)}`);
|
|
72
|
+
log.blank();
|
|
73
|
+
}
|
|
41
74
|
//# sourceMappingURL=init-ui.js.map
|
|
@@ -7,7 +7,7 @@ import { normalizeTools } from './normalizer.js';
|
|
|
7
7
|
import { buildIndexes } from './indexer.js';
|
|
8
8
|
import { inferDependencies } from './dependencies.js';
|
|
9
9
|
import { ALL_RULES } from './rules/index.js';
|
|
10
|
-
import {
|
|
10
|
+
import { log } from '../../utils/logger.js';
|
|
11
11
|
/**
|
|
12
12
|
* Analyze MCP tools and return diagnostics.
|
|
13
13
|
* Executes all registered rules.
|
|
@@ -32,7 +32,7 @@ function runRules(tools, dependencies, indexes) {
|
|
|
32
32
|
}
|
|
33
33
|
catch (error) {
|
|
34
34
|
// If a rule fails, log error but don't crash the analysis
|
|
35
|
-
|
|
35
|
+
log.error(`Rule ${rule.id} failed`, error instanceof Error ? error : new Error(String(error)), {
|
|
36
36
|
ruleId: rule.id,
|
|
37
37
|
ruleName: rule.ruleName,
|
|
38
38
|
});
|
|
@@ -42,7 +42,7 @@ declare class W104GenericDescriptionRule extends BaseRule {
|
|
|
42
42
|
*
|
|
43
43
|
* @example
|
|
44
44
|
* ```ts
|
|
45
|
-
* import { createW104Rule, DEFAULT_CONCRETE_NOUNS } from './w005-generic-description
|
|
45
|
+
* import { createW104Rule, DEFAULT_CONCRETE_NOUNS } from './w005-generic-description';
|
|
46
46
|
* const customNouns = [...DEFAULT_CONCRETE_NOUNS, 'invoice', 'report'];
|
|
47
47
|
* const customRule = createW104Rule(customNouns);
|
|
48
48
|
* ```
|
|
@@ -96,7 +96,7 @@ class W104GenericDescriptionRule extends BaseRule {
|
|
|
96
96
|
*
|
|
97
97
|
* @example
|
|
98
98
|
* ```ts
|
|
99
|
-
* import { createW104Rule, DEFAULT_CONCRETE_NOUNS } from './w005-generic-description
|
|
99
|
+
* import { createW104Rule, DEFAULT_CONCRETE_NOUNS } from './w005-generic-description';
|
|
100
100
|
* const customNouns = [...DEFAULT_CONCRETE_NOUNS, 'invoice', 'report'];
|
|
101
101
|
* const customRule = createW104Rule(customNouns);
|
|
102
102
|
* ```
|
|
@@ -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
|