prompt-language-shell 0.9.4 → 0.9.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/README.md +37 -6
- package/dist/components/Workflow.js +12 -1
- package/dist/components/controllers/Execute.js +3 -3
- package/dist/components/controllers/Schedule.js +6 -11
- package/dist/components/views/Debug.js +6 -1
- package/dist/components/views/Feedback.js +2 -13
- package/dist/components/views/Output.js +10 -8
- package/dist/components/views/Schedule.js +4 -3
- package/dist/components/views/Table.js +15 -0
- package/dist/configuration/io.js +10 -0
- package/dist/configuration/schema.js +6 -0
- package/dist/configuration/validation.js +5 -0
- package/dist/execution/processing.js +45 -14
- package/dist/execution/runner.js +1 -1
- package/dist/index.js +2 -0
- package/dist/services/anthropic.js +27 -31
- package/dist/services/colors.js +2 -1
- package/dist/services/filesystem.js +13 -1
- package/dist/services/logger.js +254 -21
- package/dist/services/messages.js +7 -4
- package/dist/services/monitor.js +288 -0
- package/dist/services/parser.js +13 -5
- package/dist/services/performance.js +14 -0
- package/dist/services/refinement.js +14 -11
- package/dist/services/router.js +159 -122
- package/dist/services/shell.js +32 -14
- package/dist/services/skills.js +35 -7
- package/dist/skills/execute.md +82 -3
- package/dist/skills/schedule.md +155 -0
- package/dist/tools/schedule.tool.js +1 -1
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ Here's what I can help with:
|
|
|
31
31
|
- Configure - manage and configure system settings
|
|
32
32
|
- Answer - respond to questions and provide information
|
|
33
33
|
- Execute - run shell commands and process operations
|
|
34
|
-
|
|
34
|
+
```
|
|
35
35
|
|
|
36
36
|
Skills are custom workflows you can define to teach `pls` about your specific
|
|
37
37
|
projects and commands. Once defined, you can use them naturally:
|
|
@@ -87,16 +87,47 @@ commands your environment requires.
|
|
|
87
87
|
|
|
88
88
|
## Configuration
|
|
89
89
|
|
|
90
|
-
Your configuration is stored in `~/.plsrc` as a YAML file
|
|
90
|
+
Your configuration is stored in `~/.plsrc` as a YAML file:
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
# Mandatory
|
|
94
|
+
anthropic:
|
|
95
|
+
key: sk-ant-...
|
|
96
|
+
model: claude-...
|
|
97
|
+
|
|
98
|
+
# Optional
|
|
99
|
+
settings:
|
|
100
|
+
memory: 1024 # Child process memory limit (MB)
|
|
101
|
+
debug: none # none | info | verbose
|
|
102
|
+
|
|
103
|
+
# Custom
|
|
104
|
+
project:
|
|
105
|
+
path: ~/projects/app
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Skills can define their own configuration properties via a `Config` section. When
|
|
109
|
+
a skill requires config values that don't exist, `pls` prompts you to provide
|
|
110
|
+
them before execution. See [Skills](#skills) for details.
|
|
111
|
+
|
|
112
|
+
## Reference
|
|
113
|
+
|
|
114
|
+
### Debug Mode
|
|
115
|
+
Press `Shift+Tab` during execution to cycle through debug levels
|
|
116
|
+
(none → info → verbose).
|
|
117
|
+
Logs are saved to `~/.pls/logs/` when debug is `info` or `verbose`.
|
|
91
118
|
|
|
92
|
-
|
|
93
|
-
|
|
119
|
+
### Data Locations
|
|
120
|
+
```
|
|
121
|
+
~/.plsrc # Configuration
|
|
122
|
+
~/.pls/skills/ # Custom skills
|
|
123
|
+
~/.pls/logs/ # Debug logs
|
|
124
|
+
```
|
|
94
125
|
|
|
95
126
|
## Skills
|
|
96
127
|
|
|
97
128
|
Skills let you teach `pls` about your project-specific workflows. Create
|
|
98
|
-
markdown files in `~/.pls/skills/` to define custom operations that
|
|
99
|
-
understand and execute.
|
|
129
|
+
markdown files in `~/.pls/skills/` to define custom operations that
|
|
130
|
+
`pls` can understand and execute.
|
|
100
131
|
|
|
101
132
|
For complete documentation, see [docs/SKILLS.md](./docs/SKILLS.md).
|
|
102
133
|
|
|
@@ -107,7 +107,18 @@ export const Workflow = ({ initialQueue, debug }) => {
|
|
|
107
107
|
setQueue((queue) => [...queue, ...items]);
|
|
108
108
|
},
|
|
109
109
|
addToTimeline: (...items) => {
|
|
110
|
-
|
|
110
|
+
// Flush pending to timeline first, then add new items
|
|
111
|
+
// Both are added in a SINGLE setTimeline call to guarantee order
|
|
112
|
+
setCurrent((curr) => {
|
|
113
|
+
const { active, pending } = curr;
|
|
114
|
+
if (pending) {
|
|
115
|
+
const donePending = markAsDone(pending);
|
|
116
|
+
setTimeline((prev) => [...prev, donePending, ...items]);
|
|
117
|
+
return { active, pending: null };
|
|
118
|
+
}
|
|
119
|
+
setTimeline((prev) => [...prev, ...items]);
|
|
120
|
+
return curr;
|
|
121
|
+
});
|
|
111
122
|
},
|
|
112
123
|
}), []);
|
|
113
124
|
// Global Esc handler removed - components handle their own Esc individually
|
|
@@ -139,9 +139,9 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
|
|
|
139
139
|
lifecycleHandlers.completeActive();
|
|
140
140
|
return;
|
|
141
141
|
}
|
|
142
|
-
// Create task data from commands
|
|
143
|
-
const tasks = result.commands.map((cmd
|
|
144
|
-
label:
|
|
142
|
+
// Create task data from commands - use descriptions from execute response
|
|
143
|
+
const tasks = result.commands.map((cmd) => ({
|
|
144
|
+
label: cmd.description,
|
|
145
145
|
command: cmd,
|
|
146
146
|
status: ExecutionStatus.Pending,
|
|
147
147
|
elapsed: 0,
|
|
@@ -37,8 +37,7 @@ export function Schedule({ message, tasks, status, debug = DebugLevel.None, requ
|
|
|
37
37
|
completedSelections,
|
|
38
38
|
};
|
|
39
39
|
requestHandlers.onCompleted(finalState);
|
|
40
|
-
//
|
|
41
|
-
// Callback will create a new Plan showing refined tasks (pending) + Confirm (active)
|
|
40
|
+
// Move Schedule to pending - callback will flush to timeline
|
|
42
41
|
lifecycleHandlers.completeActive();
|
|
43
42
|
void onSelectionConfirmed(concreteTasks);
|
|
44
43
|
}
|
|
@@ -102,9 +101,9 @@ export function Schedule({ message, tasks, status, debug = DebugLevel.None, requ
|
|
|
102
101
|
const options = task.params.options;
|
|
103
102
|
const selectedIndex = newCompletedSelections[defineGroupIndex];
|
|
104
103
|
const selectedOption = options[selectedIndex];
|
|
105
|
-
// Use
|
|
104
|
+
// Use the command from the selected option
|
|
106
105
|
refinedTasks.push({
|
|
107
|
-
action: selectedOption,
|
|
106
|
+
action: selectedOption.command,
|
|
108
107
|
type: TaskType.Execute,
|
|
109
108
|
config: [],
|
|
110
109
|
});
|
|
@@ -122,16 +121,12 @@ export function Schedule({ message, tasks, status, debug = DebugLevel.None, requ
|
|
|
122
121
|
completedSelections: newCompletedSelections,
|
|
123
122
|
};
|
|
124
123
|
requestHandlers.onCompleted(finalState);
|
|
124
|
+
// Move Schedule to pending - refinement will flush it to timeline
|
|
125
|
+
// before adding Command, ensuring correct order
|
|
126
|
+
lifecycleHandlers.completeActive();
|
|
125
127
|
if (onSelectionConfirmed) {
|
|
126
|
-
// Complete the selection phase - it goes to timeline
|
|
127
|
-
// Callback will create a new Plan showing refined tasks (pending) + Confirm (active)
|
|
128
|
-
lifecycleHandlers.completeActive();
|
|
129
128
|
void onSelectionConfirmed(refinedTasks);
|
|
130
129
|
}
|
|
131
|
-
else {
|
|
132
|
-
// No selection callback, just complete normally
|
|
133
|
-
lifecycleHandlers.completeActive();
|
|
134
|
-
}
|
|
135
130
|
}
|
|
136
131
|
}
|
|
137
132
|
}, { isActive: isActive && defineTask !== null });
|
|
@@ -3,5 +3,10 @@ import { Box, Text } from 'ink';
|
|
|
3
3
|
const CONTENT_WIDTH = 80;
|
|
4
4
|
const HORIZONTAL_PADDING = 2;
|
|
5
5
|
export const Debug = ({ title, content, color }) => {
|
|
6
|
-
|
|
6
|
+
// Plain text content - single bordered box
|
|
7
|
+
if (typeof content === 'string') {
|
|
8
|
+
return (_jsxs(Box, { flexDirection: "column", paddingX: HORIZONTAL_PADDING, paddingY: 1, borderStyle: "single", borderColor: color, width: CONTENT_WIDTH, children: [_jsx(Text, { color: color, wrap: "wrap", children: title }), _jsx(Text, { color: color, wrap: "wrap", children: content })] }));
|
|
9
|
+
}
|
|
10
|
+
// Array content - table with one column, each item in bordered row
|
|
11
|
+
return (_jsxs(Box, { flexDirection: "column", width: CONTENT_WIDTH, children: [_jsx(Box, { paddingX: HORIZONTAL_PADDING, paddingY: 1, borderStyle: "single", borderColor: color, width: CONTENT_WIDTH, children: _jsx(Text, { color: color, wrap: "wrap", children: title }) }), content.map((section, index) => (_jsx(Box, { paddingX: HORIZONTAL_PADDING, paddingY: 1, borderStyle: "single", borderColor: color, width: CONTENT_WIDTH, marginTop: -1, children: _jsx(Text, { color: color, wrap: "wrap", children: section }) }, index)))] }));
|
|
7
12
|
};
|
|
@@ -1,19 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import { ComponentStatus } from '../../types/components.js';
|
|
4
|
-
import { FeedbackType } from '../../types/types.js';
|
|
5
4
|
import { getFeedbackColor } from '../../services/colors.js';
|
|
6
|
-
function getSymbol(type) {
|
|
7
|
-
return {
|
|
8
|
-
[FeedbackType.Info]: 'ℹ',
|
|
9
|
-
[FeedbackType.Warning]: '⚠',
|
|
10
|
-
[FeedbackType.Succeeded]: '✓',
|
|
11
|
-
[FeedbackType.Aborted]: '⊘',
|
|
12
|
-
[FeedbackType.Failed]: '✗',
|
|
13
|
-
}[type];
|
|
14
|
-
}
|
|
15
5
|
export function Feedback({ type, message }) {
|
|
16
6
|
const color = getFeedbackColor(type, ComponentStatus.Done);
|
|
17
|
-
|
|
18
|
-
return (_jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: color, children: [symbol, " ", message] }) }));
|
|
7
|
+
return (_jsx(Box, { marginLeft: 1, children: _jsx(Text, { color: color, children: message }) }));
|
|
19
8
|
}
|
|
@@ -4,15 +4,21 @@ import { Palette } from '../../services/colors.js';
|
|
|
4
4
|
import { ExecutionStatus } from '../../services/shell.js';
|
|
5
5
|
const MAX_LINES = 8;
|
|
6
6
|
const MAX_WIDTH = 75;
|
|
7
|
-
const SHORT_OUTPUT_THRESHOLD = 4;
|
|
8
7
|
const MINIMAL_INFO_THRESHOLD = 2;
|
|
9
8
|
/**
|
|
10
|
-
* Get the last N lines from text, filtering out empty/whitespace-only lines
|
|
9
|
+
* Get the last N lines from text, filtering out empty/whitespace-only lines.
|
|
10
|
+
* Handles carriage returns used in progress output by keeping only the
|
|
11
|
+
* content after the last \r in each line.
|
|
11
12
|
*/
|
|
12
13
|
export function getLastLines(text, maxLines = MAX_LINES) {
|
|
13
14
|
const lines = text
|
|
14
15
|
.trim()
|
|
15
16
|
.split(/\r?\n/)
|
|
17
|
+
.map((line) => {
|
|
18
|
+
// Handle carriage returns: keep only content after the last \r
|
|
19
|
+
const lastCR = line.lastIndexOf('\r');
|
|
20
|
+
return lastCR >= 0 ? line.slice(lastCR + 1) : line;
|
|
21
|
+
})
|
|
16
22
|
.filter((line) => line.trim().length > 0);
|
|
17
23
|
return lines.length <= maxLines ? lines : lines.slice(-maxLines);
|
|
18
24
|
}
|
|
@@ -29,9 +35,6 @@ export function computeDisplayConfig(stdout, stderr, status, isFinished) {
|
|
|
29
35
|
const stderrLines = hasStderr ? getLastLines(stderr) : [];
|
|
30
36
|
// Show stdout if no stderr, or if stderr is minimal (provides context)
|
|
31
37
|
const showStdout = hasStdout && (!hasStderr || stderrLines.length <= MINIMAL_INFO_THRESHOLD);
|
|
32
|
-
// Use word wrapping for short outputs to show more detail
|
|
33
|
-
const totalLines = stdoutLines.length + stderrLines.length;
|
|
34
|
-
const wrapMode = totalLines <= SHORT_OUTPUT_THRESHOLD ? 'wrap' : 'truncate-end';
|
|
35
38
|
// Darker colors for finished tasks
|
|
36
39
|
const baseColor = isFinished ? Palette.DarkGray : Palette.Gray;
|
|
37
40
|
const stderrColor = status === ExecutionStatus.Failed ? Palette.Yellow : baseColor;
|
|
@@ -39,7 +42,6 @@ export function computeDisplayConfig(stdout, stderr, status, isFinished) {
|
|
|
39
42
|
stdoutLines,
|
|
40
43
|
stderrLines,
|
|
41
44
|
showStdout,
|
|
42
|
-
wrapMode,
|
|
43
45
|
stdoutColor: baseColor,
|
|
44
46
|
stderrColor,
|
|
45
47
|
};
|
|
@@ -48,7 +50,7 @@ export function Output({ stdout, stderr, status, isFinished }) {
|
|
|
48
50
|
const config = computeDisplayConfig(stdout, stderr, status, isFinished ?? false);
|
|
49
51
|
if (!config)
|
|
50
52
|
return null;
|
|
51
|
-
const { stdoutLines, stderrLines, showStdout,
|
|
53
|
+
const { stdoutLines, stderrLines, showStdout, stdoutColor, stderrColor } = config;
|
|
52
54
|
return (_jsxs(Box, { marginTop: 1, marginLeft: 5, flexDirection: "column", width: MAX_WIDTH, children: [showStdout &&
|
|
53
|
-
stdoutLines.map((line, index) => (_jsx(Text, { color: stdoutColor, wrap:
|
|
55
|
+
stdoutLines.map((line, index) => (_jsx(Text, { color: stdoutColor, wrap: "wrap", children: line }, `out-${index}`))), stderrLines.map((line, index) => (_jsx(Text, { color: stderrColor, wrap: "wrap", children: line }, `err-${index}`)))] }));
|
|
54
56
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Box } from 'ink';
|
|
3
3
|
import { ComponentStatus } from '../../types/components.js';
|
|
4
|
-
import { TaskType } from '../../types/types.js';
|
|
4
|
+
import { TaskType, } from '../../types/types.js';
|
|
5
5
|
import { DebugLevel } from '../../configuration/types.js';
|
|
6
6
|
import { getTaskColors, getTaskTypeLabel, Palette, } from '../../services/colors.js';
|
|
7
7
|
import { Label } from './Label.js';
|
|
@@ -28,7 +28,8 @@ export function taskToListItem(task, highlightedChildIndex = null, isDefineTaskW
|
|
|
28
28
|
}
|
|
29
29
|
// Add children for Define tasks with options
|
|
30
30
|
if (task.type === TaskType.Define && Array.isArray(task.params?.options)) {
|
|
31
|
-
|
|
31
|
+
const options = task.params.options;
|
|
32
|
+
item.children = options.map((option, index) => {
|
|
32
33
|
// Determine the type based on selection state
|
|
33
34
|
let childType = TaskType.Select;
|
|
34
35
|
if (highlightedChildIndex !== null) {
|
|
@@ -40,7 +41,7 @@ export function taskToListItem(task, highlightedChildIndex = null, isDefineTaskW
|
|
|
40
41
|
const planColors = getTaskColors(TaskType.Schedule, status);
|
|
41
42
|
return {
|
|
42
43
|
description: {
|
|
43
|
-
text: option,
|
|
44
|
+
text: option.name,
|
|
44
45
|
color: colors.description,
|
|
45
46
|
highlightedColor: planColors.description,
|
|
46
47
|
},
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
const DEFAULT_WIDTH = 80;
|
|
5
|
+
const DEFAULT_PADDING = 1;
|
|
6
|
+
export const Cell = ({ children, padding = DEFAULT_PADDING }) => (_jsx(Box, { paddingX: padding, children: _jsx(Text, { wrap: "wrap", children: children }) }));
|
|
7
|
+
export const Column = ({ children, width }) => (_jsx(Box, { flexDirection: "column", width: width, children: children }));
|
|
8
|
+
const Row = ({ children, color, innerWidth }) => (_jsxs(Box, { children: [_jsx(Text, { color: color, children: '│' }), _jsx(Box, { width: innerWidth, children: children }), _jsx(Text, { color: color, children: '│' })] }));
|
|
9
|
+
export const Table = ({ data, width = DEFAULT_WIDTH, color }) => {
|
|
10
|
+
const innerWidth = width - 2;
|
|
11
|
+
const TOP = '┌' + '─'.repeat(innerWidth) + '┐';
|
|
12
|
+
const DIV = '├' + '─'.repeat(innerWidth) + '┤';
|
|
13
|
+
const BOT = '└' + '─'.repeat(innerWidth) + '┘';
|
|
14
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: color, children: TOP }), data.map((content, index) => (_jsxs(React.Fragment, { children: [index > 0 && _jsx(Text, { color: color, children: DIV }), _jsx(Row, { color: color, innerWidth: innerWidth, children: _jsx(Cell, { children: content }) })] }, index))), _jsx(Text, { color: color, children: BOT })] }));
|
|
15
|
+
};
|
package/dist/configuration/io.js
CHANGED
|
@@ -104,3 +104,13 @@ export function loadDebugSetting(fs = defaultFileSystem) {
|
|
|
104
104
|
return DebugLevel.None;
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
|
+
const DEFAULT_MEMORY_LIMIT = 1024;
|
|
108
|
+
export function loadMemorySetting(fs = defaultFileSystem) {
|
|
109
|
+
try {
|
|
110
|
+
const config = loadConfig(fs);
|
|
111
|
+
return config.settings?.memory ?? DEFAULT_MEMORY_LIMIT;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
return DEFAULT_MEMORY_LIMIT;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -38,6 +38,12 @@ const coreConfigSchema = {
|
|
|
38
38
|
default: DebugLevel.None,
|
|
39
39
|
description: 'Debug mode',
|
|
40
40
|
},
|
|
41
|
+
'settings.memory': {
|
|
42
|
+
type: ConfigDefinitionType.Number,
|
|
43
|
+
required: false,
|
|
44
|
+
default: 1024,
|
|
45
|
+
description: 'Child process memory limit (MB)',
|
|
46
|
+
},
|
|
41
47
|
};
|
|
42
48
|
/**
|
|
43
49
|
* Get complete configuration schema
|
|
@@ -37,6 +37,11 @@ export function validateConfig(parsed) {
|
|
|
37
37
|
validatedConfig.settings.debug = settings.debug;
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
+
if ('memory' in settings) {
|
|
41
|
+
if (typeof settings.memory === 'number' && settings.memory > 0) {
|
|
42
|
+
validatedConfig.settings.memory = settings.memory;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
40
45
|
}
|
|
41
46
|
return validatedConfig;
|
|
42
47
|
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { stringify } from 'yaml';
|
|
2
|
+
import { loadMemorySetting } from '../configuration/io.js';
|
|
1
3
|
import { loadUserConfig } from '../services/loader.js';
|
|
2
4
|
import { replacePlaceholders } from '../services/resolver.js';
|
|
3
5
|
import { validatePlaceholderResolution } from './validation.js';
|
|
@@ -10,6 +12,36 @@ export function fixEscapedQuotes(command) {
|
|
|
10
12
|
// Replace ="value" with =\"value\"
|
|
11
13
|
return command.replace(/="([^"]*)"/g, '=\\"$1\\"');
|
|
12
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* Format a task as YAML with action line and metadata block
|
|
17
|
+
*/
|
|
18
|
+
export function formatTaskAsYaml(action, metadata, indent = '') {
|
|
19
|
+
const normalizedAction = action.charAt(0).toLowerCase() + action.slice(1);
|
|
20
|
+
if (!metadata || Object.keys(metadata).length === 0) {
|
|
21
|
+
return normalizedAction;
|
|
22
|
+
}
|
|
23
|
+
const metadataYaml = stringify({ metadata })
|
|
24
|
+
.trim()
|
|
25
|
+
.split('\n')
|
|
26
|
+
.map((line) => `${indent}${line}`)
|
|
27
|
+
.join('\n');
|
|
28
|
+
return `${normalizedAction}\n\n${metadataYaml}`;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Build task descriptions for the LLM
|
|
32
|
+
* Single task: use as-is; multiple tasks: add header and bullet prefix
|
|
33
|
+
*/
|
|
34
|
+
function buildTaskDescriptions(resolvedTasks) {
|
|
35
|
+
if (resolvedTasks.length === 1) {
|
|
36
|
+
const { action, params } = resolvedTasks[0];
|
|
37
|
+
return formatTaskAsYaml(action, params);
|
|
38
|
+
}
|
|
39
|
+
const header = `complete these ${resolvedTasks.length} tasks:`;
|
|
40
|
+
const bulletedTasks = resolvedTasks
|
|
41
|
+
.map(({ action, params }) => `- ${formatTaskAsYaml(action, params, ' ')}`)
|
|
42
|
+
.join('\n\n');
|
|
43
|
+
return `${header}\n\n${bulletedTasks}`;
|
|
44
|
+
}
|
|
13
45
|
/**
|
|
14
46
|
* Processes tasks through the AI service to generate executable commands.
|
|
15
47
|
* Resolves placeholders in task descriptions and validates the results.
|
|
@@ -17,27 +49,26 @@ export function fixEscapedQuotes(command) {
|
|
|
17
49
|
export async function processTasks(tasks, service) {
|
|
18
50
|
// Load user config for placeholder resolution
|
|
19
51
|
const userConfig = loadUserConfig();
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
return `- ${resolvedAction}${params}`;
|
|
28
|
-
})
|
|
29
|
-
.join('\n');
|
|
30
|
-
// Build message with confirmed schedule header
|
|
31
|
-
const taskDescriptions = `Confirmed schedule (${tasks.length} tasks):\n${taskList}`;
|
|
52
|
+
const memoryLimitMB = loadMemorySetting();
|
|
53
|
+
// Resolve placeholders in task actions
|
|
54
|
+
const resolvedTasks = tasks.map((task) => ({
|
|
55
|
+
action: replacePlaceholders(task.action, userConfig),
|
|
56
|
+
params: task.params,
|
|
57
|
+
}));
|
|
58
|
+
const taskDescriptions = buildTaskDescriptions(resolvedTasks);
|
|
32
59
|
// Call execute tool to get commands
|
|
33
60
|
const result = await service.processWithTool(taskDescriptions, 'execute');
|
|
34
|
-
// Resolve placeholders in command strings
|
|
61
|
+
// Resolve placeholders in command strings and inject memory limit
|
|
35
62
|
const resolvedCommands = (result.commands || []).map((cmd) => {
|
|
36
63
|
// Fix escaped quotes lost in JSON parsing
|
|
37
64
|
const fixed = fixEscapedQuotes(cmd.command);
|
|
38
65
|
const resolved = replacePlaceholders(fixed, userConfig);
|
|
39
66
|
validatePlaceholderResolution(resolved);
|
|
40
|
-
return {
|
|
67
|
+
return {
|
|
68
|
+
...cmd,
|
|
69
|
+
command: resolved,
|
|
70
|
+
memoryLimit: memoryLimitMB,
|
|
71
|
+
};
|
|
41
72
|
});
|
|
42
73
|
return {
|
|
43
74
|
message: result.message,
|
package/dist/execution/runner.js
CHANGED
|
@@ -70,7 +70,7 @@ export async function executeTask(command, index, callbacks) {
|
|
|
70
70
|
return { status: ExecutionStatus.Success, elapsed, output };
|
|
71
71
|
}
|
|
72
72
|
else {
|
|
73
|
-
const errorMsg = result.
|
|
73
|
+
const errorMsg = result.error || result.errors || 'Command failed';
|
|
74
74
|
error = errorMsg;
|
|
75
75
|
const output = createOutput();
|
|
76
76
|
callbacks.onUpdate(output);
|
package/dist/index.js
CHANGED
|
@@ -5,7 +5,9 @@ import { dirname, join } from 'path';
|
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { render } from 'ink';
|
|
7
7
|
import { DebugLevel } from './configuration/types.js';
|
|
8
|
+
import { preventPerformanceBufferOverflow } from './services/performance.js';
|
|
8
9
|
import { Main } from './Main.js';
|
|
10
|
+
preventPerformanceBufferOverflow();
|
|
9
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
12
|
const __dirname = dirname(__filename);
|
|
11
13
|
// Get package info
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Anthropic from '@anthropic-ai/sdk';
|
|
2
2
|
import { getAvailableConfigStructure, getConfiguredKeys, } from '../configuration/schema.js';
|
|
3
3
|
import { logPrompt, logResponse } from './logger.js';
|
|
4
|
-
import {
|
|
4
|
+
import { loadSkillsForPrompt } from './skills.js';
|
|
5
5
|
import { toolRegistry } from './registry.js';
|
|
6
6
|
import { CommandResultSchema, IntrospectResultSchema, } from '../types/schemas.js';
|
|
7
7
|
/**
|
|
@@ -69,36 +69,32 @@ export class AnthropicService {
|
|
|
69
69
|
async processWithTool(command, toolName, customInstructions) {
|
|
70
70
|
// Load tool from registry
|
|
71
71
|
const tool = toolRegistry.getSchema(toolName);
|
|
72
|
-
//
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
72
|
+
// Check if this tool uses skills
|
|
73
|
+
const usesSkills = toolName === 'schedule' ||
|
|
74
|
+
toolName === 'introspect' ||
|
|
75
|
+
toolName === 'execute' ||
|
|
76
|
+
toolName === 'validate';
|
|
77
|
+
// Load base instructions and skills
|
|
78
|
+
const baseInstructions = customInstructions || toolRegistry.getInstructions(toolName);
|
|
79
|
+
let formattedSkills = '';
|
|
80
|
+
let skillDefinitions = [];
|
|
81
|
+
let systemPrompt = baseInstructions;
|
|
82
|
+
if (!customInstructions && usesSkills) {
|
|
83
|
+
const skillsResult = loadSkillsForPrompt();
|
|
84
|
+
formattedSkills = skillsResult.formatted;
|
|
85
|
+
skillDefinitions = skillsResult.definitions;
|
|
86
|
+
systemPrompt += formattedSkills;
|
|
77
87
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const skillsSection = formatSkillsForPrompt(skills);
|
|
89
|
-
systemPrompt += skillsSection;
|
|
90
|
-
}
|
|
91
|
-
// Add config structure for configure tool only
|
|
92
|
-
if (toolName === 'configure') {
|
|
93
|
-
const configStructure = getAvailableConfigStructure();
|
|
94
|
-
const configuredKeys = getConfiguredKeys();
|
|
95
|
-
const configSection = '\n## Available Configuration\n\n' +
|
|
96
|
-
'Config structure (key: description):\n' +
|
|
97
|
-
JSON.stringify(configStructure, null, 2) +
|
|
98
|
-
'\n\nConfigured keys (keys that exist in config file):\n' +
|
|
99
|
-
JSON.stringify(configuredKeys, null, 2);
|
|
100
|
-
systemPrompt += configSection;
|
|
101
|
-
}
|
|
88
|
+
// Add config structure for configure tool only
|
|
89
|
+
if (!customInstructions && toolName === 'configure') {
|
|
90
|
+
const configStructure = getAvailableConfigStructure();
|
|
91
|
+
const configuredKeys = getConfiguredKeys();
|
|
92
|
+
const configSection = '\n## Available Configuration\n\n' +
|
|
93
|
+
'Config structure (key: description):\n' +
|
|
94
|
+
JSON.stringify(configStructure, null, 2) +
|
|
95
|
+
'\n\nConfigured keys (keys that exist in config file):\n' +
|
|
96
|
+
JSON.stringify(configuredKeys, null, 2);
|
|
97
|
+
systemPrompt += configSection;
|
|
102
98
|
}
|
|
103
99
|
// Build tools array - add web search for answer tool
|
|
104
100
|
const tools = [tool];
|
|
@@ -111,7 +107,7 @@ export class AnthropicService {
|
|
|
111
107
|
// Collect debug components
|
|
112
108
|
const debug = [];
|
|
113
109
|
// Log prompt at Verbose level
|
|
114
|
-
const promptDebug = logPrompt(toolName, command,
|
|
110
|
+
const promptDebug = logPrompt(toolName, command, baseInstructions, formattedSkills, skillDefinitions);
|
|
115
111
|
if (promptDebug) {
|
|
116
112
|
debug.push(promptDebug);
|
|
117
113
|
}
|
package/dist/services/colors.js
CHANGED
|
@@ -20,6 +20,7 @@ export const Palette = {
|
|
|
20
20
|
Yellow: '#cccc5c',
|
|
21
21
|
Orange: '#f48c80',
|
|
22
22
|
MediumOrange: '#d07560',
|
|
23
|
+
WarmOrange: '#ce985e',
|
|
23
24
|
DarkOrange: '#ab5e40',
|
|
24
25
|
BurntOrange: '#cc7a5c',
|
|
25
26
|
Red: '#cc5c5c',
|
|
@@ -132,7 +133,7 @@ const taskColors = {
|
|
|
132
133
|
*/
|
|
133
134
|
const feedbackColors = {
|
|
134
135
|
[FeedbackType.Info]: Colors.Status.Info,
|
|
135
|
-
[FeedbackType.Warning]: Palette.
|
|
136
|
+
[FeedbackType.Warning]: Palette.WarmOrange,
|
|
136
137
|
[FeedbackType.Succeeded]: Colors.Status.Success,
|
|
137
138
|
[FeedbackType.Aborted]: Palette.MediumOrange,
|
|
138
139
|
[FeedbackType.Failed]: Colors.Status.Error,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from 'fs';
|
|
1
|
+
import { appendFileSync, existsSync, mkdirSync, readdirSync, readFileSync, renameSync, unlinkSync, writeFileSync, } from 'fs';
|
|
2
2
|
import { dirname } from 'path';
|
|
3
3
|
/**
|
|
4
4
|
* Real filesystem implementation using Node's fs module
|
|
@@ -13,6 +13,9 @@ export class RealFileSystem {
|
|
|
13
13
|
writeFile(path, data) {
|
|
14
14
|
writeFileSync(path, data, 'utf-8');
|
|
15
15
|
}
|
|
16
|
+
appendFile(path, data) {
|
|
17
|
+
appendFileSync(path, data, 'utf-8');
|
|
18
|
+
}
|
|
16
19
|
readDirectory(path) {
|
|
17
20
|
return readdirSync(path);
|
|
18
21
|
}
|
|
@@ -51,6 +54,15 @@ export class MemoryFileSystem {
|
|
|
51
54
|
}
|
|
52
55
|
this.files.set(path, data);
|
|
53
56
|
}
|
|
57
|
+
appendFile(path, data) {
|
|
58
|
+
// Auto-create parent directories (consistent with writeFile)
|
|
59
|
+
const dir = dirname(path);
|
|
60
|
+
if (dir !== '.' && dir !== path) {
|
|
61
|
+
this.createDirectory(dir, { recursive: true });
|
|
62
|
+
}
|
|
63
|
+
const existing = this.files.get(path) ?? '';
|
|
64
|
+
this.files.set(path, existing + data);
|
|
65
|
+
}
|
|
54
66
|
readDirectory(path) {
|
|
55
67
|
if (!this.directories.has(path)) {
|
|
56
68
|
throw new Error(`ENOENT: no such file or directory, scandir '${path}'`);
|