prompt-language-shell 0.8.4 → 0.8.8
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/dist/configuration/io.js +85 -0
- package/dist/configuration/messages.js +30 -0
- package/dist/configuration/schema.js +167 -0
- package/dist/configuration/transformation.js +55 -0
- package/dist/configuration/types.js +30 -0
- package/dist/configuration/validation.js +52 -0
- package/dist/execution/handlers.js +135 -0
- package/dist/execution/processing.js +36 -0
- package/dist/execution/reducer.js +148 -0
- package/dist/execution/types.js +12 -0
- package/dist/execution/validation.js +12 -0
- package/dist/index.js +1 -1
- package/dist/services/anthropic.js +2 -1
- package/dist/services/colors.js +22 -12
- package/dist/services/components.js +35 -11
- package/dist/services/config-labels.js +15 -15
- package/dist/services/logger.js +2 -1
- package/dist/services/messages.js +53 -1
- package/dist/services/refinement.js +11 -6
- package/dist/services/router.js +92 -52
- package/dist/skills/execute.md +79 -9
- package/dist/skills/schedule.md +121 -29
- package/dist/tools/execute.tool.js +4 -0
- package/dist/types/schemas.js +1 -0
- package/dist/ui/Answer.js +36 -15
- package/dist/ui/Command.js +43 -23
- package/dist/ui/Component.js +147 -33
- package/dist/ui/Config.js +73 -79
- package/dist/ui/Confirm.js +34 -21
- package/dist/ui/Execute.js +129 -329
- package/dist/ui/Feedback.js +2 -1
- package/dist/ui/Introspect.js +51 -24
- package/dist/ui/Label.js +4 -3
- package/dist/ui/List.js +3 -2
- package/dist/ui/Main.js +5 -1
- package/dist/ui/Refinement.js +8 -1
- package/dist/ui/Schedule.js +89 -61
- package/dist/ui/Validate.js +75 -77
- package/dist/ui/Workflow.js +47 -123
- package/package.json +1 -1
- package/dist/services/configuration.js +0 -409
|
@@ -1,409 +0,0 @@
|
|
|
1
|
-
import { homedir } from 'os';
|
|
2
|
-
import { join } from 'path';
|
|
3
|
-
import YAML from 'yaml';
|
|
4
|
-
import { getConfigLabel } from './config-labels.js';
|
|
5
|
-
import { flattenConfig } from './config-utils.js';
|
|
6
|
-
import { defaultFileSystem } from './filesystem.js';
|
|
7
|
-
/**
|
|
8
|
-
* Convert a dotted config key to a readable label
|
|
9
|
-
* Example: "project.alpha.repo" -> "Project Alpha Repo"
|
|
10
|
-
*/
|
|
11
|
-
function keyToLabel(key) {
|
|
12
|
-
return key
|
|
13
|
-
.split('.')
|
|
14
|
-
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
15
|
-
.join(' ');
|
|
16
|
-
}
|
|
17
|
-
export var AnthropicModel;
|
|
18
|
-
(function (AnthropicModel) {
|
|
19
|
-
AnthropicModel["Sonnet"] = "claude-sonnet-4-5";
|
|
20
|
-
AnthropicModel["Haiku"] = "claude-haiku-4-5";
|
|
21
|
-
AnthropicModel["Opus"] = "claude-opus-4-1";
|
|
22
|
-
})(AnthropicModel || (AnthropicModel = {}));
|
|
23
|
-
export const SUPPORTED_MODELS = Object.values(AnthropicModel);
|
|
24
|
-
export var DebugLevel;
|
|
25
|
-
(function (DebugLevel) {
|
|
26
|
-
DebugLevel["None"] = "none";
|
|
27
|
-
DebugLevel["Info"] = "info";
|
|
28
|
-
DebugLevel["Verbose"] = "verbose";
|
|
29
|
-
})(DebugLevel || (DebugLevel = {}));
|
|
30
|
-
export const SUPPORTED_DEBUG_LEVELS = Object.values(DebugLevel);
|
|
31
|
-
export var ConfigDefinitionType;
|
|
32
|
-
(function (ConfigDefinitionType) {
|
|
33
|
-
ConfigDefinitionType["RegExp"] = "regexp";
|
|
34
|
-
ConfigDefinitionType["String"] = "string";
|
|
35
|
-
ConfigDefinitionType["Enum"] = "enum";
|
|
36
|
-
ConfigDefinitionType["Number"] = "number";
|
|
37
|
-
ConfigDefinitionType["Boolean"] = "boolean";
|
|
38
|
-
})(ConfigDefinitionType || (ConfigDefinitionType = {}));
|
|
39
|
-
export class ConfigError extends Error {
|
|
40
|
-
origin;
|
|
41
|
-
constructor(message, origin) {
|
|
42
|
-
super(message);
|
|
43
|
-
this.name = 'ConfigError';
|
|
44
|
-
this.origin = origin;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
function getConfigFile() {
|
|
48
|
-
return join(homedir(), '.plsrc');
|
|
49
|
-
}
|
|
50
|
-
function parseYamlConfig(content) {
|
|
51
|
-
try {
|
|
52
|
-
return YAML.parse(content);
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
throw new ConfigError('Failed to parse configuration file', error instanceof Error ? error : undefined);
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
function validateConfig(parsed) {
|
|
59
|
-
if (!parsed || typeof parsed !== 'object') {
|
|
60
|
-
throw new ConfigError('Invalid configuration format');
|
|
61
|
-
}
|
|
62
|
-
const config = parsed;
|
|
63
|
-
// Validate anthropic section
|
|
64
|
-
if (!config.anthropic || typeof config.anthropic !== 'object') {
|
|
65
|
-
throw new ConfigError('Missing or invalid anthropic configuration');
|
|
66
|
-
}
|
|
67
|
-
const { key, model } = config.anthropic;
|
|
68
|
-
if (!key || typeof key !== 'string') {
|
|
69
|
-
throw new ConfigError('Missing or invalid API key');
|
|
70
|
-
}
|
|
71
|
-
const validatedConfig = {
|
|
72
|
-
anthropic: {
|
|
73
|
-
key,
|
|
74
|
-
},
|
|
75
|
-
};
|
|
76
|
-
// Optional model - only set if valid
|
|
77
|
-
if (model && typeof model === 'string' && isValidAnthropicModel(model)) {
|
|
78
|
-
validatedConfig.anthropic.model = model;
|
|
79
|
-
}
|
|
80
|
-
// Optional settings section
|
|
81
|
-
if (config.settings && typeof config.settings === 'object') {
|
|
82
|
-
const settings = config.settings;
|
|
83
|
-
validatedConfig.settings = {};
|
|
84
|
-
if ('debug' in settings) {
|
|
85
|
-
// Handle migration from boolean to enum
|
|
86
|
-
if (typeof settings.debug === 'boolean') {
|
|
87
|
-
validatedConfig.settings.debug = settings.debug
|
|
88
|
-
? DebugLevel.Info
|
|
89
|
-
: DebugLevel.None;
|
|
90
|
-
}
|
|
91
|
-
else if (typeof settings.debug === 'string' &&
|
|
92
|
-
SUPPORTED_DEBUG_LEVELS.includes(settings.debug)) {
|
|
93
|
-
validatedConfig.settings.debug = settings.debug;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return validatedConfig;
|
|
98
|
-
}
|
|
99
|
-
export function loadConfig(fs = defaultFileSystem) {
|
|
100
|
-
const configFile = getConfigFile();
|
|
101
|
-
if (!fs.exists(configFile)) {
|
|
102
|
-
throw new ConfigError('Configuration not found');
|
|
103
|
-
}
|
|
104
|
-
const content = fs.readFile(configFile, 'utf-8');
|
|
105
|
-
const parsed = parseYamlConfig(content);
|
|
106
|
-
return validateConfig(parsed);
|
|
107
|
-
}
|
|
108
|
-
export function getConfigPath() {
|
|
109
|
-
return getConfigFile();
|
|
110
|
-
}
|
|
111
|
-
export function configExists(fs = defaultFileSystem) {
|
|
112
|
-
return fs.exists(getConfigFile());
|
|
113
|
-
}
|
|
114
|
-
export function isValidAnthropicApiKey(key) {
|
|
115
|
-
// Anthropic API keys format: sk-ant-api03-XXXXX (108 chars total)
|
|
116
|
-
// - Prefix: sk-ant-api03- (13 chars)
|
|
117
|
-
// - Key body: 95 characters (uppercase, lowercase, digits, hyphens, underscores)
|
|
118
|
-
const apiKeyPattern = /^sk-ant-api03-[A-Za-z0-9_-]{95}$/;
|
|
119
|
-
return apiKeyPattern.test(key);
|
|
120
|
-
}
|
|
121
|
-
export function isValidAnthropicModel(model) {
|
|
122
|
-
return SUPPORTED_MODELS.includes(model);
|
|
123
|
-
}
|
|
124
|
-
export function hasValidAnthropicKey() {
|
|
125
|
-
try {
|
|
126
|
-
const config = loadConfig();
|
|
127
|
-
return (!!config.anthropic.key && isValidAnthropicApiKey(config.anthropic.key));
|
|
128
|
-
}
|
|
129
|
-
catch {
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
export function mergeConfig(existingContent, sectionName, newValues) {
|
|
134
|
-
const parsed = existingContent.trim()
|
|
135
|
-
? YAML.parse(existingContent)
|
|
136
|
-
: {};
|
|
137
|
-
// Update or add section
|
|
138
|
-
const section = parsed[sectionName] ?? {};
|
|
139
|
-
for (const [key, value] of Object.entries(newValues)) {
|
|
140
|
-
section[key] = value;
|
|
141
|
-
}
|
|
142
|
-
parsed[sectionName] = section;
|
|
143
|
-
// Sort sections alphabetically
|
|
144
|
-
const sortedKeys = Object.keys(parsed).sort();
|
|
145
|
-
const sortedConfig = {};
|
|
146
|
-
for (const key of sortedKeys) {
|
|
147
|
-
sortedConfig[key] = parsed[key];
|
|
148
|
-
}
|
|
149
|
-
// Convert back to YAML
|
|
150
|
-
return YAML.stringify(sortedConfig);
|
|
151
|
-
}
|
|
152
|
-
export function saveConfig(section, config, fs = defaultFileSystem) {
|
|
153
|
-
const configFile = getConfigFile();
|
|
154
|
-
const existingContent = fs.exists(configFile)
|
|
155
|
-
? fs.readFile(configFile, 'utf-8')
|
|
156
|
-
: '';
|
|
157
|
-
const newContent = mergeConfig(existingContent, section, config);
|
|
158
|
-
fs.writeFile(configFile, newContent);
|
|
159
|
-
}
|
|
160
|
-
export function saveAnthropicConfig(config, fs = defaultFileSystem) {
|
|
161
|
-
saveConfig('anthropic', config, fs);
|
|
162
|
-
return loadConfig(fs);
|
|
163
|
-
}
|
|
164
|
-
export function saveDebugSetting(debug, fs = defaultFileSystem) {
|
|
165
|
-
saveConfig('settings', { debug }, fs);
|
|
166
|
-
}
|
|
167
|
-
export function loadDebugSetting(fs = defaultFileSystem) {
|
|
168
|
-
try {
|
|
169
|
-
const config = loadConfig(fs);
|
|
170
|
-
return config.settings?.debug ?? DebugLevel.None;
|
|
171
|
-
}
|
|
172
|
-
catch {
|
|
173
|
-
return DebugLevel.None;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
/**
|
|
177
|
-
* Returns a message requesting initial setup.
|
|
178
|
-
* Provides natural language variations that sound like a professional concierge
|
|
179
|
-
* preparing to serve, avoiding technical jargon.
|
|
180
|
-
*
|
|
181
|
-
* @param forFutureUse - If true, indicates setup is for future requests rather than
|
|
182
|
-
* an immediate task
|
|
183
|
-
*/
|
|
184
|
-
export function getConfigurationRequiredMessage(forFutureUse = false) {
|
|
185
|
-
if (forFutureUse) {
|
|
186
|
-
const messages = [
|
|
187
|
-
"Before I can assist with your requests, let's get a few things ready.",
|
|
188
|
-
'Let me set up a few things so I can help you in the future.',
|
|
189
|
-
"I'll need to prepare a few things before I can assist you.",
|
|
190
|
-
"Let's get everything ready so I can help with your tasks.",
|
|
191
|
-
"I need to set up a few things first, then I'll be ready to assist.",
|
|
192
|
-
'Let me prepare everything so I can help you going forward.',
|
|
193
|
-
];
|
|
194
|
-
return messages[Math.floor(Math.random() * messages.length)];
|
|
195
|
-
}
|
|
196
|
-
const messages = [
|
|
197
|
-
'Before I can help, let me get a few things ready.',
|
|
198
|
-
'I need to set up a few things first.',
|
|
199
|
-
'Let me prepare everything before we begin.',
|
|
200
|
-
'Just a moment while I get ready to assist you.',
|
|
201
|
-
"I'll need to get set up before I can help with that.",
|
|
202
|
-
'Let me get everything ready for you.',
|
|
203
|
-
];
|
|
204
|
-
return messages[Math.floor(Math.random() * messages.length)];
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Core configuration schema - defines structure and types for system settings
|
|
208
|
-
*/
|
|
209
|
-
const coreConfigSchema = {
|
|
210
|
-
'anthropic.key': {
|
|
211
|
-
type: ConfigDefinitionType.RegExp,
|
|
212
|
-
required: true,
|
|
213
|
-
pattern: /^sk-ant-api03-[A-Za-z0-9_-]{95}$/,
|
|
214
|
-
description: 'Anthropic API key',
|
|
215
|
-
},
|
|
216
|
-
'anthropic.model': {
|
|
217
|
-
type: ConfigDefinitionType.Enum,
|
|
218
|
-
required: true,
|
|
219
|
-
values: SUPPORTED_MODELS,
|
|
220
|
-
default: AnthropicModel.Haiku,
|
|
221
|
-
description: 'Anthropic model',
|
|
222
|
-
},
|
|
223
|
-
'settings.debug': {
|
|
224
|
-
type: ConfigDefinitionType.Enum,
|
|
225
|
-
required: false,
|
|
226
|
-
values: SUPPORTED_DEBUG_LEVELS,
|
|
227
|
-
default: DebugLevel.None,
|
|
228
|
-
description: 'Debug mode',
|
|
229
|
-
},
|
|
230
|
-
};
|
|
231
|
-
/**
|
|
232
|
-
* Get complete configuration schema
|
|
233
|
-
* Currently returns core schema only
|
|
234
|
-
* Future: will merge with skill-declared schemas
|
|
235
|
-
*/
|
|
236
|
-
export function getConfigSchema() {
|
|
237
|
-
return {
|
|
238
|
-
...coreConfigSchema,
|
|
239
|
-
// Future: ...loadSkillSchemas()
|
|
240
|
-
};
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Get missing required configuration keys
|
|
244
|
-
* Returns array of keys that are required but not present or invalid in config
|
|
245
|
-
*/
|
|
246
|
-
export function getMissingConfigKeys() {
|
|
247
|
-
const schema = getConfigSchema();
|
|
248
|
-
const missing = [];
|
|
249
|
-
let currentConfig = null;
|
|
250
|
-
try {
|
|
251
|
-
currentConfig = loadConfig();
|
|
252
|
-
}
|
|
253
|
-
catch {
|
|
254
|
-
// Config doesn't exist
|
|
255
|
-
}
|
|
256
|
-
for (const [key, definition] of Object.entries(schema)) {
|
|
257
|
-
if (!definition.required) {
|
|
258
|
-
continue;
|
|
259
|
-
}
|
|
260
|
-
// Get current value for this key
|
|
261
|
-
const parts = key.split('.');
|
|
262
|
-
let value = currentConfig;
|
|
263
|
-
for (const part of parts) {
|
|
264
|
-
if (value && typeof value === 'object' && part in value) {
|
|
265
|
-
value = value[part];
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
value = undefined;
|
|
269
|
-
break;
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
// Check if value is missing or invalid
|
|
273
|
-
if (value === undefined || value === null) {
|
|
274
|
-
missing.push(key);
|
|
275
|
-
continue;
|
|
276
|
-
}
|
|
277
|
-
// Validate based on type
|
|
278
|
-
let isValid = false;
|
|
279
|
-
switch (definition.type) {
|
|
280
|
-
case ConfigDefinitionType.RegExp:
|
|
281
|
-
isValid = typeof value === 'string' && definition.pattern.test(value);
|
|
282
|
-
break;
|
|
283
|
-
case ConfigDefinitionType.String:
|
|
284
|
-
isValid = typeof value === 'string';
|
|
285
|
-
break;
|
|
286
|
-
case ConfigDefinitionType.Enum:
|
|
287
|
-
isValid =
|
|
288
|
-
typeof value === 'string' && definition.values.includes(value);
|
|
289
|
-
break;
|
|
290
|
-
case ConfigDefinitionType.Number:
|
|
291
|
-
isValid = typeof value === 'number';
|
|
292
|
-
break;
|
|
293
|
-
case ConfigDefinitionType.Boolean:
|
|
294
|
-
isValid = typeof value === 'boolean';
|
|
295
|
-
break;
|
|
296
|
-
}
|
|
297
|
-
if (!isValid) {
|
|
298
|
-
missing.push(key);
|
|
299
|
-
}
|
|
300
|
-
}
|
|
301
|
-
return missing;
|
|
302
|
-
}
|
|
303
|
-
/**
|
|
304
|
-
* Get list of configured keys from config file
|
|
305
|
-
* Returns array of dot-notation keys that exist in the config file
|
|
306
|
-
*/
|
|
307
|
-
export function getConfiguredKeys(fs = defaultFileSystem) {
|
|
308
|
-
try {
|
|
309
|
-
const configFile = getConfigFile();
|
|
310
|
-
if (!fs.exists(configFile)) {
|
|
311
|
-
return [];
|
|
312
|
-
}
|
|
313
|
-
const content = fs.readFile(configFile, 'utf-8');
|
|
314
|
-
const parsed = YAML.parse(content);
|
|
315
|
-
// Flatten nested config to dot notation
|
|
316
|
-
const flatConfig = flattenConfig(parsed);
|
|
317
|
-
return Object.keys(flatConfig);
|
|
318
|
-
}
|
|
319
|
-
catch {
|
|
320
|
-
return [];
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
/**
|
|
324
|
-
* Get available config structure for CONFIG tool
|
|
325
|
-
* Returns keys with descriptions only (no values for privacy)
|
|
326
|
-
* Marks optional keys as "(optional)"
|
|
327
|
-
*/
|
|
328
|
-
export function getAvailableConfigStructure(fs = defaultFileSystem) {
|
|
329
|
-
const schema = getConfigSchema();
|
|
330
|
-
const structure = {};
|
|
331
|
-
// Try to load existing config to see which keys are already set
|
|
332
|
-
let flatConfig = {};
|
|
333
|
-
try {
|
|
334
|
-
const configFile = getConfigFile();
|
|
335
|
-
if (fs.exists(configFile)) {
|
|
336
|
-
const content = fs.readFile(configFile, 'utf-8');
|
|
337
|
-
const parsed = YAML.parse(content);
|
|
338
|
-
// Flatten nested config to dot notation
|
|
339
|
-
flatConfig = flattenConfig(parsed);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
catch {
|
|
343
|
-
// Config file doesn't exist or can't be read
|
|
344
|
-
}
|
|
345
|
-
// Add schema keys with descriptions
|
|
346
|
-
for (const [key, definition] of Object.entries(schema)) {
|
|
347
|
-
structure[key] = definition.description;
|
|
348
|
-
}
|
|
349
|
-
// Add discovered keys that aren't in schema
|
|
350
|
-
for (const key of Object.keys(flatConfig)) {
|
|
351
|
-
if (!(key in structure)) {
|
|
352
|
-
structure[key] = getConfigLabel(key) || keyToLabel(key);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
return structure;
|
|
356
|
-
}
|
|
357
|
-
/**
|
|
358
|
-
* Convert string value to appropriate type based on schema definition
|
|
359
|
-
*/
|
|
360
|
-
function parseConfigValue(key, stringValue, schema) {
|
|
361
|
-
// If we have a schema definition, use its type
|
|
362
|
-
if (key in schema) {
|
|
363
|
-
const definition = schema[key];
|
|
364
|
-
switch (definition.type) {
|
|
365
|
-
case ConfigDefinitionType.Boolean:
|
|
366
|
-
return stringValue === 'true';
|
|
367
|
-
case ConfigDefinitionType.Number:
|
|
368
|
-
return Number(stringValue);
|
|
369
|
-
case ConfigDefinitionType.String:
|
|
370
|
-
case ConfigDefinitionType.RegExp:
|
|
371
|
-
case ConfigDefinitionType.Enum:
|
|
372
|
-
return stringValue;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
// No schema definition - try to infer type from string value
|
|
376
|
-
// This handles skill-defined configs that may not be in schema yet
|
|
377
|
-
if (stringValue === 'true' || stringValue === 'false') {
|
|
378
|
-
return stringValue === 'true';
|
|
379
|
-
}
|
|
380
|
-
if (!isNaN(Number(stringValue)) && stringValue.trim() !== '') {
|
|
381
|
-
return Number(stringValue);
|
|
382
|
-
}
|
|
383
|
-
return stringValue;
|
|
384
|
-
}
|
|
385
|
-
/**
|
|
386
|
-
* Unflatten dotted keys into nested structure
|
|
387
|
-
* Example: { "product.alpha.path": "value" } -> { product: { alpha: { path: "value" } } }
|
|
388
|
-
* Converts string values to appropriate types based on config schema
|
|
389
|
-
*/
|
|
390
|
-
export function unflattenConfig(dotted) {
|
|
391
|
-
const result = {};
|
|
392
|
-
const schema = getConfigSchema();
|
|
393
|
-
for (const [dottedKey, stringValue] of Object.entries(dotted)) {
|
|
394
|
-
const parts = dottedKey.split('.');
|
|
395
|
-
const section = parts[0];
|
|
396
|
-
// Initialize section if needed
|
|
397
|
-
result[section] = result[section] ?? {};
|
|
398
|
-
// Build nested structure for this section
|
|
399
|
-
let current = result[section];
|
|
400
|
-
for (let i = 1; i < parts.length - 1; i++) {
|
|
401
|
-
current[parts[i]] = current[parts[i]] ?? {};
|
|
402
|
-
current = current[parts[i]];
|
|
403
|
-
}
|
|
404
|
-
// Convert string value to appropriate type and set
|
|
405
|
-
const typedValue = parseConfigValue(dottedKey, stringValue, schema);
|
|
406
|
-
current[parts[parts.length - 1]] = typedValue;
|
|
407
|
-
}
|
|
408
|
-
return result;
|
|
409
|
-
}
|