prompt-language-shell 0.2.6 → 0.2.9
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/config/PLAN.md +1 -0
- package/dist/index.js +18 -22
- package/dist/services/config.js +23 -23
- package/dist/types/components.js +6 -0
- package/dist/ui/Column.js +1 -1
- package/dist/ui/Command.js +10 -3
- package/dist/ui/Component.js +3 -0
- package/dist/ui/Config.js +30 -12
- package/dist/ui/Feedback.js +22 -0
- package/dist/ui/Main.js +123 -53
- package/dist/ui/Welcome.js +1 -1
- package/package.json +3 -2
- package/dist/config/EXECUTE.md +0 -700
- package/dist/services/config.test.js +0 -82
- package/dist/services/skills.test.js +0 -115
- package/dist/ui/ConfigSetup.js +0 -20
- package/dist/ui/ConfigThenCommand.js +0 -16
- package/dist/ui/Configure.js +0 -23
- package/dist/ui/History.js +0 -9
- package/dist/ui/Please.js +0 -25
- package/dist/ui/renderComponent.js +0 -14
package/dist/config/PLAN.md
CHANGED
|
@@ -333,6 +333,7 @@ When creating task definitions, focus on:
|
|
|
333
333
|
with precise, contextually appropriate alternatives. Use professional, clear
|
|
334
334
|
terminology suitable for technical documentation. Maintain natural, fluent
|
|
335
335
|
English phrasing while preserving the original intent.
|
|
336
|
+
**Keep action descriptions concise, at most 64 characters.**
|
|
336
337
|
|
|
337
338
|
- **Type**: Categorize the operation using one of these supported types:
|
|
338
339
|
- `config` - Configuration changes, settings updates
|
package/dist/index.js
CHANGED
|
@@ -3,8 +3,8 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
3
3
|
import { existsSync, readFileSync } from 'fs';
|
|
4
4
|
import { dirname, join } from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
|
-
import { render
|
|
7
|
-
import {
|
|
6
|
+
import { render } from 'ink';
|
|
7
|
+
import { hasValidConfig, loadConfig, saveAnthropicConfig, } from './services/config.js';
|
|
8
8
|
import { createAnthropicService } from './services/anthropic.js';
|
|
9
9
|
import { Main } from './ui/Main.js';
|
|
10
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -27,29 +27,25 @@ const app = {
|
|
|
27
27
|
const args = process.argv.slice(2);
|
|
28
28
|
const command = args.join(' ').trim() || null;
|
|
29
29
|
async function runApp() {
|
|
30
|
-
//
|
|
31
|
-
if (
|
|
32
|
-
const { waitUntilExit } = render(_jsx(Main, { app: app, command: command, isReady: false, onConfigured: (config) => {
|
|
33
|
-
saveConfig('anthropic', config);
|
|
34
|
-
// Create service once for the session
|
|
35
|
-
return command ? createAnthropicService(config) : undefined;
|
|
36
|
-
} }));
|
|
37
|
-
await waitUntilExit();
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
// Try to load and validate config
|
|
41
|
-
try {
|
|
30
|
+
// Happy path: valid config exists
|
|
31
|
+
if (hasValidConfig()) {
|
|
42
32
|
const config = loadConfig();
|
|
43
|
-
// Create service once at app initialization
|
|
44
33
|
const service = createAnthropicService(config.anthropic);
|
|
45
34
|
render(_jsx(Main, { app: app, command: command, service: service, isReady: true }));
|
|
35
|
+
return;
|
|
46
36
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
37
|
+
// Setup: config doesn't exist or is invalid
|
|
38
|
+
const { waitUntilExit, unmount } = render(_jsx(Main, { app: app, command: command, isReady: false, onConfigured: (config) => {
|
|
39
|
+
saveAnthropicConfig(config);
|
|
40
|
+
if (command) {
|
|
41
|
+
return createAnthropicService(config);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
// No command - exit after showing completion message
|
|
45
|
+
setTimeout(() => unmount(), 100);
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
} }));
|
|
49
|
+
await waitUntilExit();
|
|
54
50
|
}
|
|
55
51
|
runApp();
|
package/dist/services/config.js
CHANGED
|
@@ -3,9 +3,11 @@ import { homedir } from 'os';
|
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
import YAML from 'yaml';
|
|
5
5
|
export class ConfigError extends Error {
|
|
6
|
-
|
|
6
|
+
origin;
|
|
7
|
+
constructor(message, origin) {
|
|
7
8
|
super(message);
|
|
8
9
|
this.name = 'ConfigError';
|
|
10
|
+
this.origin = origin;
|
|
9
11
|
}
|
|
10
12
|
}
|
|
11
13
|
const CONFIG_FILE = join(homedir(), '.plsrc');
|
|
@@ -14,30 +16,21 @@ function parseYamlConfig(content) {
|
|
|
14
16
|
return YAML.parse(content);
|
|
15
17
|
}
|
|
16
18
|
catch (error) {
|
|
17
|
-
throw new ConfigError(
|
|
18
|
-
`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
19
|
+
throw new ConfigError('Failed to parse configuration file', error instanceof Error ? error : undefined);
|
|
19
20
|
}
|
|
20
21
|
}
|
|
21
22
|
function validateConfig(parsed) {
|
|
22
23
|
if (!parsed || typeof parsed !== 'object') {
|
|
23
|
-
throw new ConfigError(
|
|
24
|
-
'Expected a YAML object with configuration settings.');
|
|
24
|
+
throw new ConfigError('Invalid configuration format');
|
|
25
25
|
}
|
|
26
26
|
const config = parsed;
|
|
27
27
|
// Validate anthropic section
|
|
28
28
|
if (!config.anthropic || typeof config.anthropic !== 'object') {
|
|
29
|
-
throw new ConfigError(
|
|
30
|
-
'Please add:\n' +
|
|
31
|
-
'anthropic:\n' +
|
|
32
|
-
' key: sk-ant-...');
|
|
29
|
+
throw new ConfigError('Missing or invalid anthropic configuration');
|
|
33
30
|
}
|
|
34
|
-
const
|
|
35
|
-
const key = anthropic['key'];
|
|
31
|
+
const { key, model } = config.anthropic;
|
|
36
32
|
if (!key || typeof key !== 'string') {
|
|
37
|
-
throw new ConfigError(
|
|
38
|
-
'Please add your Anthropic API key:\n' +
|
|
39
|
-
'anthropic:\n' +
|
|
40
|
-
' key: sk-ant-...');
|
|
33
|
+
throw new ConfigError('Missing or invalid API key');
|
|
41
34
|
}
|
|
42
35
|
const validatedConfig = {
|
|
43
36
|
anthropic: {
|
|
@@ -45,19 +38,14 @@ function validateConfig(parsed) {
|
|
|
45
38
|
},
|
|
46
39
|
};
|
|
47
40
|
// Optional model
|
|
48
|
-
if (
|
|
49
|
-
validatedConfig.anthropic.model =
|
|
41
|
+
if (model && typeof model === 'string') {
|
|
42
|
+
validatedConfig.anthropic.model = model;
|
|
50
43
|
}
|
|
51
44
|
return validatedConfig;
|
|
52
45
|
}
|
|
53
46
|
export function loadConfig() {
|
|
54
47
|
if (!existsSync(CONFIG_FILE)) {
|
|
55
|
-
throw new ConfigError(
|
|
56
|
-
'Please create it with your Anthropic API key.\n' +
|
|
57
|
-
'Example:\n\n' +
|
|
58
|
-
'anthropic:\n' +
|
|
59
|
-
' key: sk-ant-...\n' +
|
|
60
|
-
' model: claude-haiku-4-5-20251001\n');
|
|
48
|
+
throw new ConfigError('Configuration not found');
|
|
61
49
|
}
|
|
62
50
|
const content = readFileSync(CONFIG_FILE, 'utf-8');
|
|
63
51
|
const parsed = parseYamlConfig(content);
|
|
@@ -69,6 +57,15 @@ export function getConfigPath() {
|
|
|
69
57
|
export function configExists() {
|
|
70
58
|
return existsSync(CONFIG_FILE);
|
|
71
59
|
}
|
|
60
|
+
export function hasValidConfig() {
|
|
61
|
+
try {
|
|
62
|
+
const config = loadConfig();
|
|
63
|
+
return !!config.anthropic.key;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
72
69
|
export function mergeConfig(existingContent, sectionName, newValues) {
|
|
73
70
|
const parsed = existingContent.trim()
|
|
74
71
|
? YAML.parse(existingContent)
|
|
@@ -95,3 +92,6 @@ export function saveConfig(section, config) {
|
|
|
95
92
|
const newContent = mergeConfig(existingContent, section, config);
|
|
96
93
|
writeFileSync(CONFIG_FILE, newContent, 'utf-8');
|
|
97
94
|
}
|
|
95
|
+
export function saveAnthropicConfig(config) {
|
|
96
|
+
saveConfig('anthropic', config);
|
|
97
|
+
}
|
package/dist/types/components.js
CHANGED
|
@@ -9,3 +9,9 @@ export var TaskType;
|
|
|
9
9
|
TaskType["Ignore"] = "ignore";
|
|
10
10
|
TaskType["Select"] = "select";
|
|
11
11
|
})(TaskType || (TaskType = {}));
|
|
12
|
+
export var FeedbackType;
|
|
13
|
+
(function (FeedbackType) {
|
|
14
|
+
FeedbackType["Succeeded"] = "succeeded";
|
|
15
|
+
FeedbackType["Aborted"] = "aborted";
|
|
16
|
+
FeedbackType["Failed"] = "failed";
|
|
17
|
+
})(FeedbackType || (FeedbackType = {}));
|
package/dist/ui/Column.js
CHANGED
|
@@ -2,5 +2,5 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { Box } from 'ink';
|
|
3
3
|
import { Component } from './Component.js';
|
|
4
4
|
export const Column = ({ items }) => {
|
|
5
|
-
return (_jsx(Box, { marginTop: 1, flexDirection: "column", gap: 1, children: items.map((item, index) => (_jsx(Box, { children: _jsx(Component, { def: item }) }, index))) }));
|
|
5
|
+
return (_jsx(Box, { marginTop: 1, marginBottom: 1, flexDirection: "column", gap: 1, children: items.map((item, index) => (_jsx(Box, { children: _jsx(Component, { def: item }) }, index))) }));
|
|
6
6
|
};
|
package/dist/ui/Command.js
CHANGED
|
@@ -69,7 +69,7 @@ function taskToListItem(task) {
|
|
|
69
69
|
}
|
|
70
70
|
return item;
|
|
71
71
|
}
|
|
72
|
-
export function Command({ command, state, service, error: errorProp, children, }) {
|
|
72
|
+
export function Command({ command, state, service, error: errorProp, children, onError, onComplete, }) {
|
|
73
73
|
const done = state?.done ?? false;
|
|
74
74
|
const [error, setError] = useState(state?.error || errorProp || null);
|
|
75
75
|
const [isLoading, setIsLoading] = useState(state?.isLoading ?? !done);
|
|
@@ -98,6 +98,7 @@ export function Command({ command, state, service, error: errorProp, children, }
|
|
|
98
98
|
setMessage(result.message);
|
|
99
99
|
setTasks(result.tasks);
|
|
100
100
|
setIsLoading(false);
|
|
101
|
+
onComplete?.();
|
|
101
102
|
}
|
|
102
103
|
}
|
|
103
104
|
catch (err) {
|
|
@@ -105,8 +106,14 @@ export function Command({ command, state, service, error: errorProp, children, }
|
|
|
105
106
|
const remainingTime = Math.max(0, MIN_PROCESSING_TIME - elapsed);
|
|
106
107
|
await new Promise((resolve) => setTimeout(resolve, remainingTime));
|
|
107
108
|
if (mounted) {
|
|
108
|
-
|
|
109
|
+
const errorMessage = err instanceof Error ? err.message : 'Unknown error occurred';
|
|
109
110
|
setIsLoading(false);
|
|
111
|
+
if (onError) {
|
|
112
|
+
onError(errorMessage);
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
setError(errorMessage);
|
|
116
|
+
}
|
|
110
117
|
}
|
|
111
118
|
}
|
|
112
119
|
}
|
|
@@ -115,5 +122,5 @@ export function Command({ command, state, service, error: errorProp, children, }
|
|
|
115
122
|
mounted = false;
|
|
116
123
|
};
|
|
117
124
|
}, [command, done, service]);
|
|
118
|
-
return (_jsxs(Box, { alignSelf: "flex-start",
|
|
125
|
+
return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", marginLeft: 1, children: [_jsxs(Box, { children: [_jsxs(Text, { color: "gray", children: ["> pls ", command] }), isLoading && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Spinner, {})] }))] }), error && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "red", children: ["Error: ", error] }) })), !isLoading && tasks.length > 0 && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [message && (_jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { children: [" ", message] }), _jsx(Separator, { color: "#9c5ccc" }), _jsx(Text, { color: "#9c5ccc", children: "plan" })] })), _jsx(List, { items: tasks.map(taskToListItem) })] })), children] }));
|
|
119
126
|
}
|
package/dist/ui/Component.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Command } from './Command.js';
|
|
3
3
|
import { Config } from './Config.js';
|
|
4
|
+
import { Feedback } from './Feedback.js';
|
|
4
5
|
import { Welcome } from './Welcome.js';
|
|
5
6
|
export function Component({ def }) {
|
|
6
7
|
switch (def.name) {
|
|
@@ -11,6 +12,8 @@ export function Component({ def }) {
|
|
|
11
12
|
const state = def.state;
|
|
12
13
|
return _jsx(Config, { ...props, state: state });
|
|
13
14
|
}
|
|
15
|
+
case 'feedback':
|
|
16
|
+
return _jsx(Feedback, { ...def.props });
|
|
14
17
|
case 'command': {
|
|
15
18
|
const props = def.props;
|
|
16
19
|
const state = def.state;
|
package/dist/ui/Config.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { Box, Text } from 'ink';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
4
|
import TextInput from 'ink-text-input';
|
|
5
|
-
export function Config({ steps, state, onFinished }) {
|
|
5
|
+
export function Config({ steps, state, onFinished, onAborted }) {
|
|
6
6
|
const done = state?.done ?? false;
|
|
7
7
|
const [step, setStep] = React.useState(done ? steps.length : 0);
|
|
8
8
|
const [values, setValues] = React.useState(() => {
|
|
@@ -15,9 +15,26 @@ export function Config({ steps, state, onFinished }) {
|
|
|
15
15
|
return initial;
|
|
16
16
|
});
|
|
17
17
|
const [inputValue, setInputValue] = React.useState('');
|
|
18
|
+
const normalizeValue = (value) => {
|
|
19
|
+
if (value === null || value === undefined) {
|
|
20
|
+
return '';
|
|
21
|
+
}
|
|
22
|
+
return value.replace(/\n/g, '').trim();
|
|
23
|
+
};
|
|
24
|
+
useInput((input, key) => {
|
|
25
|
+
if (key.escape && !done && step < steps.length) {
|
|
26
|
+
if (onAborted) {
|
|
27
|
+
onAborted();
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
});
|
|
18
31
|
const handleSubmit = (value) => {
|
|
19
32
|
const currentStepConfig = steps[step];
|
|
20
|
-
const finalValue = value
|
|
33
|
+
const finalValue = normalizeValue(value) || normalizeValue(currentStepConfig.value);
|
|
34
|
+
// Don't allow empty value if step has no default (mandatory field)
|
|
35
|
+
if (!finalValue && !currentStepConfig.value) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
21
38
|
const newValues = { ...values, [currentStepConfig.key]: finalValue };
|
|
22
39
|
setValues(newValues);
|
|
23
40
|
setInputValue('');
|
|
@@ -32,13 +49,14 @@ export function Config({ steps, state, onFinished }) {
|
|
|
32
49
|
setStep(step + 1);
|
|
33
50
|
}
|
|
34
51
|
};
|
|
35
|
-
return (
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
return (_jsx(Box, { flexDirection: "column", marginLeft: 1, children: steps.map((stepConfig, index) => {
|
|
53
|
+
const isCurrentStep = index === step && !done;
|
|
54
|
+
const isCompleted = index < step;
|
|
55
|
+
const wasAborted = index === step && done;
|
|
56
|
+
const shouldShow = isCompleted || isCurrentStep || wasAborted;
|
|
57
|
+
if (!shouldShow) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
return (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsx(Box, { children: _jsxs(Text, { children: [stepConfig.description, ":"] }) }), _jsxs(Box, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: "#5c8cbc", dimColor: !isCurrentStep, children: ">" }), _jsx(Text, { children: " " }), isCurrentStep ? (_jsx(TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleSubmit, placeholder: stepConfig.value || undefined })) : (_jsx(Text, { dimColor: true, children: values[stepConfig.key] || '' }))] })] }, stepConfig.key));
|
|
61
|
+
}) }));
|
|
44
62
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Box, Text } from 'ink';
|
|
3
|
+
import { FeedbackType } from '../types/components.js';
|
|
4
|
+
function getSymbol(type) {
|
|
5
|
+
return {
|
|
6
|
+
[FeedbackType.Succeeded]: '✓',
|
|
7
|
+
[FeedbackType.Aborted]: '⊘',
|
|
8
|
+
[FeedbackType.Failed]: '✗',
|
|
9
|
+
}[type];
|
|
10
|
+
}
|
|
11
|
+
function getColor(type) {
|
|
12
|
+
return {
|
|
13
|
+
[FeedbackType.Succeeded]: '#00aa00', // green
|
|
14
|
+
[FeedbackType.Aborted]: '#cc9c5c', // orange
|
|
15
|
+
[FeedbackType.Failed]: '#aa0000', // red
|
|
16
|
+
}[type];
|
|
17
|
+
}
|
|
18
|
+
export function Feedback({ type, message }) {
|
|
19
|
+
const color = getColor(type);
|
|
20
|
+
const symbol = getSymbol(type);
|
|
21
|
+
return (_jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: color, children: [symbol, " ", message] }) }));
|
|
22
|
+
}
|
package/dist/ui/Main.js
CHANGED
|
@@ -1,66 +1,136 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import React from 'react';
|
|
3
|
+
import { FeedbackType } from '../types/components.js';
|
|
3
4
|
import { Column } from './Column.js';
|
|
4
|
-
|
|
5
|
+
function exit(code) {
|
|
6
|
+
setTimeout(() => globalThis.process.exit(code), 100);
|
|
7
|
+
}
|
|
8
|
+
function markAsDone(component) {
|
|
9
|
+
return { ...component, state: { ...component.state, done: true } };
|
|
10
|
+
}
|
|
11
|
+
function createWelcomeDefinition(app) {
|
|
12
|
+
return {
|
|
13
|
+
name: 'welcome',
|
|
14
|
+
props: { app },
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
function createConfigSteps() {
|
|
18
|
+
return [
|
|
19
|
+
{ description: 'Anthropic API key', key: 'key', value: null },
|
|
20
|
+
{
|
|
21
|
+
description: 'Model',
|
|
22
|
+
key: 'model',
|
|
23
|
+
value: 'claude-haiku-4-5-20251001',
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
}
|
|
27
|
+
function createConfigDefinition(onFinished, onAborted) {
|
|
28
|
+
return {
|
|
29
|
+
name: 'config',
|
|
30
|
+
state: { done: false },
|
|
31
|
+
props: {
|
|
32
|
+
steps: createConfigSteps(),
|
|
33
|
+
onFinished,
|
|
34
|
+
onAborted,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function createCommandDefinition(command, service, onError, onComplete) {
|
|
39
|
+
return {
|
|
40
|
+
name: 'command',
|
|
41
|
+
state: {
|
|
42
|
+
done: false,
|
|
43
|
+
isLoading: true,
|
|
44
|
+
},
|
|
45
|
+
props: {
|
|
46
|
+
command,
|
|
47
|
+
service,
|
|
48
|
+
onError,
|
|
49
|
+
onComplete,
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function createFeedback(type, ...messages) {
|
|
54
|
+
return {
|
|
55
|
+
name: 'feedback',
|
|
56
|
+
props: {
|
|
57
|
+
type,
|
|
58
|
+
message: messages.join('\n\n'),
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export const Main = ({ app, command, service: initialService, isReady, onConfigured, }) => {
|
|
5
63
|
const [history, setHistory] = React.useState([]);
|
|
6
64
|
const [current, setCurrent] = React.useState(null);
|
|
65
|
+
const [service, setService] = React.useState(initialService);
|
|
66
|
+
const addToHistory = React.useCallback((...items) => {
|
|
67
|
+
setHistory((history) => [...history, ...items]);
|
|
68
|
+
}, []);
|
|
69
|
+
const handleConfigFinished = React.useCallback((config) => {
|
|
70
|
+
const service = onConfigured?.(config);
|
|
71
|
+
if (service) {
|
|
72
|
+
setService(service);
|
|
73
|
+
}
|
|
74
|
+
// Move config to history with done state and add success feedback
|
|
75
|
+
setCurrent((previous) => {
|
|
76
|
+
if (previous && previous.name === 'config') {
|
|
77
|
+
addToHistory(markAsDone(previous), createFeedback(FeedbackType.Succeeded, 'Configuration complete'));
|
|
78
|
+
}
|
|
79
|
+
return null;
|
|
80
|
+
});
|
|
81
|
+
}, [onConfigured, addToHistory]);
|
|
82
|
+
const handleConfigAborted = React.useCallback(() => {
|
|
83
|
+
// Move config to history with done state and add aborted feedback
|
|
84
|
+
setCurrent((previous) => {
|
|
85
|
+
if (previous && previous.name === 'config') {
|
|
86
|
+
addToHistory(markAsDone(previous), createFeedback(FeedbackType.Aborted, 'Configuration aborted by user'));
|
|
87
|
+
// Exit after showing abort message
|
|
88
|
+
exit(0);
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
});
|
|
92
|
+
}, [addToHistory]);
|
|
93
|
+
const handleCommandError = React.useCallback((error) => {
|
|
94
|
+
// Move command to history with done state and add error feedback
|
|
95
|
+
setCurrent((previous) => {
|
|
96
|
+
if (previous && previous.name === 'command') {
|
|
97
|
+
addToHistory(markAsDone(previous), createFeedback(FeedbackType.Failed, 'Unexpected error occurred:', error));
|
|
98
|
+
// Exit after showing error
|
|
99
|
+
exit(1);
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
});
|
|
103
|
+
}, [addToHistory]);
|
|
104
|
+
const handleCommandComplete = React.useCallback(() => {
|
|
105
|
+
// Move command to history with done state
|
|
106
|
+
setCurrent((previous) => {
|
|
107
|
+
if (previous && previous.name === 'command') {
|
|
108
|
+
addToHistory(markAsDone(previous));
|
|
109
|
+
// Exit after showing plan
|
|
110
|
+
exit(0);
|
|
111
|
+
}
|
|
112
|
+
return null;
|
|
113
|
+
});
|
|
114
|
+
}, [addToHistory]);
|
|
115
|
+
// Initialize configuration flow when not ready
|
|
7
116
|
React.useEffect(() => {
|
|
8
|
-
// Initialize history and current component based on props
|
|
9
117
|
if (!isReady) {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
{
|
|
13
|
-
name: 'welcome',
|
|
14
|
-
props: {
|
|
15
|
-
app,
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
]);
|
|
19
|
-
const configSteps = [
|
|
20
|
-
{ description: 'Anthropic API key', key: 'key', value: null },
|
|
21
|
-
{
|
|
22
|
-
description: 'Model',
|
|
23
|
-
key: 'model',
|
|
24
|
-
value: 'claude-haiku-4-5-20251001',
|
|
25
|
-
},
|
|
26
|
-
];
|
|
27
|
-
setCurrent({
|
|
28
|
-
name: 'config',
|
|
29
|
-
state: {
|
|
30
|
-
done: false,
|
|
31
|
-
},
|
|
32
|
-
props: {
|
|
33
|
-
steps: configSteps,
|
|
34
|
-
onFinished: (config) => {
|
|
35
|
-
if (onConfigured) {
|
|
36
|
-
onConfigured(config);
|
|
37
|
-
}
|
|
38
|
-
},
|
|
39
|
-
},
|
|
40
|
-
});
|
|
118
|
+
setHistory(command ? [] : [createWelcomeDefinition(app)]);
|
|
119
|
+
setCurrent(createConfigDefinition(handleConfigFinished, handleConfigAborted));
|
|
41
120
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
isLoading: true,
|
|
48
|
-
},
|
|
49
|
-
props: {
|
|
50
|
-
command,
|
|
51
|
-
service,
|
|
52
|
-
},
|
|
53
|
-
});
|
|
121
|
+
}, [isReady, app, command, handleConfigFinished, handleConfigAborted]);
|
|
122
|
+
// Execute command when service and command are available
|
|
123
|
+
React.useEffect(() => {
|
|
124
|
+
if (command && service) {
|
|
125
|
+
setCurrent(createCommandDefinition(command, service, handleCommandError, handleCommandComplete));
|
|
54
126
|
}
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
},
|
|
61
|
-
});
|
|
127
|
+
}, [command, service, handleCommandError, handleCommandComplete]);
|
|
128
|
+
// Show welcome screen when ready but no command
|
|
129
|
+
React.useEffect(() => {
|
|
130
|
+
if (isReady && !command) {
|
|
131
|
+
setCurrent(createWelcomeDefinition(app));
|
|
62
132
|
}
|
|
63
|
-
}, [isReady, command,
|
|
133
|
+
}, [isReady, command, app]);
|
|
64
134
|
const items = [...history, ...(current ? [current] : [])];
|
|
65
135
|
return _jsx(Column, { items: items });
|
|
66
136
|
};
|
package/dist/ui/Welcome.js
CHANGED
|
@@ -18,5 +18,5 @@ function Usage() {
|
|
|
18
18
|
return (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: "brightWhite", bold: true, children: "Usage:" }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "whiteBright", dimColor: true, children: ">" }), _jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "greenBright", bold: true, children: "pls" }), _jsx(Text, { color: "yellow", bold: true, children: "[describe your request]" })] })] })] }));
|
|
19
19
|
}
|
|
20
20
|
export function Welcome({ app }) {
|
|
21
|
-
return (_jsx(Box, { alignSelf: "flex-start",
|
|
21
|
+
return (_jsx(Box, { alignSelf: "flex-start", children: _jsxs(Panel, { children: [_jsx(Header, { app: app }), _jsx(Description, { description: app.description }), _jsx(Usage, {})] }) }));
|
|
22
22
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prompt-language-shell",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "Your personal command-line concierge. Ask politely, and it gets things done.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -11,7 +11,8 @@
|
|
|
11
11
|
"dist"
|
|
12
12
|
],
|
|
13
13
|
"scripts": {
|
|
14
|
-
"
|
|
14
|
+
"clean": "rm -rf dist",
|
|
15
|
+
"build": "npm run clean && tsc && chmod +x dist/index.js && mkdir -p dist/config && cp src/config/*.md dist/config/",
|
|
15
16
|
"dev": "npm run build && tsc --watch",
|
|
16
17
|
"prepare": "husky",
|
|
17
18
|
"prepublishOnly": "npm run check",
|