prompt-language-shell 0.8.8 → 0.9.2
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 +0 -1
- package/dist/configuration/io.js +22 -1
- package/dist/{services/config-labels.js → configuration/labels.js} +1 -1
- package/dist/configuration/schema.js +2 -2
- package/dist/configuration/steps.js +171 -0
- package/dist/configuration/transformation.js +17 -0
- package/dist/configuration/types.js +3 -4
- package/dist/execution/handlers.js +20 -35
- package/dist/execution/hooks.js +291 -0
- package/dist/execution/processing.js +15 -2
- package/dist/execution/reducer.js +30 -48
- package/dist/execution/runner.js +81 -0
- package/dist/execution/types.js +1 -0
- package/dist/execution/utils.js +28 -0
- package/dist/services/components.js +109 -395
- package/dist/services/filesystem.js +21 -1
- package/dist/services/logger.js +3 -3
- package/dist/services/messages.js +10 -16
- package/dist/services/process.js +7 -2
- package/dist/services/refinement.js +5 -2
- package/dist/services/router.js +120 -67
- package/dist/services/shell.js +179 -10
- package/dist/services/skills.js +2 -1
- package/dist/skills/answer.md +14 -12
- package/dist/skills/execute.md +98 -39
- package/dist/skills/introspect.md +9 -9
- package/dist/skills/schedule.md +0 -6
- package/dist/types/errors.js +47 -0
- package/dist/types/result.js +40 -0
- package/dist/ui/Command.js +11 -7
- package/dist/ui/Component.js +6 -3
- package/dist/ui/Config.js +9 -3
- package/dist/ui/Execute.js +249 -163
- package/dist/ui/Introspect.js +13 -14
- package/dist/ui/List.js +2 -2
- package/dist/ui/Main.js +14 -7
- package/dist/ui/Output.js +54 -0
- package/dist/ui/Schedule.js +3 -1
- package/dist/ui/Subtask.js +6 -3
- package/dist/ui/Task.js +10 -85
- package/dist/ui/Validate.js +26 -21
- package/dist/ui/Workflow.js +21 -4
- package/package.json +1 -1
- package/dist/parser.js +0 -13
- package/dist/services/config-utils.js +0 -20
package/README.md
CHANGED
package/dist/configuration/io.js
CHANGED
|
@@ -61,11 +61,32 @@ export function mergeConfig(existingContent, sectionName, newValues) {
|
|
|
61
61
|
}
|
|
62
62
|
export function saveConfig(section, config, fs = defaultFileSystem) {
|
|
63
63
|
const configFile = getConfigFile();
|
|
64
|
+
const tempFile = `${configFile}.tmp`;
|
|
64
65
|
const existingContent = fs.exists(configFile)
|
|
65
66
|
? fs.readFile(configFile, 'utf-8')
|
|
66
67
|
: '';
|
|
67
68
|
const newContent = mergeConfig(existingContent, section, config);
|
|
68
|
-
|
|
69
|
+
try {
|
|
70
|
+
// Write to temp file first
|
|
71
|
+
fs.writeFile(tempFile, newContent);
|
|
72
|
+
// Validate the temp file can be parsed
|
|
73
|
+
const tempContent = fs.readFile(tempFile, 'utf-8');
|
|
74
|
+
parseYamlConfig(tempContent);
|
|
75
|
+
// Atomic rename (on POSIX systems)
|
|
76
|
+
fs.rename(tempFile, configFile);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
// Clean up temp file if it exists
|
|
80
|
+
if (fs.exists(tempFile)) {
|
|
81
|
+
try {
|
|
82
|
+
fs.remove(tempFile);
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
// Ignore cleanup errors
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
69
90
|
}
|
|
70
91
|
export function saveAnthropicConfig(config, fs = defaultFileSystem) {
|
|
71
92
|
saveConfig('anthropic', config, fs);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import YAML from 'yaml';
|
|
2
2
|
import { AnthropicModel, ConfigDefinitionType, DebugLevel, SUPPORTED_DEBUG_LEVELS, SUPPORTED_MODELS, } from './types.js';
|
|
3
|
-
import { flattenConfig } from '
|
|
4
|
-
import { getConfigLabel } from '
|
|
3
|
+
import { flattenConfig } from './transformation.js';
|
|
4
|
+
import { getConfigLabel } from './labels.js';
|
|
5
5
|
import { defaultFileSystem } from '../services/filesystem.js';
|
|
6
6
|
import { getConfigPath, loadConfig } from './io.js';
|
|
7
7
|
/**
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { parse as parseYaml } from 'yaml';
|
|
2
|
+
import { ConfigDefinitionType } from './types.js';
|
|
3
|
+
import { getConfigPath, loadConfig } from './io.js';
|
|
4
|
+
import { getConfigSchema } from './schema.js';
|
|
5
|
+
import { getConfigLabel } from './labels.js';
|
|
6
|
+
import { defaultFileSystem } from '../services/filesystem.js';
|
|
7
|
+
import { StepType } from '../ui/Config.js';
|
|
8
|
+
export function createConfigSteps() {
|
|
9
|
+
// Use schema-based config step generation for required Anthropic settings
|
|
10
|
+
return createConfigStepsFromSchema(['anthropic.key', 'anthropic.model']);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Get current config value for a dotted key path
|
|
14
|
+
*/
|
|
15
|
+
function getConfigValue(config, key) {
|
|
16
|
+
if (!config)
|
|
17
|
+
return undefined;
|
|
18
|
+
const parts = key.split('.');
|
|
19
|
+
let value = config;
|
|
20
|
+
for (const part of parts) {
|
|
21
|
+
if (value && typeof value === 'object' && part in value) {
|
|
22
|
+
value = value[part];
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get validation function for a config definition
|
|
32
|
+
*/
|
|
33
|
+
function getValidator(definition) {
|
|
34
|
+
switch (definition.type) {
|
|
35
|
+
case ConfigDefinitionType.RegExp:
|
|
36
|
+
return (value) => definition.pattern.test(value);
|
|
37
|
+
case ConfigDefinitionType.String:
|
|
38
|
+
return () => true; // Strings are always valid
|
|
39
|
+
case ConfigDefinitionType.Enum:
|
|
40
|
+
return (value) => definition.values.includes(value);
|
|
41
|
+
case ConfigDefinitionType.Number:
|
|
42
|
+
return (value) => !isNaN(Number(value));
|
|
43
|
+
case ConfigDefinitionType.Boolean:
|
|
44
|
+
return (value) => value === 'true' || value === 'false';
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Create config steps from schema for specified keys
|
|
49
|
+
*/
|
|
50
|
+
export function createConfigStepsFromSchema(keys, fs = defaultFileSystem) {
|
|
51
|
+
const schema = getConfigSchema();
|
|
52
|
+
let currentConfig = null;
|
|
53
|
+
let rawConfig = null;
|
|
54
|
+
// Load validated config (may fail if config has validation errors)
|
|
55
|
+
try {
|
|
56
|
+
currentConfig = loadConfig(fs);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Config doesn't exist or has validation errors, use defaults
|
|
60
|
+
}
|
|
61
|
+
// Load raw config separately (for discovered keys not in schema)
|
|
62
|
+
try {
|
|
63
|
+
const configFile = getConfigPath();
|
|
64
|
+
if (fs.exists(configFile)) {
|
|
65
|
+
const content = fs.readFile(configFile, 'utf-8');
|
|
66
|
+
rawConfig = parseYaml(content);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
// Config file doesn't exist or can't be parsed
|
|
71
|
+
}
|
|
72
|
+
return keys.map((key) => {
|
|
73
|
+
// Check if key is in schema (system config)
|
|
74
|
+
if (!(key in schema)) {
|
|
75
|
+
// Key is not in schema - it's from a skill or discovered config
|
|
76
|
+
// Create a simple text step with cached label or full path as description
|
|
77
|
+
const keyParts = key.split('.');
|
|
78
|
+
const shortKey = keyParts[keyParts.length - 1];
|
|
79
|
+
// Load current value if it exists (use rawConfig since discovered keys aren't in validated config)
|
|
80
|
+
const currentValue = getConfigValue(rawConfig, key);
|
|
81
|
+
const value = currentValue !== undefined && typeof currentValue === 'string'
|
|
82
|
+
? currentValue
|
|
83
|
+
: null;
|
|
84
|
+
// Use cached label if available, fallback to key path
|
|
85
|
+
const cachedLabel = getConfigLabel(key, fs);
|
|
86
|
+
return {
|
|
87
|
+
description: cachedLabel ?? key,
|
|
88
|
+
key: shortKey,
|
|
89
|
+
path: key,
|
|
90
|
+
type: StepType.Text,
|
|
91
|
+
value,
|
|
92
|
+
validate: () => true, // Accept any string for now
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const definition = schema[key];
|
|
96
|
+
const currentValue = getConfigValue(currentConfig, key);
|
|
97
|
+
const keyParts = key.split('.');
|
|
98
|
+
const shortKey = keyParts[keyParts.length - 1];
|
|
99
|
+
// Map definition to ConfigStep based on type
|
|
100
|
+
switch (definition.type) {
|
|
101
|
+
case ConfigDefinitionType.RegExp:
|
|
102
|
+
case ConfigDefinitionType.String: {
|
|
103
|
+
const value = currentValue !== undefined && typeof currentValue === 'string'
|
|
104
|
+
? currentValue
|
|
105
|
+
: definition.type === ConfigDefinitionType.String
|
|
106
|
+
? (definition.default ?? '')
|
|
107
|
+
: null;
|
|
108
|
+
return {
|
|
109
|
+
description: definition.description,
|
|
110
|
+
key: shortKey,
|
|
111
|
+
path: key,
|
|
112
|
+
type: StepType.Text,
|
|
113
|
+
value,
|
|
114
|
+
validate: getValidator(definition),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
case ConfigDefinitionType.Number: {
|
|
118
|
+
const value = currentValue !== undefined && typeof currentValue === 'number'
|
|
119
|
+
? String(currentValue)
|
|
120
|
+
: definition.default !== undefined
|
|
121
|
+
? String(definition.default)
|
|
122
|
+
: '0';
|
|
123
|
+
return {
|
|
124
|
+
description: definition.description,
|
|
125
|
+
key: shortKey,
|
|
126
|
+
path: key,
|
|
127
|
+
type: StepType.Text,
|
|
128
|
+
value,
|
|
129
|
+
validate: getValidator(definition),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
case ConfigDefinitionType.Enum: {
|
|
133
|
+
const currentStr = currentValue !== undefined && typeof currentValue === 'string'
|
|
134
|
+
? currentValue
|
|
135
|
+
: definition.default;
|
|
136
|
+
const defaultIndex = currentStr
|
|
137
|
+
? definition.values.indexOf(currentStr)
|
|
138
|
+
: 0;
|
|
139
|
+
return {
|
|
140
|
+
description: definition.description,
|
|
141
|
+
key: shortKey,
|
|
142
|
+
path: key,
|
|
143
|
+
type: StepType.Selection,
|
|
144
|
+
options: definition.values.map((value) => ({
|
|
145
|
+
label: value,
|
|
146
|
+
value,
|
|
147
|
+
})),
|
|
148
|
+
defaultIndex: Math.max(0, defaultIndex),
|
|
149
|
+
validate: getValidator(definition),
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
case ConfigDefinitionType.Boolean: {
|
|
153
|
+
const currentBool = currentValue !== undefined && typeof currentValue === 'boolean'
|
|
154
|
+
? currentValue
|
|
155
|
+
: undefined;
|
|
156
|
+
return {
|
|
157
|
+
description: definition.description,
|
|
158
|
+
key: shortKey,
|
|
159
|
+
path: key,
|
|
160
|
+
type: StepType.Selection,
|
|
161
|
+
options: [
|
|
162
|
+
{ label: 'yes', value: 'true' },
|
|
163
|
+
{ label: 'no', value: 'false' },
|
|
164
|
+
],
|
|
165
|
+
defaultIndex: currentBool !== undefined ? (currentBool ? 0 : 1) : 0,
|
|
166
|
+
validate: getValidator(definition),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
}
|
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
import { ConfigDefinitionType } from './types.js';
|
|
2
2
|
import { getConfigSchema } from './schema.js';
|
|
3
|
+
/**
|
|
4
|
+
* Flatten nested config object to dot notation
|
|
5
|
+
* Example: { a: { b: 1 } } => { 'a.b': 1 }
|
|
6
|
+
*/
|
|
7
|
+
export function flattenConfig(obj, prefix = '') {
|
|
8
|
+
const result = {};
|
|
9
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
10
|
+
const fullKey = prefix ? `${prefix}.${key}` : key;
|
|
11
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
12
|
+
Object.assign(result, flattenConfig(value, fullKey));
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
result[fullKey] = value;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
3
20
|
/**
|
|
4
21
|
* Convert string value to appropriate type based on schema definition
|
|
5
22
|
*/
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { AppError, ErrorCode } from '../types/errors.js';
|
|
1
2
|
export var AnthropicModel;
|
|
2
3
|
(function (AnthropicModel) {
|
|
3
4
|
AnthropicModel["Sonnet"] = "claude-sonnet-4-5";
|
|
@@ -20,11 +21,9 @@ export var ConfigDefinitionType;
|
|
|
20
21
|
ConfigDefinitionType["Number"] = "number";
|
|
21
22
|
ConfigDefinitionType["Boolean"] = "boolean";
|
|
22
23
|
})(ConfigDefinitionType || (ConfigDefinitionType = {}));
|
|
23
|
-
export class ConfigError extends
|
|
24
|
-
origin;
|
|
24
|
+
export class ConfigError extends AppError {
|
|
25
25
|
constructor(message, origin) {
|
|
26
|
-
super(message);
|
|
26
|
+
super(message, ErrorCode.MissingConfig, origin);
|
|
27
27
|
this.name = 'ConfigError';
|
|
28
|
-
this.origin = origin;
|
|
29
28
|
}
|
|
30
29
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { ExecutionStatus } from '../services/shell.js';
|
|
2
2
|
import { formatDuration } from '../services/utils.js';
|
|
3
3
|
import { ExecuteActionType, } from './types.js';
|
|
4
|
+
import { getTotalElapsed } from './utils.js';
|
|
4
5
|
/**
|
|
5
6
|
* Handles task completion logic and returns the appropriate action and state.
|
|
6
7
|
*/
|
|
7
8
|
export function handleTaskCompletion(index, elapsed, context) {
|
|
8
|
-
const {
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
if (index < taskInfos.length - 1) {
|
|
9
|
+
const { tasks, message, summary } = context;
|
|
10
|
+
const updatedTaskInfos = tasks.map((task, i) => i === index ? { ...task, status: ExecutionStatus.Success, elapsed } : task);
|
|
11
|
+
if (index < tasks.length - 1) {
|
|
12
12
|
// More tasks to execute
|
|
13
13
|
return {
|
|
14
14
|
action: {
|
|
@@ -18,9 +18,7 @@ export function handleTaskCompletion(index, elapsed, context) {
|
|
|
18
18
|
finalState: {
|
|
19
19
|
message,
|
|
20
20
|
summary,
|
|
21
|
-
|
|
22
|
-
completed: index + 1,
|
|
23
|
-
taskExecutionTimes: updatedTimes,
|
|
21
|
+
tasks: updatedTaskInfos,
|
|
24
22
|
completionMessage: null,
|
|
25
23
|
error: null,
|
|
26
24
|
},
|
|
@@ -29,7 +27,7 @@ export function handleTaskCompletion(index, elapsed, context) {
|
|
|
29
27
|
}
|
|
30
28
|
// All tasks complete
|
|
31
29
|
const summaryText = summary.trim() || 'Execution completed';
|
|
32
|
-
const totalElapsed =
|
|
30
|
+
const totalElapsed = getTotalElapsed(updatedTaskInfos);
|
|
33
31
|
const completion = `${summaryText} in ${formatDuration(totalElapsed)}.`;
|
|
34
32
|
return {
|
|
35
33
|
action: {
|
|
@@ -39,9 +37,7 @@ export function handleTaskCompletion(index, elapsed, context) {
|
|
|
39
37
|
finalState: {
|
|
40
38
|
message,
|
|
41
39
|
summary,
|
|
42
|
-
|
|
43
|
-
completed: index + 1,
|
|
44
|
-
taskExecutionTimes: updatedTimes,
|
|
40
|
+
tasks: updatedTaskInfos,
|
|
45
41
|
completionMessage: completion,
|
|
46
42
|
error: null,
|
|
47
43
|
},
|
|
@@ -52,10 +48,10 @@ export function handleTaskCompletion(index, elapsed, context) {
|
|
|
52
48
|
* Handles task error logic and returns the appropriate action and state.
|
|
53
49
|
*/
|
|
54
50
|
export function handleTaskFailure(index, error, elapsed, context) {
|
|
55
|
-
const {
|
|
56
|
-
const task =
|
|
51
|
+
const { tasks, message, summary } = context;
|
|
52
|
+
const task = tasks[index];
|
|
57
53
|
const isCritical = task.command.critical !== false; // Default to true
|
|
58
|
-
const updatedTaskInfos =
|
|
54
|
+
const updatedTaskInfos = tasks.map((task, i) => i === index ? { ...task, status: ExecutionStatus.Failed, elapsed } : task);
|
|
59
55
|
if (isCritical) {
|
|
60
56
|
// Critical failure - stop execution
|
|
61
57
|
return {
|
|
@@ -66,19 +62,15 @@ export function handleTaskFailure(index, error, elapsed, context) {
|
|
|
66
62
|
finalState: {
|
|
67
63
|
message,
|
|
68
64
|
summary,
|
|
69
|
-
|
|
70
|
-
completed: index + 1,
|
|
71
|
-
taskExecutionTimes,
|
|
65
|
+
tasks: updatedTaskInfos,
|
|
72
66
|
completionMessage: null,
|
|
73
|
-
error,
|
|
67
|
+
error: null,
|
|
74
68
|
},
|
|
75
69
|
shouldComplete: true,
|
|
76
|
-
shouldReportError: true,
|
|
77
70
|
};
|
|
78
71
|
}
|
|
79
72
|
// Non-critical failure - continue to next task
|
|
80
|
-
|
|
81
|
-
if (index < taskInfos.length - 1) {
|
|
73
|
+
if (index < tasks.length - 1) {
|
|
82
74
|
return {
|
|
83
75
|
action: {
|
|
84
76
|
type: ExecuteActionType.TaskErrorContinue,
|
|
@@ -87,19 +79,17 @@ export function handleTaskFailure(index, error, elapsed, context) {
|
|
|
87
79
|
finalState: {
|
|
88
80
|
message,
|
|
89
81
|
summary,
|
|
90
|
-
|
|
91
|
-
completed: index + 1,
|
|
92
|
-
taskExecutionTimes: updatedTimes,
|
|
82
|
+
tasks: updatedTaskInfos,
|
|
93
83
|
completionMessage: null,
|
|
94
84
|
error: null,
|
|
95
85
|
},
|
|
96
86
|
shouldComplete: false,
|
|
97
|
-
shouldReportError: false,
|
|
98
87
|
};
|
|
99
88
|
}
|
|
100
|
-
// Last task, complete execution
|
|
89
|
+
// Last task failed (non-critical), complete execution
|
|
90
|
+
// Non-critical failures still show completion message with summary
|
|
101
91
|
const summaryText = summary.trim() || 'Execution completed';
|
|
102
|
-
const totalElapsed =
|
|
92
|
+
const totalElapsed = getTotalElapsed(updatedTaskInfos);
|
|
103
93
|
const completion = `${summaryText} in ${formatDuration(totalElapsed)}.`;
|
|
104
94
|
return {
|
|
105
95
|
action: {
|
|
@@ -109,26 +99,21 @@ export function handleTaskFailure(index, error, elapsed, context) {
|
|
|
109
99
|
finalState: {
|
|
110
100
|
message,
|
|
111
101
|
summary,
|
|
112
|
-
|
|
113
|
-
completed: index + 1,
|
|
114
|
-
taskExecutionTimes: updatedTimes,
|
|
102
|
+
tasks: updatedTaskInfos,
|
|
115
103
|
completionMessage: completion,
|
|
116
104
|
error: null,
|
|
117
105
|
},
|
|
118
106
|
shouldComplete: true,
|
|
119
|
-
shouldReportError: false,
|
|
120
107
|
};
|
|
121
108
|
}
|
|
122
109
|
/**
|
|
123
110
|
* Builds final state for task abortion.
|
|
124
111
|
*/
|
|
125
|
-
export function buildAbortedState(
|
|
112
|
+
export function buildAbortedState(tasks, message, summary) {
|
|
126
113
|
return {
|
|
127
114
|
message,
|
|
128
115
|
summary,
|
|
129
|
-
|
|
130
|
-
completed,
|
|
131
|
-
taskExecutionTimes,
|
|
116
|
+
tasks,
|
|
132
117
|
completionMessage: null,
|
|
133
118
|
error: null,
|
|
134
119
|
};
|