@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
|
@@ -3,19 +3,26 @@
|
|
|
3
3
|
* Initializes a new Syrin project with configuration.
|
|
4
4
|
*/
|
|
5
5
|
import { generateConfigFile, isProjectInitialized } from '../../config/generator.js';
|
|
6
|
-
import { promptInitOptions, getDefaultInitOptions, } from '../../cli/prompts/init-prompt.js';
|
|
6
|
+
import { promptInitOptions, getDefaultInitOptions, promptGlobalInitOptions, getDefaultGlobalInitOptions, } from '../../cli/prompts/init-prompt.js';
|
|
7
7
|
import { ConfigurationError } from '../../utils/errors.js';
|
|
8
|
-
import {
|
|
8
|
+
import { log } from '../../utils/logger.js';
|
|
9
9
|
import { Messages, Paths } from '../../constants/index.js';
|
|
10
10
|
import { ensureGitignorePattern } from '../../utils/gitignore.js';
|
|
11
|
-
import { displayAlreadyInitialized, displayInitSuccess, } from '../../presentation/init-ui.js';
|
|
11
|
+
import { displayAlreadyInitialized, displayInitSuccess, displayGlobalAlreadyInitialized, displayGlobalInitSuccess, } from '../../presentation/init-ui.js';
|
|
12
12
|
import { showVersionBanner } from '../../cli/utils/version-banner.js';
|
|
13
|
+
import { globalConfigExists, saveGlobalConfig, getGlobalConfigPath, } from '../../config/global-loader.js';
|
|
14
|
+
import { makeSyrinVersion, makeAPIKey, makeModelName } from '../../types/factories.js';
|
|
13
15
|
/**
|
|
14
16
|
* Execute the init command.
|
|
15
17
|
* @param options - Command options
|
|
16
18
|
*/
|
|
17
19
|
export async function executeInit(options = {}) {
|
|
18
20
|
await showVersionBanner();
|
|
21
|
+
// Handle global init
|
|
22
|
+
if (options.global) {
|
|
23
|
+
await executeGlobalInit(options);
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
19
26
|
const projectRoot = options.projectRoot || process.cwd();
|
|
20
27
|
// Check if project is already initialized
|
|
21
28
|
if (isProjectInitialized(projectRoot)) {
|
|
@@ -26,19 +33,19 @@ export async function executeInit(options = {}) {
|
|
|
26
33
|
let initOptions;
|
|
27
34
|
if (options.yes) {
|
|
28
35
|
// Non-interactive mode: use defaults
|
|
29
|
-
|
|
36
|
+
log.info('Initializing project with default values (non-interactive mode)');
|
|
30
37
|
initOptions = getDefaultInitOptions(projectRoot);
|
|
31
38
|
}
|
|
32
39
|
else {
|
|
33
40
|
// Interactive mode: prompt user
|
|
34
|
-
|
|
41
|
+
log.info('Starting interactive project initialization');
|
|
35
42
|
try {
|
|
36
43
|
initOptions = await promptInitOptions();
|
|
37
44
|
}
|
|
38
45
|
catch (error) {
|
|
39
46
|
if (error instanceof Error && error.name === 'ExitPromptError') {
|
|
40
47
|
// User cancelled
|
|
41
|
-
|
|
48
|
+
log.info('Initialization cancelled by user');
|
|
42
49
|
process.exit(0);
|
|
43
50
|
}
|
|
44
51
|
throw error;
|
|
@@ -47,31 +54,25 @@ export async function executeInit(options = {}) {
|
|
|
47
54
|
try {
|
|
48
55
|
// Generate config file
|
|
49
56
|
const configPath = generateConfigFile(initOptions, projectRoot);
|
|
50
|
-
|
|
51
|
-
configPath,
|
|
52
|
-
projectName: initOptions.projectName,
|
|
53
|
-
transport: initOptions.transport,
|
|
54
|
-
});
|
|
57
|
+
log.info(`Configuration file created successfully: ${configPath}`);
|
|
55
58
|
// Update .gitignore to exclude event files and dev history
|
|
56
59
|
try {
|
|
57
60
|
const eventsPatternAdded = await ensureGitignorePattern(projectRoot, Paths.EVENTS_DIR);
|
|
58
61
|
if (eventsPatternAdded) {
|
|
59
|
-
|
|
62
|
+
log.info(`Added ${Paths.EVENTS_DIR} to .gitignore`);
|
|
60
63
|
}
|
|
61
64
|
const historyPatternAdded = await ensureGitignorePattern(projectRoot, Paths.DEV_HISTORY_FILE);
|
|
62
65
|
if (historyPatternAdded) {
|
|
63
|
-
|
|
66
|
+
log.info('Added .syrin/.dev-history to .gitignore');
|
|
64
67
|
}
|
|
65
68
|
const dataPatternAdded = await ensureGitignorePattern(projectRoot, Paths.DATA_DIR);
|
|
66
69
|
if (dataPatternAdded) {
|
|
67
|
-
|
|
70
|
+
log.info(`Added ${Paths.DATA_DIR} to .gitignore`);
|
|
68
71
|
}
|
|
69
72
|
}
|
|
70
73
|
catch (error) {
|
|
71
74
|
// Log but don't fail initialization if .gitignore update fails
|
|
72
|
-
|
|
73
|
-
error: error instanceof Error ? error.message : String(error),
|
|
74
|
-
});
|
|
75
|
+
log.warn(`Failed to update .gitignore: ${error instanceof Error ? error.message : String(error)}`);
|
|
75
76
|
}
|
|
76
77
|
// Display success message
|
|
77
78
|
displayInitSuccess(configPath);
|
|
@@ -83,7 +84,77 @@ export async function executeInit(options = {}) {
|
|
|
83
84
|
cause: error instanceof Error ? error : new Error(String(error)),
|
|
84
85
|
context: { projectRoot },
|
|
85
86
|
});
|
|
86
|
-
|
|
87
|
+
log.error(`Error: ${configError.message}`);
|
|
88
|
+
throw configError;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Execute global init command (LLM providers only).
|
|
93
|
+
* Creates ~/.syrin/syrin.yaml with LLM configuration.
|
|
94
|
+
* @param options - Command options
|
|
95
|
+
*/
|
|
96
|
+
async function executeGlobalInit(options) {
|
|
97
|
+
// Check if global config already exists
|
|
98
|
+
if (globalConfigExists()) {
|
|
99
|
+
displayGlobalAlreadyInitialized();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
let globalOptions;
|
|
103
|
+
if (options.yes) {
|
|
104
|
+
// Non-interactive mode: use defaults
|
|
105
|
+
log.info('Initializing global config with default values (non-interactive mode)');
|
|
106
|
+
globalOptions = getDefaultGlobalInitOptions();
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
// Interactive mode: prompt user
|
|
110
|
+
log.info('Starting interactive global initialization');
|
|
111
|
+
try {
|
|
112
|
+
globalOptions = await promptGlobalInitOptions();
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
if (error instanceof Error && error.name === 'ExitPromptError') {
|
|
116
|
+
// User cancelled
|
|
117
|
+
log.info('Global initialization cancelled by user');
|
|
118
|
+
process.exit(0);
|
|
119
|
+
}
|
|
120
|
+
throw error;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
try {
|
|
124
|
+
// Build GlobalSyrinConfig
|
|
125
|
+
const llmConfig = {};
|
|
126
|
+
for (const [provider, settings] of Object.entries(globalOptions.llmProviders)) {
|
|
127
|
+
llmConfig[provider] = {
|
|
128
|
+
...('apiKey' in settings && settings.apiKey
|
|
129
|
+
? { API_KEY: makeAPIKey(String(settings.apiKey)) }
|
|
130
|
+
: {}),
|
|
131
|
+
...(settings.modelName
|
|
132
|
+
? { MODEL_NAME: makeModelName(String(settings.modelName)) }
|
|
133
|
+
: {}),
|
|
134
|
+
default: settings.default || false,
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
const globalConfig = {
|
|
138
|
+
version: makeSyrinVersion('1.0'),
|
|
139
|
+
project_name: 'GlobalSyrin',
|
|
140
|
+
agent_name: globalOptions.agentName,
|
|
141
|
+
llm: llmConfig,
|
|
142
|
+
};
|
|
143
|
+
// Save global config
|
|
144
|
+
saveGlobalConfig(globalConfig);
|
|
145
|
+
const configPath = getGlobalConfigPath();
|
|
146
|
+
log.info(`Global configuration file created successfully: ${configPath}`);
|
|
147
|
+
// Display success message
|
|
148
|
+
displayGlobalInitSuccess(configPath);
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
const configError = error instanceof ConfigurationError
|
|
152
|
+
? error
|
|
153
|
+
: new ConfigurationError(Messages.ERROR_GENERATE_CONFIG, {
|
|
154
|
+
cause: error instanceof Error ? error : new Error(String(error)),
|
|
155
|
+
context: { type: 'global' },
|
|
156
|
+
});
|
|
157
|
+
log.error(`Error: ${configError.message}`);
|
|
87
158
|
throw configError;
|
|
88
159
|
}
|
|
89
160
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `syrin status` command implementation.
|
|
3
|
+
* Shows a quick overview of project health (like `git status`).
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Execute the status command.
|
|
7
|
+
* @param projectRoot - Project root directory (defaults to current working directory)
|
|
8
|
+
*/
|
|
9
|
+
export declare function executeStatus(projectRoot?: string): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=status.d.ts.map
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `syrin status` command implementation.
|
|
3
|
+
* Shows a quick overview of project health (like `git status`).
|
|
4
|
+
*/
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import { loadConfigOptional } from '../../config/loader.js';
|
|
8
|
+
import { loadGlobalConfig, getGlobalConfigPath } from '../../config/global-loader.js';
|
|
9
|
+
import { checkEnvVar } from '../../config/env-checker.js';
|
|
10
|
+
import { handleCommandError } from '../../cli/utils/index.js';
|
|
11
|
+
import { showVersionBanner } from '../../cli/utils/version-banner.js';
|
|
12
|
+
import { log } from '../../utils/logger.js';
|
|
13
|
+
import { Paths, LLMProviders, Icons } from '../../constants/index.js';
|
|
14
|
+
/**
|
|
15
|
+
* Generate status report.
|
|
16
|
+
*/
|
|
17
|
+
function generateStatusReport(projectRoot) {
|
|
18
|
+
const localConfig = loadConfigOptional(projectRoot);
|
|
19
|
+
const globalConfig = loadGlobalConfig();
|
|
20
|
+
const localEnvPath = path.join(projectRoot, Paths.ENV_FILE);
|
|
21
|
+
const hasLocalEnv = fs.existsSync(localEnvPath);
|
|
22
|
+
const report = {
|
|
23
|
+
hasLocalConfig: !!localConfig,
|
|
24
|
+
hasGlobalConfig: !!globalConfig,
|
|
25
|
+
llmProviders: [],
|
|
26
|
+
hasEnvFile: hasLocalEnv,
|
|
27
|
+
envFilePath: hasLocalEnv ? localEnvPath : undefined,
|
|
28
|
+
};
|
|
29
|
+
const config = localConfig || globalConfig;
|
|
30
|
+
if (!config) {
|
|
31
|
+
return report;
|
|
32
|
+
}
|
|
33
|
+
if (localConfig) {
|
|
34
|
+
report.configPath = path.join(projectRoot, Paths.CONFIG_FILE);
|
|
35
|
+
report.projectName = String(localConfig.project_name);
|
|
36
|
+
report.transport = String(localConfig.transport);
|
|
37
|
+
report.mcpUrl = localConfig.mcp_url
|
|
38
|
+
? String(localConfig.mcp_url)
|
|
39
|
+
: undefined;
|
|
40
|
+
report.script = localConfig.script ? String(localConfig.script) : undefined;
|
|
41
|
+
}
|
|
42
|
+
else if (globalConfig) {
|
|
43
|
+
report.configPath = getGlobalConfigPath();
|
|
44
|
+
}
|
|
45
|
+
// Check LLM providers
|
|
46
|
+
for (const [providerName, providerConfig] of Object.entries(config.llm)) {
|
|
47
|
+
let isConfigured = false;
|
|
48
|
+
if (providerName === LLMProviders.OLLAMA) {
|
|
49
|
+
// Ollama only needs MODEL_NAME
|
|
50
|
+
if (providerConfig.MODEL_NAME) {
|
|
51
|
+
const modelCheck = checkEnvVar(String(providerConfig.MODEL_NAME), projectRoot);
|
|
52
|
+
isConfigured = modelCheck.isSet;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// Cloud providers need API_KEY
|
|
57
|
+
if (providerConfig.API_KEY) {
|
|
58
|
+
const apiKeyCheck = checkEnvVar(String(providerConfig.API_KEY), projectRoot);
|
|
59
|
+
isConfigured = apiKeyCheck.isSet;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
report.llmProviders.push({
|
|
63
|
+
name: providerName,
|
|
64
|
+
isDefault: providerConfig.default === true,
|
|
65
|
+
isConfigured,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return report;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Display status report.
|
|
72
|
+
*/
|
|
73
|
+
function displayStatusReport(report) {
|
|
74
|
+
log.blank();
|
|
75
|
+
// Configuration Status
|
|
76
|
+
log.heading('Configuration');
|
|
77
|
+
log.plain('─'.repeat(40));
|
|
78
|
+
if (report.hasLocalConfig) {
|
|
79
|
+
log.plain(` ${Icons.SUCCESS} Local config: ${report.configPath}`);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
log.plain(` ${Icons.FAILURE} Local config: Not found`);
|
|
83
|
+
}
|
|
84
|
+
if (report.hasGlobalConfig) {
|
|
85
|
+
const globalPath = getGlobalConfigPath();
|
|
86
|
+
log.plain(` ${Icons.SUCCESS} Global config: ${globalPath}`);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
log.plain(` ${log.styleText('○', 'dim')} Global config: Not configured`);
|
|
90
|
+
}
|
|
91
|
+
log.blank();
|
|
92
|
+
// Project Info (if local config exists)
|
|
93
|
+
if (report.hasLocalConfig && report.projectName) {
|
|
94
|
+
log.heading('Project');
|
|
95
|
+
log.plain('─'.repeat(40));
|
|
96
|
+
log.plain(` Name: ${report.projectName}`);
|
|
97
|
+
log.plain(` Transport: ${report.transport}`);
|
|
98
|
+
if (report.mcpUrl) {
|
|
99
|
+
log.plain(` MCP URL: ${report.mcpUrl}`);
|
|
100
|
+
}
|
|
101
|
+
if (report.script) {
|
|
102
|
+
log.plain(` Script: ${report.script}`);
|
|
103
|
+
}
|
|
104
|
+
log.blank();
|
|
105
|
+
}
|
|
106
|
+
// LLM Providers
|
|
107
|
+
if (report.llmProviders.length > 0) {
|
|
108
|
+
log.heading('LLM Providers');
|
|
109
|
+
log.plain('─'.repeat(40));
|
|
110
|
+
for (const provider of report.llmProviders) {
|
|
111
|
+
const icon = provider.isConfigured ? Icons.SUCCESS : Icons.FAILURE;
|
|
112
|
+
const defaultLabel = provider.isDefault
|
|
113
|
+
? log.styleText(' (default)', 'cyan')
|
|
114
|
+
: '';
|
|
115
|
+
const status = provider.isConfigured
|
|
116
|
+
? log.styleText('configured', 'green')
|
|
117
|
+
: log.styleText('not configured', 'red');
|
|
118
|
+
log.plain(` ${icon} ${provider.name}${defaultLabel}: ${status}`);
|
|
119
|
+
}
|
|
120
|
+
log.blank();
|
|
121
|
+
}
|
|
122
|
+
// Environment
|
|
123
|
+
log.heading('Environment');
|
|
124
|
+
log.plain('─'.repeat(40));
|
|
125
|
+
if (report.hasEnvFile) {
|
|
126
|
+
log.plain(` ${Icons.SUCCESS} .env file: ${report.envFilePath}`);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
log.plain(` ${log.styleText('○', 'dim')} .env file: Not found (optional)`);
|
|
130
|
+
}
|
|
131
|
+
log.blank();
|
|
132
|
+
// Quick Actions
|
|
133
|
+
const hasIssues = !report.hasLocalConfig && !report.hasGlobalConfig;
|
|
134
|
+
const hasUnconfiguredProviders = report.llmProviders.some(p => !p.isConfigured);
|
|
135
|
+
if (hasIssues || hasUnconfiguredProviders) {
|
|
136
|
+
log.heading('Suggested Actions');
|
|
137
|
+
log.plain('─'.repeat(40));
|
|
138
|
+
if (!report.hasLocalConfig && !report.hasGlobalConfig) {
|
|
139
|
+
log.plain(` ${Icons.TIP} Run ${log.styleText('syrin init', 'cyan')} to initialize a project`);
|
|
140
|
+
}
|
|
141
|
+
if (hasUnconfiguredProviders) {
|
|
142
|
+
log.plain(` ${Icons.TIP} Run ${log.styleText('syrin doctor', 'cyan')} for detailed diagnostics`);
|
|
143
|
+
log.plain(` ${Icons.TIP} Run ${log.styleText('syrin config edit-env', 'cyan')} to set API keys`);
|
|
144
|
+
}
|
|
145
|
+
log.blank();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Execute the status command.
|
|
150
|
+
* @param projectRoot - Project root directory (defaults to current working directory)
|
|
151
|
+
*/
|
|
152
|
+
export async function executeStatus(projectRoot = process.cwd()) {
|
|
153
|
+
await showVersionBanner();
|
|
154
|
+
try {
|
|
155
|
+
const report = generateStatusReport(projectRoot);
|
|
156
|
+
displayStatusReport(report);
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
handleCommandError(error);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=status.js.map
|
package/dist/cli/index.js
CHANGED
|
@@ -11,7 +11,9 @@ import { executeAnalyse } from '../cli/commands/analyse.js';
|
|
|
11
11
|
import { executeDev } from '../cli/commands/dev.js';
|
|
12
12
|
import { executeUpdate } from '../cli/commands/update.js';
|
|
13
13
|
import { executeRollback } from '../cli/commands/rollback.js';
|
|
14
|
-
import {
|
|
14
|
+
import { executeStatus } from '../cli/commands/status.js';
|
|
15
|
+
import { executeConfigSet, executeConfigGet, executeConfigList, executeConfigShow, executeConfigEdit, executeConfigEditEnv, executeConfigSetDefault, executeConfigRemove, } from '../cli/commands/config.js';
|
|
16
|
+
import { log, setQuietMode, setVerboseMode } from '../utils/logger.js';
|
|
15
17
|
import { Messages, ListTypes } from '../constants/index.js';
|
|
16
18
|
import { getCurrentVersion } from '../utils/version-checker.js';
|
|
17
19
|
/**
|
|
@@ -19,7 +21,6 @@ import { getCurrentVersion } from '../utils/version-checker.js';
|
|
|
19
21
|
* Logs full error to internal logger and concise message to user.
|
|
20
22
|
*/
|
|
21
23
|
function reportAnalyseError(error) {
|
|
22
|
-
logger.error('Analyse command failed', error);
|
|
23
24
|
log.error(`Analyse command failed: ${error.message}`);
|
|
24
25
|
}
|
|
25
26
|
const program = new Command();
|
|
@@ -32,29 +33,46 @@ export function setupCLI() {
|
|
|
32
33
|
program
|
|
33
34
|
.name('syrin')
|
|
34
35
|
.description('Syrin - Runtime intelligence system for MCP servers')
|
|
35
|
-
.version(currentVersion, '-v, --version', 'Display version number')
|
|
36
|
+
.version(currentVersion, '-v, --version', 'Display version number')
|
|
37
|
+
.option('-q, --quiet', 'Minimal output (errors only)')
|
|
38
|
+
.option('--verbose', 'Verbose output for debugging')
|
|
39
|
+
.hook('preAction', (_thisCommand, actionCommand) => {
|
|
40
|
+
const opts = actionCommand.optsWithGlobals();
|
|
41
|
+
if (opts.quiet) {
|
|
42
|
+
setQuietMode(true);
|
|
43
|
+
}
|
|
44
|
+
if (opts.verbose) {
|
|
45
|
+
setVerboseMode(true);
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
.addHelpText('after', '\nAliases:\n' +
|
|
49
|
+
' ls Alias for list\n' +
|
|
50
|
+
' doc Alias for doctor\n' +
|
|
51
|
+
' cfg Alias for config');
|
|
36
52
|
// init command
|
|
37
53
|
program
|
|
38
54
|
.command('init')
|
|
39
55
|
.description('Initialize a new Syrin project')
|
|
40
56
|
.option('-y, --yes', 'Skip interactive prompts and use default values')
|
|
41
57
|
.option('--project-root <path>', 'Project root directory (defaults to current directory)')
|
|
58
|
+
.option('--global', 'Create global configuration (LLM providers only)')
|
|
42
59
|
.action(async (options) => {
|
|
43
60
|
try {
|
|
44
61
|
await executeInit({
|
|
45
62
|
yes: options.yes || false,
|
|
46
63
|
projectRoot: options.projectRoot,
|
|
64
|
+
global: options.global || false,
|
|
47
65
|
});
|
|
48
66
|
}
|
|
49
67
|
catch (error) {
|
|
50
68
|
// Handle ALREADY_INITIALIZED case first with early return
|
|
51
|
-
if (error instanceof Error &&
|
|
69
|
+
if (error instanceof Error &&
|
|
70
|
+
error.message === 'ALREADY_INITIALIZED') {
|
|
52
71
|
// Already handled in executeInit, just exit
|
|
53
72
|
return;
|
|
54
73
|
}
|
|
55
74
|
// Handle other Error cases
|
|
56
75
|
if (error instanceof Error) {
|
|
57
|
-
logger.error('Init command failed', error);
|
|
58
76
|
log.blank();
|
|
59
77
|
log.error(`Error: ${error.message}`);
|
|
60
78
|
log.blank();
|
|
@@ -67,6 +85,7 @@ export function setupCLI() {
|
|
|
67
85
|
// doctor command
|
|
68
86
|
program
|
|
69
87
|
.command('doctor')
|
|
88
|
+
.alias('doc')
|
|
70
89
|
.description('Validate Syrin project configuration and setup')
|
|
71
90
|
.option('--project-root <path>', 'Project root directory (defaults to current directory)')
|
|
72
91
|
.action(async (options) => {
|
|
@@ -76,8 +95,9 @@ export function setupCLI() {
|
|
|
76
95
|
catch (error) {
|
|
77
96
|
// Error handling is done in executeDoctor, this is a safety net
|
|
78
97
|
if (error instanceof Error) {
|
|
79
|
-
|
|
98
|
+
log.error(`Error: ${error.message}`);
|
|
80
99
|
}
|
|
100
|
+
process.exit(1);
|
|
81
101
|
}
|
|
82
102
|
});
|
|
83
103
|
// test command
|
|
@@ -171,13 +191,15 @@ export function setupCLI() {
|
|
|
171
191
|
catch (error) {
|
|
172
192
|
// Error handling is done in executeTest
|
|
173
193
|
if (error instanceof Error) {
|
|
174
|
-
|
|
194
|
+
log.error(`Error: ${error.message}`);
|
|
195
|
+
process.exit(1);
|
|
175
196
|
}
|
|
176
197
|
}
|
|
177
198
|
});
|
|
178
199
|
// list command
|
|
179
200
|
program
|
|
180
201
|
.command('list')
|
|
202
|
+
.alias('ls')
|
|
181
203
|
.description('List tools, resources, or prompts from an MCP server')
|
|
182
204
|
.argument('[type]', `Type to list: ${ListTypes.TOOLS}, ${ListTypes.RESOURCES}, or ${ListTypes.PROMPTS} (default: ${ListTypes.TOOLS})`, ListTypes.TOOLS)
|
|
183
205
|
.option('--transport <type>', 'Transport type (http or stdio). If not provided, uses transport from config.yaml')
|
|
@@ -216,7 +238,8 @@ export function setupCLI() {
|
|
|
216
238
|
catch (error) {
|
|
217
239
|
// Error handling is done in executeList
|
|
218
240
|
if (error instanceof Error) {
|
|
219
|
-
|
|
241
|
+
log.error(`Error: ${error.message}`);
|
|
242
|
+
process.exit(1);
|
|
220
243
|
}
|
|
221
244
|
}
|
|
222
245
|
});
|
|
@@ -273,6 +296,9 @@ export function setupCLI() {
|
|
|
273
296
|
.option('--save-events', 'Save events to file for debugging')
|
|
274
297
|
.option('--event-file <path>', 'Directory path for event files (default: .syrin/events). Events are saved as {sessionId}.jsonl')
|
|
275
298
|
.option('--run-script', 'Run script to spawn server internally. If not provided, stdio uses script automatically, http connects to existing server')
|
|
299
|
+
.option('--transport <type>', 'Transport type (stdio or http). Required when using global config.')
|
|
300
|
+
.option('--url <url>', 'MCP server URL (required for http transport when using global config)')
|
|
301
|
+
.option('--script <command>', 'Script command to run MCP server (required for stdio transport when using global config)')
|
|
276
302
|
.action(async (options) => {
|
|
277
303
|
try {
|
|
278
304
|
await executeDev({
|
|
@@ -282,12 +308,16 @@ export function setupCLI() {
|
|
|
282
308
|
saveEvents: options.saveEvents || false,
|
|
283
309
|
eventFile: options.eventFile,
|
|
284
310
|
runScript: options.runScript || false,
|
|
311
|
+
transport: options.transport,
|
|
312
|
+
url: options.url,
|
|
313
|
+
script: options.script,
|
|
285
314
|
});
|
|
286
315
|
}
|
|
287
316
|
catch (error) {
|
|
288
317
|
// Error handling is done in executeDev
|
|
289
318
|
if (error instanceof Error) {
|
|
290
|
-
|
|
319
|
+
log.error(`Error: ${error.message}`);
|
|
320
|
+
process.exit(1);
|
|
291
321
|
}
|
|
292
322
|
}
|
|
293
323
|
});
|
|
@@ -302,7 +332,8 @@ export function setupCLI() {
|
|
|
302
332
|
catch (error) {
|
|
303
333
|
// Error handling is done in executeUpdate
|
|
304
334
|
if (error instanceof Error) {
|
|
305
|
-
|
|
335
|
+
log.error(`Error: ${error.message}`);
|
|
336
|
+
process.exit(1);
|
|
306
337
|
}
|
|
307
338
|
}
|
|
308
339
|
});
|
|
@@ -318,10 +349,178 @@ export function setupCLI() {
|
|
|
318
349
|
catch (error) {
|
|
319
350
|
// Error handling is done in executeRollback
|
|
320
351
|
if (error instanceof Error) {
|
|
321
|
-
|
|
352
|
+
log.error(`Error: ${error.message}`);
|
|
353
|
+
process.exit(1);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
// status command
|
|
358
|
+
program
|
|
359
|
+
.command('status')
|
|
360
|
+
.description('Show project health overview (config, LLM, MCP connection)')
|
|
361
|
+
.option('--project-root <path>', 'Project root directory (defaults to current directory)')
|
|
362
|
+
.action(async (options) => {
|
|
363
|
+
try {
|
|
364
|
+
await executeStatus(options.projectRoot);
|
|
365
|
+
}
|
|
366
|
+
catch (error) {
|
|
367
|
+
if (error instanceof Error) {
|
|
368
|
+
log.error(`Error: ${error.message}`);
|
|
369
|
+
process.exit(1);
|
|
322
370
|
}
|
|
323
371
|
}
|
|
324
372
|
});
|
|
373
|
+
// config command
|
|
374
|
+
const configCommand = program
|
|
375
|
+
.command('config')
|
|
376
|
+
.alias('cfg')
|
|
377
|
+
.description('Manage Syrin configuration (local or global)');
|
|
378
|
+
// config set
|
|
379
|
+
configCommand
|
|
380
|
+
.command('set')
|
|
381
|
+
.description('Set a configuration value')
|
|
382
|
+
.argument('<key>', 'Configuration key (e.g., "openai.model", "agent_name")')
|
|
383
|
+
.argument('<value>', 'Value to set')
|
|
384
|
+
.option('--global', 'Operate on global config')
|
|
385
|
+
.option('--local', 'Operate on local config')
|
|
386
|
+
.action(async (key, value, options) => {
|
|
387
|
+
try {
|
|
388
|
+
await executeConfigSet(key, value, options);
|
|
389
|
+
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
if (error instanceof Error) {
|
|
392
|
+
log.error(`Error: ${error.message}`);
|
|
393
|
+
}
|
|
394
|
+
process.exit(1);
|
|
395
|
+
}
|
|
396
|
+
});
|
|
397
|
+
// config get
|
|
398
|
+
configCommand
|
|
399
|
+
.command('get')
|
|
400
|
+
.description('Get a configuration value')
|
|
401
|
+
.argument('<key>', 'Configuration key')
|
|
402
|
+
.option('--global', 'Operate on global config')
|
|
403
|
+
.option('--local', 'Operate on local config')
|
|
404
|
+
.action(async (key, options) => {
|
|
405
|
+
try {
|
|
406
|
+
await executeConfigGet(key, options);
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
if (error instanceof Error) {
|
|
410
|
+
log.error(`Error: ${error.message}`);
|
|
411
|
+
}
|
|
412
|
+
process.exit(1);
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
// config list
|
|
416
|
+
configCommand
|
|
417
|
+
.command('list')
|
|
418
|
+
.description('List all configuration values')
|
|
419
|
+
.option('--global', 'Operate on global config')
|
|
420
|
+
.option('--local', 'Operate on local config')
|
|
421
|
+
.action(async (options) => {
|
|
422
|
+
try {
|
|
423
|
+
await executeConfigList(options);
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
if (error instanceof Error) {
|
|
427
|
+
log.error(`Error: ${error.message}`);
|
|
428
|
+
}
|
|
429
|
+
process.exit(1);
|
|
430
|
+
}
|
|
431
|
+
});
|
|
432
|
+
// config show
|
|
433
|
+
configCommand
|
|
434
|
+
.command('show')
|
|
435
|
+
.description('Show full configuration')
|
|
436
|
+
.option('--global', 'Operate on global config')
|
|
437
|
+
.option('--local', 'Operate on local config')
|
|
438
|
+
.action(async (options) => {
|
|
439
|
+
try {
|
|
440
|
+
await executeConfigShow(options);
|
|
441
|
+
}
|
|
442
|
+
catch (error) {
|
|
443
|
+
if (error instanceof Error) {
|
|
444
|
+
log.error(`Error: ${error.message}`);
|
|
445
|
+
}
|
|
446
|
+
process.exit(1);
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
// config edit
|
|
450
|
+
configCommand
|
|
451
|
+
.command('edit')
|
|
452
|
+
.description('Edit configuration file in editor')
|
|
453
|
+
.option('--global', 'Operate on global config')
|
|
454
|
+
.option('--local', 'Operate on local config')
|
|
455
|
+
.action(async (options) => {
|
|
456
|
+
try {
|
|
457
|
+
await executeConfigEdit(options);
|
|
458
|
+
}
|
|
459
|
+
catch (error) {
|
|
460
|
+
if (error instanceof Error) {
|
|
461
|
+
log.error(`Error: ${error.message}`);
|
|
462
|
+
}
|
|
463
|
+
process.exit(1);
|
|
464
|
+
}
|
|
465
|
+
});
|
|
466
|
+
// config edit-env
|
|
467
|
+
configCommand
|
|
468
|
+
.command('edit-env')
|
|
469
|
+
.description('Edit environment file in editor')
|
|
470
|
+
.option('--global', 'Operate on global config')
|
|
471
|
+
.option('--local', 'Operate on local config')
|
|
472
|
+
.action(async (options) => {
|
|
473
|
+
try {
|
|
474
|
+
await executeConfigEditEnv(options);
|
|
475
|
+
}
|
|
476
|
+
catch (error) {
|
|
477
|
+
if (error instanceof Error) {
|
|
478
|
+
log.error(`Error: ${error.message}`);
|
|
479
|
+
}
|
|
480
|
+
process.exit(1);
|
|
481
|
+
}
|
|
482
|
+
});
|
|
483
|
+
// config set-default
|
|
484
|
+
configCommand
|
|
485
|
+
.command('set-default')
|
|
486
|
+
.description('Set default LLM provider')
|
|
487
|
+
.argument('<provider>', 'LLM provider name (e.g., openai, claude)')
|
|
488
|
+
.option('--global', 'Operate on global config')
|
|
489
|
+
.option('--local', 'Operate on local config')
|
|
490
|
+
.action(async (provider, options) => {
|
|
491
|
+
try {
|
|
492
|
+
await executeConfigSetDefault(provider, options);
|
|
493
|
+
}
|
|
494
|
+
catch (error) {
|
|
495
|
+
if (error instanceof Error) {
|
|
496
|
+
log.error(`Error: ${error.message}`);
|
|
497
|
+
}
|
|
498
|
+
process.exit(1);
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
// config remove
|
|
502
|
+
configCommand
|
|
503
|
+
.command('remove')
|
|
504
|
+
.description('Remove an LLM provider from configuration')
|
|
505
|
+
.argument('<provider>', 'LLM provider name to remove')
|
|
506
|
+
.option('--global', 'Operate on global config')
|
|
507
|
+
.option('--local', 'Operate on local config')
|
|
508
|
+
.action(async (provider, options) => {
|
|
509
|
+
try {
|
|
510
|
+
await executeConfigRemove(provider, options);
|
|
511
|
+
}
|
|
512
|
+
catch (error) {
|
|
513
|
+
if (error instanceof Error) {
|
|
514
|
+
log.error(`Error: ${error.message}`);
|
|
515
|
+
}
|
|
516
|
+
process.exit(1);
|
|
517
|
+
}
|
|
518
|
+
});
|
|
519
|
+
// Handle no command provided - show help and exit with 0
|
|
520
|
+
if (process.argv.length <= 2) {
|
|
521
|
+
program.outputHelp();
|
|
522
|
+
process.exit(0);
|
|
523
|
+
}
|
|
325
524
|
// Parse command line arguments
|
|
326
525
|
program.parse();
|
|
327
526
|
}
|
|
@@ -334,7 +533,7 @@ export function run() {
|
|
|
334
533
|
}
|
|
335
534
|
catch (error) {
|
|
336
535
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
337
|
-
|
|
536
|
+
log.error(`Error: ${err.message}`);
|
|
338
537
|
log.error(Messages.CLI_START_FAILED);
|
|
339
538
|
process.exit(1);
|
|
340
539
|
}
|