@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
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Interactive prompts for the `syrin init` command.
|
|
3
3
|
*/
|
|
4
4
|
import type { InitOptions } from '../../config/types.js';
|
|
5
|
+
import { makeAgentName } from '../../types/factories.js';
|
|
5
6
|
/**
|
|
6
7
|
* Prompt user for project initialization details.
|
|
7
8
|
* @param defaults - Default values for non-interactive mode
|
|
@@ -14,4 +15,21 @@ export declare function promptInitOptions(defaults?: Partial<InitOptions>): Prom
|
|
|
14
15
|
* @returns Default options
|
|
15
16
|
*/
|
|
16
17
|
export declare function getDefaultInitOptions(projectRoot: string): InitOptions;
|
|
18
|
+
/**
|
|
19
|
+
* Global init options (LLM providers only).
|
|
20
|
+
*/
|
|
21
|
+
export interface GlobalInitOptions {
|
|
22
|
+
agentName: ReturnType<typeof makeAgentName>;
|
|
23
|
+
llmProviders: InitOptions['llmProviders'];
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Prompt user for global initialization details (LLM providers only).
|
|
27
|
+
* @returns User's answers for global config
|
|
28
|
+
*/
|
|
29
|
+
export declare function promptGlobalInitOptions(): Promise<GlobalInitOptions>;
|
|
30
|
+
/**
|
|
31
|
+
* Create default global init options for non-interactive mode.
|
|
32
|
+
* @returns Default options for global config
|
|
33
|
+
*/
|
|
34
|
+
export declare function getDefaultGlobalInitOptions(): GlobalInitOptions;
|
|
17
35
|
//# sourceMappingURL=init-prompt.d.ts.map
|
|
@@ -6,116 +6,33 @@ import * as path from 'path';
|
|
|
6
6
|
import { makeProjectName, makeAgentName, makeMCPURL, makeAPIKey, makeModelName, makeScriptCommand, } from '../../types/factories.js';
|
|
7
7
|
import { Messages, TransportTypes, LLMProviders, LLMProviderDisplayNames, Defaults, EnvVars, } from '../../constants/index.js';
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
* @param
|
|
11
|
-
* @returns
|
|
9
|
+
* Get LLM provider prompts (shared between init and global init).
|
|
10
|
+
* @param defaultProviders - Default providers to check (for non-interactive mode)
|
|
11
|
+
* @returns Array of inquirer questions for LLM provider configuration
|
|
12
12
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const defaultProjectName = defaults?.projectName ||
|
|
16
|
-
currentDirName.replace(/[^a-z0-9-]/gi, '-').toLowerCase();
|
|
17
|
-
const answers = await inquirer.prompt([
|
|
18
|
-
{
|
|
19
|
-
type: 'input',
|
|
20
|
-
name: 'projectName',
|
|
21
|
-
message: 'Project name:',
|
|
22
|
-
default: defaultProjectName,
|
|
23
|
-
validate: (input) => {
|
|
24
|
-
if (!input.trim()) {
|
|
25
|
-
return Messages.PROMPT_PROJECT_NAME_REQUIRED;
|
|
26
|
-
}
|
|
27
|
-
if (!/^[a-z0-9-]+$/i.test(input)) {
|
|
28
|
-
return Messages.PROMPT_PROJECT_NAME_INVALID;
|
|
29
|
-
}
|
|
30
|
-
return true;
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
type: 'input',
|
|
35
|
-
name: 'agentName',
|
|
36
|
-
message: 'Agent name:',
|
|
37
|
-
default: defaults?.agentName
|
|
38
|
-
? String(defaults.agentName)
|
|
39
|
-
: Defaults.AGENT_NAME,
|
|
40
|
-
validate: (input) => {
|
|
41
|
-
if (!input.trim()) {
|
|
42
|
-
return Messages.PROMPT_AGENT_NAME_REQUIRED;
|
|
43
|
-
}
|
|
44
|
-
return true;
|
|
45
|
-
},
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
type: 'list',
|
|
49
|
-
name: 'transport',
|
|
50
|
-
message: 'Transport type:',
|
|
51
|
-
choices: [
|
|
52
|
-
{
|
|
53
|
-
name: `${TransportTypes.STDIO} - Spawn MCP server as a subprocess`,
|
|
54
|
-
value: TransportTypes.STDIO,
|
|
55
|
-
},
|
|
56
|
-
{
|
|
57
|
-
name: `${TransportTypes.HTTP} - Connect to a running MCP server via HTTP`,
|
|
58
|
-
value: TransportTypes.HTTP,
|
|
59
|
-
},
|
|
60
|
-
],
|
|
61
|
-
default: defaults?.transport || Defaults.TRANSPORT,
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
type: 'input',
|
|
65
|
-
name: 'mcpUrl',
|
|
66
|
-
message: 'MCP server URL:',
|
|
67
|
-
default: defaults?.mcpUrl ? String(defaults.mcpUrl) : Defaults.MCP_URL,
|
|
68
|
-
when: (answers) => answers?.transport === TransportTypes.HTTP,
|
|
69
|
-
validate: (input, answers) => {
|
|
70
|
-
if (answers?.transport === TransportTypes.HTTP) {
|
|
71
|
-
if (!input.trim()) {
|
|
72
|
-
return Messages.PROMPT_MCP_URL_REQUIRED;
|
|
73
|
-
}
|
|
74
|
-
try {
|
|
75
|
-
new URL(input);
|
|
76
|
-
return true;
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
return Messages.PROMPT_URL_INVALID;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return true;
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
type: 'input',
|
|
87
|
-
name: 'script',
|
|
88
|
-
message: 'Script command to run MCP server:',
|
|
89
|
-
default: defaults?.script ? String(defaults.script) : 'python3 server.py',
|
|
90
|
-
validate: (input) => {
|
|
91
|
-
if (!input.trim()) {
|
|
92
|
-
return Messages.PROMPT_SCRIPT_REQUIRED;
|
|
93
|
-
}
|
|
94
|
-
return true;
|
|
95
|
-
},
|
|
96
|
-
},
|
|
13
|
+
function getLLMProviderPrompts(defaultProviders) {
|
|
14
|
+
return [
|
|
97
15
|
{
|
|
98
16
|
type: 'checkbox',
|
|
99
17
|
name: 'llmProviders',
|
|
100
|
-
message: 'Select LLM providers to configure:',
|
|
18
|
+
message: 'Select LLM providers to configure (SPACE to toggle, ENTER to confirm):',
|
|
101
19
|
choices: [
|
|
102
20
|
{
|
|
103
21
|
name: LLMProviderDisplayNames.OPENAI,
|
|
104
22
|
value: LLMProviders.OPENAI,
|
|
105
|
-
checked: true,
|
|
23
|
+
checked: defaultProviders?.includes(LLMProviders.OPENAI) ?? true,
|
|
106
24
|
},
|
|
107
25
|
{
|
|
108
26
|
name: LLMProviderDisplayNames.CLAUDE,
|
|
109
27
|
value: LLMProviders.CLAUDE,
|
|
28
|
+
checked: defaultProviders?.includes(LLMProviders.CLAUDE) ?? false,
|
|
110
29
|
},
|
|
111
30
|
{
|
|
112
31
|
name: LLMProviderDisplayNames.OLLAMA,
|
|
113
32
|
value: LLMProviders.OLLAMA,
|
|
33
|
+
checked: defaultProviders?.includes(LLMProviders.OLLAMA) ?? false,
|
|
114
34
|
},
|
|
115
35
|
],
|
|
116
|
-
default: defaults?.llmProviders
|
|
117
|
-
? Object.keys(defaults.llmProviders)
|
|
118
|
-
: [LLMProviders.OPENAI],
|
|
119
36
|
validate: (input) => {
|
|
120
37
|
if (input.length === 0) {
|
|
121
38
|
return Messages.PROMPT_LLM_PROVIDER_REQUIRED;
|
|
@@ -198,23 +115,29 @@ export async function promptInitOptions(defaults) {
|
|
|
198
115
|
name: 'defaultProvider',
|
|
199
116
|
message: 'Default LLM provider:',
|
|
200
117
|
choices: (answers) => answers.llmProviders || [],
|
|
201
|
-
default: (answers) => answers.llmProviders?.[0]
|
|
118
|
+
default: (answers) => answers.llmProviders?.[0],
|
|
202
119
|
},
|
|
203
|
-
]
|
|
204
|
-
|
|
120
|
+
];
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Build LLM providers config from answers (shared between init and global init).
|
|
124
|
+
* @param answers - User answers containing LLM provider selections
|
|
125
|
+
* @returns LLM providers configuration object
|
|
126
|
+
*/
|
|
127
|
+
function buildLLMProvidersConfig(answers) {
|
|
205
128
|
const llmProviders = {};
|
|
206
129
|
if (answers.llmProviders.includes('openai')) {
|
|
207
130
|
llmProviders.openai = {
|
|
208
131
|
apiKey: makeAPIKey(answers.openaiApiKey || ''),
|
|
209
132
|
modelName: makeModelName(answers.openaiModel || ''),
|
|
210
|
-
default: answers.defaultProvider === 'openai'
|
|
133
|
+
default: answers.defaultProvider === 'openai',
|
|
211
134
|
};
|
|
212
135
|
}
|
|
213
136
|
if (answers.llmProviders.includes('claude')) {
|
|
214
137
|
llmProviders.claude = {
|
|
215
138
|
apiKey: makeAPIKey(answers.claudeApiKey || ''),
|
|
216
139
|
modelName: makeModelName(answers.claudeModel || ''),
|
|
217
|
-
default: answers.defaultProvider === 'claude'
|
|
140
|
+
default: answers.defaultProvider === 'claude',
|
|
218
141
|
};
|
|
219
142
|
}
|
|
220
143
|
if (answers.llmProviders.includes('ollama')) {
|
|
@@ -222,16 +145,112 @@ export async function promptInitOptions(defaults) {
|
|
|
222
145
|
modelName: answers.ollamaModelName
|
|
223
146
|
? makeModelName(answers.ollamaModelName)
|
|
224
147
|
: undefined,
|
|
225
|
-
default: answers.defaultProvider === 'ollama'
|
|
148
|
+
default: answers.defaultProvider === 'ollama',
|
|
226
149
|
};
|
|
227
150
|
}
|
|
151
|
+
return llmProviders;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Prompt user for project initialization details.
|
|
155
|
+
* @param defaults - Default values for non-interactive mode
|
|
156
|
+
* @returns User's answers
|
|
157
|
+
*/
|
|
158
|
+
export async function promptInitOptions(defaults) {
|
|
159
|
+
const currentDirName = path.basename(process.cwd());
|
|
160
|
+
const defaultProjectName = defaults?.projectName ||
|
|
161
|
+
currentDirName.replace(/[^a-z0-9-]/gi, '-').toLowerCase();
|
|
162
|
+
const projectPrompts = [
|
|
163
|
+
{
|
|
164
|
+
type: 'input',
|
|
165
|
+
name: 'projectName',
|
|
166
|
+
message: 'Project name:',
|
|
167
|
+
default: defaultProjectName,
|
|
168
|
+
validate: (input) => {
|
|
169
|
+
if (!input.trim()) {
|
|
170
|
+
return Messages.PROMPT_PROJECT_NAME_REQUIRED;
|
|
171
|
+
}
|
|
172
|
+
if (!/^[a-z0-9-]+$/i.test(input)) {
|
|
173
|
+
return Messages.PROMPT_PROJECT_NAME_INVALID;
|
|
174
|
+
}
|
|
175
|
+
return true;
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
type: 'input',
|
|
180
|
+
name: 'agentName',
|
|
181
|
+
message: 'Agent name:',
|
|
182
|
+
default: defaults?.agentName
|
|
183
|
+
? String(defaults.agentName)
|
|
184
|
+
: Defaults.AGENT_NAME,
|
|
185
|
+
validate: (input) => {
|
|
186
|
+
if (!input.trim()) {
|
|
187
|
+
return Messages.PROMPT_AGENT_NAME_REQUIRED;
|
|
188
|
+
}
|
|
189
|
+
return true;
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
type: 'list',
|
|
194
|
+
name: 'transport',
|
|
195
|
+
message: 'Transport type:',
|
|
196
|
+
choices: [
|
|
197
|
+
{
|
|
198
|
+
name: `${TransportTypes.STDIO} - Spawn MCP server as a subprocess`,
|
|
199
|
+
value: TransportTypes.STDIO,
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: `${TransportTypes.HTTP} - Connect to a running MCP server via HTTP`,
|
|
203
|
+
value: TransportTypes.HTTP,
|
|
204
|
+
},
|
|
205
|
+
],
|
|
206
|
+
default: defaults?.transport || Defaults.TRANSPORT,
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
type: 'input',
|
|
210
|
+
name: 'mcpUrl',
|
|
211
|
+
message: 'MCP server URL:',
|
|
212
|
+
default: defaults?.mcpUrl ? String(defaults.mcpUrl) : Defaults.MCP_URL,
|
|
213
|
+
when: (answers) => answers?.transport === TransportTypes.HTTP,
|
|
214
|
+
validate: (input, answers) => {
|
|
215
|
+
if (answers?.transport === TransportTypes.HTTP) {
|
|
216
|
+
if (!input.trim()) {
|
|
217
|
+
return Messages.PROMPT_MCP_URL_REQUIRED;
|
|
218
|
+
}
|
|
219
|
+
try {
|
|
220
|
+
new URL(input);
|
|
221
|
+
return true;
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
return Messages.PROMPT_URL_INVALID;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return true;
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
type: 'input',
|
|
232
|
+
name: 'script',
|
|
233
|
+
message: 'Script command to run MCP server:',
|
|
234
|
+
default: defaults?.script ? String(defaults.script) : 'python3 server.py',
|
|
235
|
+
validate: (input) => {
|
|
236
|
+
if (!input.trim()) {
|
|
237
|
+
return Messages.PROMPT_SCRIPT_REQUIRED;
|
|
238
|
+
}
|
|
239
|
+
return true;
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
];
|
|
243
|
+
// Combine project prompts with LLM provider prompts
|
|
244
|
+
const llmPrompts = getLLMProviderPrompts(defaults?.llmProviders ? Object.keys(defaults.llmProviders) : undefined);
|
|
245
|
+
const allPrompts = [...projectPrompts, ...llmPrompts];
|
|
246
|
+
const answers = await inquirer.prompt(allPrompts);
|
|
228
247
|
return {
|
|
229
248
|
projectName: makeProjectName(answers.projectName),
|
|
230
249
|
agentName: makeAgentName(answers.agentName),
|
|
231
250
|
transport: answers.transport,
|
|
232
251
|
mcpUrl: answers.mcpUrl ? makeMCPURL(answers.mcpUrl) : undefined,
|
|
233
252
|
script: makeScriptCommand(answers.script),
|
|
234
|
-
llmProviders,
|
|
253
|
+
llmProviders: buildLLMProvidersConfig(answers),
|
|
235
254
|
nonInteractive: false,
|
|
236
255
|
};
|
|
237
256
|
}
|
|
@@ -260,4 +279,45 @@ export function getDefaultInitOptions(projectRoot) {
|
|
|
260
279
|
nonInteractive: true,
|
|
261
280
|
};
|
|
262
281
|
}
|
|
282
|
+
/**
|
|
283
|
+
* Prompt user for global initialization details (LLM providers only).
|
|
284
|
+
* @returns User's answers for global config
|
|
285
|
+
*/
|
|
286
|
+
export async function promptGlobalInitOptions() {
|
|
287
|
+
const answers = await inquirer.prompt([
|
|
288
|
+
{
|
|
289
|
+
type: 'input',
|
|
290
|
+
name: 'agentName',
|
|
291
|
+
message: 'Agent name:',
|
|
292
|
+
default: Defaults.AGENT_NAME,
|
|
293
|
+
validate: (input) => {
|
|
294
|
+
if (!input.trim()) {
|
|
295
|
+
return Messages.PROMPT_AGENT_NAME_REQUIRED;
|
|
296
|
+
}
|
|
297
|
+
return true;
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
...getLLMProviderPrompts(),
|
|
301
|
+
]);
|
|
302
|
+
return {
|
|
303
|
+
agentName: makeAgentName(answers.agentName),
|
|
304
|
+
llmProviders: buildLLMProvidersConfig(answers),
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Create default global init options for non-interactive mode.
|
|
309
|
+
* @returns Default options for global config
|
|
310
|
+
*/
|
|
311
|
+
export function getDefaultGlobalInitOptions() {
|
|
312
|
+
return {
|
|
313
|
+
agentName: makeAgentName(Defaults.AGENT_NAME),
|
|
314
|
+
llmProviders: {
|
|
315
|
+
openai: {
|
|
316
|
+
apiKey: makeAPIKey(EnvVars.OPENAI_API_KEY),
|
|
317
|
+
modelName: makeModelName(EnvVars.OPENAI_MODEL_NAME),
|
|
318
|
+
default: true,
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
};
|
|
322
|
+
}
|
|
263
323
|
//# sourceMappingURL=init-prompt.js.map
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* Command error handling utilities.
|
|
3
3
|
* Provides consistent error handling patterns for CLI commands.
|
|
4
4
|
*/
|
|
5
|
-
import {
|
|
6
|
-
import { ConfigurationError,
|
|
5
|
+
import { log } from '../../utils/logger.js';
|
|
6
|
+
import { ConfigurationError, getErrorMessage } from '../../utils/errors.js';
|
|
7
7
|
import { Messages } from '../../constants/index.js';
|
|
8
8
|
/**
|
|
9
9
|
* Handle and display command errors with consistent formatting.
|
|
@@ -23,9 +23,6 @@ export function handleCommandError(error, defaultMessage = Messages.ERROR_UNEXPE
|
|
|
23
23
|
log.blank();
|
|
24
24
|
process.exit(1);
|
|
25
25
|
}
|
|
26
|
-
const err = normalizeError(error);
|
|
27
|
-
// Only log to structured logger (won't output unless debug mode is enabled)
|
|
28
|
-
logger.error('Command failed', err);
|
|
29
26
|
const message = getErrorMessage(error) || defaultMessage;
|
|
30
27
|
log.blank();
|
|
31
28
|
log.error(message);
|
|
@@ -13,15 +13,25 @@ export interface EnvCheckResult {
|
|
|
13
13
|
keyExistsInEnvFile?: boolean;
|
|
14
14
|
/** Error message explaining why the variable is not set */
|
|
15
15
|
errorMessage?: string;
|
|
16
|
+
/** Source of the environment variable: 'process.env' | 'global.env' | 'local.env' */
|
|
17
|
+
source?: 'process.env' | 'global.env' | 'local.env';
|
|
16
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Load global environment file.
|
|
21
|
+
* @returns Map of environment variables from ~/.syrin/.env
|
|
22
|
+
*/
|
|
23
|
+
export declare function loadGlobalEnvFile(): Map<string, string>;
|
|
17
24
|
/**
|
|
18
25
|
* Check if an environment variable is set.
|
|
19
|
-
* Checks
|
|
26
|
+
* Checks in order:
|
|
27
|
+
* - For local context: process.env > local .env > global .env
|
|
28
|
+
* - For global context: process.env > global .env
|
|
20
29
|
* @param varName - Name of the environment variable
|
|
21
30
|
* @param projectRoot - Root directory of the project
|
|
31
|
+
* @param isGlobalContext - Whether this check is for global config (affects check order and error messages)
|
|
22
32
|
* @returns Check result with detailed error information
|
|
23
33
|
*/
|
|
24
|
-
export declare function checkEnvVar(varName: string, projectRoot?: string): EnvCheckResult;
|
|
34
|
+
export declare function checkEnvVar(varName: string, projectRoot?: string, isGlobalContext?: boolean): EnvCheckResult;
|
|
25
35
|
/**
|
|
26
36
|
* Check if a command exists and is executable.
|
|
27
37
|
* @param command - Command to check (e.g., "python3", "node")
|
|
@@ -6,6 +6,7 @@ import * as fs from 'fs';
|
|
|
6
6
|
import * as path from 'path';
|
|
7
7
|
import { execSync } from 'child_process';
|
|
8
8
|
import { Messages, Paths } from '../constants/index.js';
|
|
9
|
+
import { getGlobalEnvPath } from './global-loader.js';
|
|
9
10
|
/**
|
|
10
11
|
* Parse .env file and return key-value pairs.
|
|
11
12
|
* @param envFilePath - Path to .env file
|
|
@@ -44,62 +45,111 @@ function parseEnvFile(envFilePath) {
|
|
|
44
45
|
}
|
|
45
46
|
return envMap;
|
|
46
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Load global environment file.
|
|
50
|
+
* @returns Map of environment variables from ~/.syrin/.env
|
|
51
|
+
*/
|
|
52
|
+
export function loadGlobalEnvFile() {
|
|
53
|
+
const globalEnvPath = getGlobalEnvPath();
|
|
54
|
+
return parseEnvFile(globalEnvPath);
|
|
55
|
+
}
|
|
47
56
|
/**
|
|
48
57
|
* Check if an environment variable is set.
|
|
49
|
-
* Checks
|
|
58
|
+
* Checks in order:
|
|
59
|
+
* - For local context: process.env > local .env > global .env
|
|
60
|
+
* - For global context: process.env > global .env
|
|
50
61
|
* @param varName - Name of the environment variable
|
|
51
62
|
* @param projectRoot - Root directory of the project
|
|
63
|
+
* @param isGlobalContext - Whether this check is for global config (affects check order and error messages)
|
|
52
64
|
* @returns Check result with detailed error information
|
|
53
65
|
*/
|
|
54
|
-
export function checkEnvVar(varName, projectRoot = process.cwd()) {
|
|
55
|
-
const
|
|
56
|
-
const
|
|
66
|
+
export function checkEnvVar(varName, projectRoot = process.cwd(), isGlobalContext = false) {
|
|
67
|
+
const localEnvFilePath = path.join(projectRoot, Paths.ENV_FILE);
|
|
68
|
+
const globalEnvPath = getGlobalEnvPath();
|
|
69
|
+
const localEnvFileExists = fs.existsSync(localEnvFilePath);
|
|
70
|
+
const globalEnvFileExists = fs.existsSync(globalEnvPath);
|
|
57
71
|
// First check process.env (takes precedence)
|
|
58
72
|
const processEnvValue = process.env[varName];
|
|
59
73
|
if (processEnvValue !== undefined && processEnvValue !== '') {
|
|
60
74
|
return {
|
|
61
75
|
isSet: true,
|
|
62
76
|
value: processEnvValue,
|
|
63
|
-
envFileExists,
|
|
64
|
-
keyExistsInEnvFile: undefined,
|
|
77
|
+
envFileExists: localEnvFileExists || globalEnvFileExists,
|
|
78
|
+
keyExistsInEnvFile: undefined,
|
|
79
|
+
source: 'process.env',
|
|
65
80
|
};
|
|
66
81
|
}
|
|
67
|
-
//
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
82
|
+
// For local context: check local .env first, then global .env
|
|
83
|
+
// For global context: check global .env only
|
|
84
|
+
if (!isGlobalContext && localEnvFileExists) {
|
|
85
|
+
const localEnvMap = parseEnvFile(localEnvFilePath);
|
|
86
|
+
const keyExistsInLocalEnv = localEnvMap.has(varName);
|
|
87
|
+
const localEnvValue = localEnvMap.get(varName);
|
|
88
|
+
if (keyExistsInLocalEnv) {
|
|
89
|
+
if (localEnvValue === '' || localEnvValue === undefined) {
|
|
90
|
+
return {
|
|
91
|
+
isSet: false,
|
|
92
|
+
value: localEnvValue,
|
|
93
|
+
envFileExists: true,
|
|
94
|
+
keyExistsInEnvFile: true,
|
|
95
|
+
errorMessage: Messages.ENV_KEY_EMPTY(varName),
|
|
96
|
+
source: 'local.env',
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
100
|
+
isSet: true,
|
|
101
|
+
value: localEnvValue,
|
|
102
|
+
envFileExists: true,
|
|
103
|
+
keyExistsInEnvFile: true,
|
|
104
|
+
source: 'local.env',
|
|
105
|
+
};
|
|
106
|
+
}
|
|
75
107
|
}
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
108
|
+
// Check global .env file (fallback for local context, primary for global context)
|
|
109
|
+
if (globalEnvFileExists) {
|
|
110
|
+
const globalEnvMap = parseEnvFile(globalEnvPath);
|
|
111
|
+
const keyExistsInGlobalEnv = globalEnvMap.has(varName);
|
|
112
|
+
const globalEnvValue = globalEnvMap.get(varName);
|
|
113
|
+
if (keyExistsInGlobalEnv) {
|
|
114
|
+
if (globalEnvValue === '') {
|
|
115
|
+
return {
|
|
116
|
+
isSet: false,
|
|
117
|
+
envFileExists: true,
|
|
118
|
+
keyExistsInEnvFile: true,
|
|
119
|
+
source: 'global.env',
|
|
120
|
+
errorMessage: Messages.ENV_SET_INSTRUCTIONS(varName),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
isSet: true,
|
|
125
|
+
value: globalEnvValue,
|
|
126
|
+
envFileExists: true,
|
|
127
|
+
keyExistsInEnvFile: true,
|
|
128
|
+
source: 'global.env',
|
|
129
|
+
};
|
|
130
|
+
}
|
|
87
131
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
errorMessage
|
|
95
|
-
}
|
|
132
|
+
// Variable not found in any source
|
|
133
|
+
// Generate appropriate error message based on context
|
|
134
|
+
let errorMessage;
|
|
135
|
+
if (isGlobalContext) {
|
|
136
|
+
// For global config, suggest global .env or environment variables
|
|
137
|
+
if (globalEnvFileExists) {
|
|
138
|
+
errorMessage = `Key "${varName}" not found in ${globalEnvPath} or environment variables`;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
errorMessage = `Global .env file not found at ${globalEnvPath}. Create it with: syrin config edit-env --global`;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
// For local config, use original message
|
|
146
|
+
errorMessage = Messages.ENV_FILE_NOT_PRESENT(varName);
|
|
96
147
|
}
|
|
97
|
-
// Key exists and has a value
|
|
98
148
|
return {
|
|
99
|
-
isSet:
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
149
|
+
isSet: false,
|
|
150
|
+
envFileExists: localEnvFileExists || globalEnvFileExists,
|
|
151
|
+
keyExistsInEnvFile: false,
|
|
152
|
+
errorMessage,
|
|
103
153
|
};
|
|
104
154
|
}
|
|
105
155
|
/**
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment file templates.
|
|
3
|
+
* Provides templates for creating .env files.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Get template for global environment file.
|
|
7
|
+
* @returns Template content for ~/.syrin/.env
|
|
8
|
+
*/
|
|
9
|
+
export declare function getGlobalEnvTemplate(): string;
|
|
10
|
+
/**
|
|
11
|
+
* Get template for local environment file.
|
|
12
|
+
* @returns Template content for ./.env
|
|
13
|
+
*/
|
|
14
|
+
export declare function getLocalEnvTemplate(): string;
|
|
15
|
+
//# sourceMappingURL=env-templates.d.ts.map
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment file templates.
|
|
3
|
+
* Provides templates for creating .env files.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Get template for global environment file.
|
|
7
|
+
* @returns Template content for ~/.syrin/.env
|
|
8
|
+
*/
|
|
9
|
+
export function getGlobalEnvTemplate() {
|
|
10
|
+
return `# Syrin Global Environment Variables
|
|
11
|
+
# ===========================================
|
|
12
|
+
# Add your API keys and model names here.
|
|
13
|
+
# These will be used when the global config references environment variable names.
|
|
14
|
+
#
|
|
15
|
+
# Example:
|
|
16
|
+
# OPENAI_API_KEY=sk-proj-abc123...
|
|
17
|
+
# OPENAI_MODEL=gpt-4-turbo
|
|
18
|
+
# ANTHROPIC_API_KEY=sk-ant-api03-...
|
|
19
|
+
# ANTHROPIC_MODEL=claude-3-5-sonnet-20241022
|
|
20
|
+
# OLLAMA_MODEL_NAME=llama2
|
|
21
|
+
#
|
|
22
|
+
# Security Note:
|
|
23
|
+
# This file contains sensitive credentials. Keep it secure and never commit it to version control.
|
|
24
|
+
# File permissions are set to 600 (read/write owner only) for security.
|
|
25
|
+
`;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Get template for local environment file.
|
|
29
|
+
* @returns Template content for ./.env
|
|
30
|
+
*/
|
|
31
|
+
export function getLocalEnvTemplate() {
|
|
32
|
+
return `# Syrin Project Environment Variables
|
|
33
|
+
# ===========================================
|
|
34
|
+
# Add your API keys and model names here.
|
|
35
|
+
# These will be used when the local config references environment variable names.
|
|
36
|
+
#
|
|
37
|
+
# Example:
|
|
38
|
+
# OPENAI_API_KEY=sk-proj-abc123...
|
|
39
|
+
# OPENAI_MODEL=gpt-4-turbo
|
|
40
|
+
# ANTHROPIC_API_KEY=sk-ant-api03-...
|
|
41
|
+
# ANTHROPIC_MODEL=claude-3-5-sonnet-20241022
|
|
42
|
+
# OLLAMA_MODEL_NAME=llama2
|
|
43
|
+
#
|
|
44
|
+
# Security Note:
|
|
45
|
+
# This file contains sensitive credentials. Keep it secure and never commit it to version control.
|
|
46
|
+
# File permissions are set to 600 (read/write owner only) for security.
|
|
47
|
+
`;
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=env-templates.js.map
|
package/dist/config/generator.js
CHANGED
|
@@ -155,8 +155,25 @@ function buildConfigContent(options) {
|
|
|
155
155
|
}
|
|
156
156
|
// Replace LLM providers section
|
|
157
157
|
template = template.replace(/\{\{#LLM_PROVIDERS\}\}([\s\S]*?)\{\{\/LLM_PROVIDERS\}\}/, llmSection);
|
|
158
|
+
// Clean up whitespace
|
|
159
|
+
template = cleanupYamlWhitespace(template);
|
|
158
160
|
return template;
|
|
159
161
|
}
|
|
162
|
+
/**
|
|
163
|
+
* Clean up excessive whitespace in generated YAML.
|
|
164
|
+
* - Removes trailing whitespace from lines
|
|
165
|
+
* - Collapses 3+ consecutive blank lines into 2
|
|
166
|
+
* - Ensures file ends with single newline
|
|
167
|
+
*/
|
|
168
|
+
function cleanupYamlWhitespace(content) {
|
|
169
|
+
return (content
|
|
170
|
+
// Remove trailing whitespace from each line
|
|
171
|
+
.replace(/[ \t]+$/gm, '')
|
|
172
|
+
// Collapse 3+ consecutive newlines into 2 (one blank line)
|
|
173
|
+
.replace(/\n{3,}/g, '\n\n')
|
|
174
|
+
// Ensure single newline at end of file
|
|
175
|
+
.replace(/\n*$/, '\n'));
|
|
176
|
+
}
|
|
160
177
|
/**
|
|
161
178
|
* Build LLM configuration object from init options.
|
|
162
179
|
*/
|