prompt-language-shell 0.1.4 → 0.1.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 +11 -32
- package/dist/config/PLAN.md +105 -134
- package/dist/index.js +10 -15
- package/dist/services/anthropic.js +35 -46
- package/dist/services/config.js +11 -15
- package/dist/services/skills.js +1 -1
- package/dist/services/tool-registry.js +41 -0
- package/dist/tools/plan.tool.js +32 -0
- package/dist/types/components.js +1 -0
- package/dist/ui/Command.js +22 -11
- package/dist/ui/Configure.js +23 -0
- package/dist/ui/History.js +2 -1
- package/dist/ui/Main.js +55 -0
- package/dist/ui/Welcome.js +2 -2
- package/dist/ui/renderComponent.js +14 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -10,61 +10,40 @@ npm install -g prompt-language-shell
|
|
|
10
10
|
|
|
11
11
|
## Setup
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
1. Get your API key from [Anthropic Console](https://console.anthropic.com/)
|
|
16
|
-
2. Create the configuration directory and file:
|
|
17
|
-
|
|
18
|
-
```bash
|
|
19
|
-
mkdir -p ~/.pls
|
|
20
|
-
echo "CLAUDE_API_KEY=sk-ant-your-api-key-here" > ~/.pls/.env
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
Replace `sk-ant-your-api-key-here` with your actual API key.
|
|
13
|
+
On first run, `pls` walks you through a quick setup. Your settings will be saved to `~/.plsrc`.
|
|
24
14
|
|
|
25
15
|
## Usage
|
|
26
16
|
|
|
27
|
-
|
|
17
|
+
Type `pls` followed by your request in natural language:
|
|
28
18
|
|
|
29
19
|
```bash
|
|
30
20
|
pls change dir to ~
|
|
31
21
|
```
|
|
32
22
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
- Display your original command
|
|
36
|
-
- Process it to grammatically correct and clarify it
|
|
37
|
-
- Show the interpreted task
|
|
38
|
-
|
|
39
|
-
Example output:
|
|
23
|
+
Your command will be interpreted and organized into a list of tasks:
|
|
40
24
|
|
|
41
25
|
```
|
|
42
26
|
> pls change dir to ~
|
|
43
|
-
-
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
You can provide multiple tasks separated by commas (`,`), semicolons (`;`), or the word "and":
|
|
47
|
-
|
|
48
|
-
```bash
|
|
49
|
-
pls install deps, run tests and deploy
|
|
27
|
+
- Change directory to the home folder
|
|
50
28
|
```
|
|
51
29
|
|
|
52
|
-
|
|
30
|
+
You can provide multiple requests at once:
|
|
53
31
|
|
|
54
32
|
```
|
|
55
33
|
> pls install deps, run tests and deploy
|
|
56
|
-
-
|
|
57
|
-
-
|
|
58
|
-
-
|
|
34
|
+
- Install dependencies
|
|
35
|
+
- Run tests
|
|
36
|
+
- Deploy to server
|
|
59
37
|
```
|
|
60
38
|
|
|
61
39
|
Run `pls` without arguments to see the welcome screen.
|
|
62
40
|
|
|
63
41
|
## Configuration
|
|
64
42
|
|
|
65
|
-
|
|
43
|
+
Your configuration is stored in `~/.plsrc` as a YAML file. Supported settings:
|
|
66
44
|
|
|
67
|
-
- `
|
|
45
|
+
- `anthropic.key` - Your API key
|
|
46
|
+
- `anthropic.model` - The model to use
|
|
68
47
|
|
|
69
48
|
## Development
|
|
70
49
|
|
package/dist/config/PLAN.md
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
## Overview
|
|
2
2
|
|
|
3
3
|
You are the planning component of "pls" (please), a professional command-line
|
|
4
|
-
concierge that users trust to execute their tasks reliably. Your role is
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
concierge that users trust to execute their tasks reliably. Your role is to
|
|
5
|
+
transform natural language requests into well-formed, executable task
|
|
6
|
+
definitions.
|
|
7
7
|
|
|
8
8
|
The concierge handles diverse operations including filesystem manipulation,
|
|
9
9
|
resource fetching, system commands, information queries, and multi-step
|
|
10
10
|
workflows. Users expect tasks to be planned logically, sequentially, and
|
|
11
11
|
atomically so they execute exactly as intended.
|
|
12
12
|
|
|
13
|
-
Your task is to
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
Your task is to create structured task definitions that:
|
|
14
|
+
- Describe WHAT needs to be done in clear, professional English
|
|
15
|
+
- Specify the TYPE of operation (when applicable)
|
|
16
|
+
- Include relevant PARAMETERS (when applicable)
|
|
17
|
+
|
|
18
|
+
Each task should be precise and unambiguous, ready to be executed by the
|
|
19
|
+
appropriate handler.
|
|
17
20
|
|
|
18
21
|
## Skills Integration
|
|
19
22
|
|
|
@@ -24,28 +27,27 @@ When a query matches a skill:
|
|
|
24
27
|
1. Recognize the semantic match between the user's request and the skill
|
|
25
28
|
description
|
|
26
29
|
2. Extract the individual steps from the skill's "Steps" section
|
|
27
|
-
3.
|
|
28
|
-
with a capital letter
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
3. Create a task definition for each step with:
|
|
31
|
+
- action: clear, professional description starting with a capital letter
|
|
32
|
+
- type: category of operation (if the skill specifies it or you can infer it)
|
|
33
|
+
- params: any specific parameters mentioned in the step
|
|
34
|
+
4. If the user's query includes additional requirements beyond the skill,
|
|
35
|
+
append those as additional task definitions
|
|
36
|
+
5. NEVER replace the skill's detailed steps with a generic restatement
|
|
34
37
|
|
|
35
38
|
Example 1:
|
|
36
|
-
- Skill
|
|
37
|
-
script -
|
|
38
|
-
- User
|
|
39
|
-
- Correct
|
|
40
|
-
|
|
41
|
-
- WRONG
|
|
39
|
+
- Skill steps: "- Navigate to the {PROJECT} root directory. - Execute the
|
|
40
|
+
{PROJECT} generation script. - Compile the {PROJECT}'s source code"
|
|
41
|
+
- User: "build project X"
|
|
42
|
+
- Correct: Three tasks with actions following the skill's steps, with
|
|
43
|
+
{PROJECT} replaced by "project X"
|
|
44
|
+
- WRONG: One task with action "Build project X"
|
|
42
45
|
|
|
43
46
|
Example 2:
|
|
44
|
-
- Skill
|
|
45
|
-
|
|
46
|
-
-
|
|
47
|
-
-
|
|
48
|
-
script", "Execute the test suite", "Generate a report"]
|
|
47
|
+
- Skill steps: "- Check prerequisites. - Run compilation. - Execute tests"
|
|
48
|
+
- User: "run tests and generate a report"
|
|
49
|
+
- Correct: Four tasks (the three from skill + one for report generation)
|
|
50
|
+
- WRONG: Two tasks ("run tests", "generate a report")
|
|
49
51
|
|
|
50
52
|
## Evaluation of Requests
|
|
51
53
|
|
|
@@ -112,20 +114,30 @@ If the request is clear enough to understand the intent, even if informal or
|
|
|
112
114
|
playful, process it normally. Refine casual language into professional task
|
|
113
115
|
descriptions.
|
|
114
116
|
|
|
115
|
-
##
|
|
117
|
+
## Task Definition Guidelines
|
|
118
|
+
|
|
119
|
+
When creating task definitions, focus on:
|
|
120
|
+
|
|
121
|
+
- **Action**: Use correct grammar and sentence structure. Replace vague words
|
|
122
|
+
with precise, contextually appropriate alternatives. Use professional, clear
|
|
123
|
+
terminology suitable for technical documentation. Maintain natural, fluent
|
|
124
|
+
English phrasing while preserving the original intent.
|
|
125
|
+
|
|
126
|
+
- **Type**: Categorize the operation using one of these supported types:
|
|
127
|
+
- `config` - Configuration changes, settings updates
|
|
128
|
+
- `plan` - Planning or breaking down tasks
|
|
129
|
+
- `execute` - Shell commands, running programs, scripts, compiling, building
|
|
130
|
+
- `answer` - Answering questions, explaining concepts, providing information
|
|
131
|
+
- `report` - Generating summaries, creating reports, displaying results
|
|
116
132
|
|
|
117
|
-
|
|
133
|
+
Omit the type field if none of these categories clearly fit the operation.
|
|
118
134
|
|
|
119
|
-
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
- Use professional, clear terminology suitable for technical documentation
|
|
123
|
-
- Maintain natural, fluent English phrasing
|
|
124
|
-
- Preserve the original intent and meaning
|
|
125
|
-
- Be concise and unambiguous
|
|
135
|
+
- **Params**: Include specific parameters mentioned in the request or skill
|
|
136
|
+
(e.g., paths, URLs, command arguments, file names). Omit if no parameters
|
|
137
|
+
are relevant.
|
|
126
138
|
|
|
127
|
-
Prioritize clarity and precision over brevity.
|
|
128
|
-
|
|
139
|
+
Prioritize clarity and precision over brevity. Each task should be unambiguous
|
|
140
|
+
and executable.
|
|
129
141
|
|
|
130
142
|
## Multiple Tasks
|
|
131
143
|
|
|
@@ -134,9 +146,8 @@ word "and", or when the user asks a complex question that requires multiple
|
|
|
134
146
|
steps to answer:
|
|
135
147
|
|
|
136
148
|
1. Identify each individual task or step
|
|
137
|
-
2. Break complex questions into separate, simpler
|
|
138
|
-
3.
|
|
139
|
-
4. Use this exact format: ["task 1", "task 2", "task 3"]
|
|
149
|
+
2. Break complex questions into separate, simpler task definitions
|
|
150
|
+
3. Create a task definition for each distinct operation
|
|
140
151
|
|
|
141
152
|
When breaking down complex questions:
|
|
142
153
|
|
|
@@ -144,7 +155,7 @@ When breaking down complex questions:
|
|
|
144
155
|
- Separate conditional checks into distinct tasks
|
|
145
156
|
- Keep each task simple and focused on one operation
|
|
146
157
|
|
|
147
|
-
Before
|
|
158
|
+
Before finalizing the task list, perform strict validation:
|
|
148
159
|
|
|
149
160
|
1. Each task is semantically unique (no duplicates with different words)
|
|
150
161
|
2. Each task provides distinct value
|
|
@@ -152,7 +163,7 @@ Before returning a JSON array, perform strict validation:
|
|
|
152
163
|
4. When uncertain whether to split, default to a single task
|
|
153
164
|
5. Executing the tasks will not result in duplicate work
|
|
154
165
|
|
|
155
|
-
Critical validation check: After creating the
|
|
166
|
+
Critical validation check: After creating the task list, examine each pair of
|
|
156
167
|
tasks and ask "Would these perform the same operation?" If yes, they are
|
|
157
168
|
duplicates and must be merged or removed. Pay special attention to synonym
|
|
158
169
|
verbs (delete, remove, erase) and equivalent noun phrases (unused apps,
|
|
@@ -160,8 +171,8 @@ applications not used).
|
|
|
160
171
|
|
|
161
172
|
## Avoiding Duplicates
|
|
162
173
|
|
|
163
|
-
Each task
|
|
164
|
-
|
|
174
|
+
Each task must be semantically unique and provide distinct value. Before
|
|
175
|
+
finalizing multiple tasks, verify there are no duplicates.
|
|
165
176
|
|
|
166
177
|
Rules for preventing duplicates:
|
|
167
178
|
|
|
@@ -218,20 +229,11 @@ Split into multiple tasks when:
|
|
|
218
229
|
- Truly separate steps: "create file and add content to it" (two distinct
|
|
219
230
|
operations)
|
|
220
231
|
|
|
221
|
-
##
|
|
222
|
-
|
|
223
|
-
- Single task: Return ONLY the corrected command text
|
|
224
|
-
- Multiple tasks: Return ONLY a JSON array of strings
|
|
225
|
-
|
|
226
|
-
Do not include explanations, commentary, markdown formatting, code blocks, or
|
|
227
|
-
any other text. For JSON arrays, return the raw JSON without ```json``` or
|
|
228
|
-
any other wrapping.
|
|
229
|
-
|
|
230
|
-
## Final Validation Before Response
|
|
232
|
+
## Final Validation
|
|
231
233
|
|
|
232
|
-
Before
|
|
234
|
+
Before finalizing the task list, perform this final check:
|
|
233
235
|
|
|
234
|
-
1. Compare each task against every other task
|
|
236
|
+
1. Compare each task against every other task
|
|
235
237
|
2. Ask for each pair: "Do these describe the same operation using different
|
|
236
238
|
words?"
|
|
237
239
|
3. Check specifically for:
|
|
@@ -243,7 +245,7 @@ Before returning any JSON array, perform this final check:
|
|
|
243
245
|
5. If in doubt about whether tasks are duplicates, they probably are - merge
|
|
244
246
|
them
|
|
245
247
|
|
|
246
|
-
Only
|
|
248
|
+
Only finalize after confirming no semantic duplicates exist.
|
|
247
249
|
|
|
248
250
|
## Examples
|
|
249
251
|
|
|
@@ -252,106 +254,75 @@ Only return the array after confirming no semantic duplicates exist.
|
|
|
252
254
|
These examples show common mistakes that create semantic duplicates:
|
|
253
255
|
|
|
254
256
|
- "explain Lehman's terms in Lehman's terms" →
|
|
255
|
-
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
"describe Lehman's terms using easy-to-understand words",
|
|
259
|
-
]
|
|
260
|
-
- correct: explain Lehman's terms in simple language
|
|
257
|
+
- WRONG: Two tasks with actions "Explain what Lehman's terms are in simple
|
|
258
|
+
language" and "Describe Lehman's terms using easy-to-understand words"
|
|
259
|
+
- CORRECT: One task with action "Explain Lehman's terms in simple language"
|
|
261
260
|
|
|
262
261
|
- "show and display files" →
|
|
263
|
-
-
|
|
264
|
-
|
|
265
|
-
"show the files",
|
|
266
|
-
"display the files",
|
|
267
|
-
]
|
|
268
|
-
- correct: "show the files"
|
|
262
|
+
- WRONG: Two tasks with actions "Show the files" and "Display the files"
|
|
263
|
+
- CORRECT: One task with action "Show the files"
|
|
269
264
|
|
|
270
265
|
- "check and verify disk space" →
|
|
271
|
-
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
"verify the disk space",
|
|
275
|
-
]
|
|
276
|
-
- correct: "check the disk space"
|
|
266
|
+
- WRONG: Two tasks with actions "Check the disk space" and "Verify the disk
|
|
267
|
+
space"
|
|
268
|
+
- CORRECT: One task with action "Check the disk space"
|
|
277
269
|
|
|
278
270
|
- "list directory contents completely" →
|
|
279
|
-
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
"show all items",
|
|
283
|
-
]
|
|
284
|
-
- correct: "list all directory contents"
|
|
271
|
+
- WRONG: Two tasks with actions "List the directory contents" and "Show all
|
|
272
|
+
items"
|
|
273
|
+
- CORRECT: One task with action "List all directory contents"
|
|
285
274
|
|
|
286
275
|
- "install and set up dependencies" →
|
|
287
|
-
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
"set up dependencies",
|
|
291
|
-
]
|
|
292
|
-
- correct: "install dependencies"
|
|
276
|
+
- WRONG: Two tasks with actions "Install dependencies" and "Set up
|
|
277
|
+
dependencies"
|
|
278
|
+
- CORRECT: One task with action "Install dependencies"
|
|
293
279
|
|
|
294
280
|
- "delete apps and remove all apps unused in a year" →
|
|
295
|
-
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
]
|
|
300
|
-
- correct: "delete all applications unused in the past year"
|
|
281
|
+
- WRONG: Two tasks with actions "Delete unused applications" and "Remove apps
|
|
282
|
+
not used in the past year"
|
|
283
|
+
- CORRECT: One task with action "Delete all applications unused in the past
|
|
284
|
+
year"
|
|
301
285
|
|
|
302
286
|
### Correct Examples: Single Task
|
|
303
287
|
|
|
304
288
|
Simple requests should remain as single tasks:
|
|
305
289
|
|
|
306
|
-
- "change dir to ~" → "
|
|
307
|
-
|
|
308
|
-
- "
|
|
309
|
-
- "
|
|
310
|
-
|
|
311
|
-
- "
|
|
312
|
-
|
|
290
|
+
- "change dir to ~" → One task with action "Change directory to the home
|
|
291
|
+
folder", type "execute", params { path: "~" }
|
|
292
|
+
- "install deps" → One task with action "Install dependencies", type "execute"
|
|
293
|
+
- "make new file called test.txt" → One task with action "Create a new file
|
|
294
|
+
called test.txt", type "execute", params { filename: "test.txt" }
|
|
295
|
+
- "show me files here" → One task with action "Show the files in the current
|
|
296
|
+
directory", type "execute"
|
|
297
|
+
- "explain quantum physics simply" → One task with action "Explain quantum
|
|
298
|
+
physics in simple terms", type "answer"
|
|
299
|
+
- "check disk space thoroughly" → One task with action "Check the disk space
|
|
300
|
+
thoroughly", type "execute"
|
|
313
301
|
|
|
314
302
|
### Correct Examples: Multiple Tasks
|
|
315
303
|
|
|
316
304
|
Only split when tasks are truly distinct operations:
|
|
317
305
|
|
|
318
|
-
- "install deps, run tests" →
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
[
|
|
325
|
-
"create a file",
|
|
326
|
-
"add content",
|
|
327
|
-
]
|
|
328
|
-
- "build project and deploy" →
|
|
329
|
-
[
|
|
330
|
-
"build the project",
|
|
331
|
-
"deploy",
|
|
332
|
-
]
|
|
306
|
+
- "install deps, run tests" → Two tasks with actions "Install dependencies"
|
|
307
|
+
(type: execute) and "Run tests" (type: execute)
|
|
308
|
+
- "create file; add content" → Two tasks with actions "Create a file" (type:
|
|
309
|
+
execute) and "Add content" (type: execute)
|
|
310
|
+
- "build project and deploy" → Two tasks with actions "Build the project"
|
|
311
|
+
(type: execute) and "Deploy" (type: execute)
|
|
333
312
|
|
|
334
313
|
### Correct Examples: Complex Questions
|
|
335
314
|
|
|
336
315
|
Split only when multiple distinct queries or operations are needed:
|
|
337
316
|
|
|
338
|
-
- "tell me weather in Wro, is it over 70 deg" →
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
"check the disk space",
|
|
351
|
-
"show a warning if it is below 10%",
|
|
352
|
-
]
|
|
353
|
-
- "find config file and show its contents" →
|
|
354
|
-
[
|
|
355
|
-
"find the config file",
|
|
356
|
-
"show its contents",
|
|
357
|
-
]
|
|
317
|
+
- "tell me weather in Wro, is it over 70 deg" → Two tasks:
|
|
318
|
+
1. Action "Show the weather in Wrocław" (type: answer, params { city: "Wrocław" })
|
|
319
|
+
2. Action "Check if the temperature is above 70 degrees" (type: answer)
|
|
320
|
+
- "pls what is 7th prime and how many are to 1000" → Two tasks:
|
|
321
|
+
1. Action "Find the 7th prime number" (type: answer)
|
|
322
|
+
2. Action "Count how many prime numbers are below 1000" (type: answer)
|
|
323
|
+
- "check disk space and warn if below 10%" → Two tasks:
|
|
324
|
+
1. Action "Check the disk space" (type: execute)
|
|
325
|
+
2. Action "Show a warning if it is below 10%" (type: report)
|
|
326
|
+
- "find config file and show its contents" → Two tasks:
|
|
327
|
+
1. Action "Find the config file" (type: execute)
|
|
328
|
+
2. Action "Show its contents" (type: report)
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { dirname, join } from 'path';
|
|
|
6
6
|
import { render, Text } from 'ink';
|
|
7
7
|
import { loadConfig, ConfigError, configExists, saveConfig, } from './services/config.js';
|
|
8
8
|
import { createAnthropicService } from './services/anthropic.js';
|
|
9
|
-
import {
|
|
9
|
+
import { Main } from './ui/Main.js';
|
|
10
10
|
const __filename = fileURLToPath(import.meta.url);
|
|
11
11
|
const __dirname = dirname(__filename);
|
|
12
12
|
// Get package info
|
|
@@ -17,7 +17,7 @@ const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
|
|
|
17
17
|
// In production, we're in node_modules and src/ doesn't exist alongside
|
|
18
18
|
const srcPath = join(__dirname, '../src');
|
|
19
19
|
const isDev = existsSync(srcPath);
|
|
20
|
-
const
|
|
20
|
+
const app = {
|
|
21
21
|
name: packageJson.name,
|
|
22
22
|
version: packageJson.version,
|
|
23
23
|
description: packageJson.description,
|
|
@@ -25,13 +25,14 @@ const appInfo = {
|
|
|
25
25
|
};
|
|
26
26
|
// Get command from command-line arguments
|
|
27
27
|
const args = process.argv.slice(2);
|
|
28
|
-
const
|
|
28
|
+
const command = args.join(' ').trim() || null;
|
|
29
29
|
async function runApp() {
|
|
30
30
|
// First-time setup: config doesn't exist
|
|
31
31
|
if (!configExists()) {
|
|
32
|
-
const { waitUntilExit } = render(_jsx(
|
|
33
|
-
saveConfig(
|
|
34
|
-
|
|
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;
|
|
35
36
|
} }));
|
|
36
37
|
await waitUntilExit();
|
|
37
38
|
return;
|
|
@@ -39,15 +40,9 @@ async function runApp() {
|
|
|
39
40
|
// Try to load and validate config
|
|
40
41
|
try {
|
|
41
42
|
const config = loadConfig();
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
// "pls do stuff": fetch and show the plan
|
|
48
|
-
const claudeService = createAnthropicService(config.anthropic.apiKey, config.anthropic.model);
|
|
49
|
-
render(_jsx(PLS, { app: appInfo, command: rawCommand, claudeService: claudeService }));
|
|
50
|
-
}
|
|
43
|
+
// Create service once at app initialization
|
|
44
|
+
const service = createAnthropicService(config.anthropic);
|
|
45
|
+
render(_jsx(Main, { app: app, command: command, service: service, isReady: true }));
|
|
51
46
|
}
|
|
52
47
|
catch (error) {
|
|
53
48
|
if (error instanceof ConfigError) {
|
|
@@ -1,74 +1,63 @@
|
|
|
1
|
-
import { readFileSync } from 'fs';
|
|
2
|
-
import { fileURLToPath } from 'url';
|
|
3
|
-
import { dirname, join } from 'path';
|
|
4
1
|
import Anthropic from '@anthropic-ai/sdk';
|
|
5
2
|
import { loadSkills, formatSkillsForPrompt } from './skills.js';
|
|
6
|
-
|
|
7
|
-
const __dirname = dirname(__filename);
|
|
8
|
-
const PLAN_PROMPT = readFileSync(join(__dirname, '../config/PLAN.md'), 'utf-8');
|
|
3
|
+
import { toolRegistry } from './tool-registry.js';
|
|
9
4
|
export class AnthropicService {
|
|
10
5
|
client;
|
|
11
6
|
model;
|
|
12
|
-
constructor(
|
|
13
|
-
this.client = new Anthropic({ apiKey });
|
|
7
|
+
constructor(key, model = 'claude-haiku-4-5-20251001') {
|
|
8
|
+
this.client = new Anthropic({ apiKey: key });
|
|
14
9
|
this.model = model;
|
|
15
10
|
}
|
|
16
|
-
async
|
|
17
|
-
// Load
|
|
11
|
+
async processWithTool(command, toolName) {
|
|
12
|
+
// Load tool from registry
|
|
13
|
+
const tool = toolRegistry.getSchema(toolName);
|
|
14
|
+
const instructions = toolRegistry.getInstructions(toolName);
|
|
15
|
+
// Load skills and augment the instructions
|
|
18
16
|
const skills = loadSkills();
|
|
19
17
|
const skillsSection = formatSkillsForPrompt(skills);
|
|
20
|
-
const systemPrompt =
|
|
18
|
+
const systemPrompt = instructions + skillsSection;
|
|
19
|
+
// Call API with tool
|
|
21
20
|
const response = await this.client.messages.create({
|
|
22
21
|
model: this.model,
|
|
23
|
-
max_tokens:
|
|
22
|
+
max_tokens: 1024,
|
|
24
23
|
system: systemPrompt,
|
|
24
|
+
tools: [tool],
|
|
25
|
+
tool_choice: { type: 'any' },
|
|
25
26
|
messages: [
|
|
26
27
|
{
|
|
27
28
|
role: 'user',
|
|
28
|
-
content:
|
|
29
|
+
content: command,
|
|
29
30
|
},
|
|
30
31
|
],
|
|
31
32
|
});
|
|
32
|
-
|
|
33
|
-
if (
|
|
34
|
-
throw new Error('
|
|
33
|
+
// Check for truncation
|
|
34
|
+
if (response.stop_reason === 'max_tokens') {
|
|
35
|
+
throw new Error('Response was truncated due to length. Please simplify your request or break it into smaller parts.');
|
|
35
36
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
const parsed = JSON.parse(text);
|
|
42
|
-
if (Array.isArray(parsed)) {
|
|
43
|
-
// Validate all items are strings
|
|
44
|
-
const allStrings = parsed.every((item) => typeof item === 'string');
|
|
45
|
-
if (allStrings) {
|
|
46
|
-
tasks = parsed.filter((item) => typeof item === 'string');
|
|
47
|
-
}
|
|
48
|
-
else {
|
|
49
|
-
tasks = [text];
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
else {
|
|
53
|
-
tasks = [text];
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
catch {
|
|
57
|
-
// If JSON parsing fails, treat as single task
|
|
58
|
-
tasks = [text];
|
|
59
|
-
}
|
|
37
|
+
// Validate response structure
|
|
38
|
+
if (response.content.length === 0 ||
|
|
39
|
+
response.content[0].type !== 'tool_use') {
|
|
40
|
+
throw new Error('Expected tool_use response from Claude API');
|
|
60
41
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
42
|
+
const content = response.content[0];
|
|
43
|
+
// Extract and validate tasks array
|
|
44
|
+
const input = content.input;
|
|
45
|
+
if (!input.tasks || !Array.isArray(input.tasks)) {
|
|
46
|
+
throw new Error('Invalid tool response: missing or invalid tasks array');
|
|
64
47
|
}
|
|
48
|
+
// Validate each task has required action field
|
|
49
|
+
input.tasks.forEach((task, i) => {
|
|
50
|
+
if (!task.action || typeof task.action !== 'string') {
|
|
51
|
+
throw new Error(`Invalid task at index ${String(i)}: missing or invalid 'action' field`);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
65
54
|
const isDebug = process.env.DEBUG === 'true';
|
|
66
55
|
return {
|
|
67
|
-
tasks,
|
|
56
|
+
tasks: input.tasks,
|
|
68
57
|
systemPrompt: isDebug ? systemPrompt : undefined,
|
|
69
58
|
};
|
|
70
59
|
}
|
|
71
60
|
}
|
|
72
|
-
export function createAnthropicService(
|
|
73
|
-
return new AnthropicService(
|
|
61
|
+
export function createAnthropicService(config) {
|
|
62
|
+
return new AnthropicService(config.key, config.model);
|
|
74
63
|
}
|
package/dist/services/config.js
CHANGED
|
@@ -29,20 +29,19 @@ function validateConfig(parsed) {
|
|
|
29
29
|
throw new ConfigError(`\nMissing or invalid 'anthropic' section in ${CONFIG_FILE}\n` +
|
|
30
30
|
'Please add:\n' +
|
|
31
31
|
'anthropic:\n' +
|
|
32
|
-
'
|
|
32
|
+
' key: sk-ant-...');
|
|
33
33
|
}
|
|
34
34
|
const anthropic = config.anthropic;
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
throw new ConfigError(`\nMissing or invalid 'anthropic.api-key' in ${CONFIG_FILE}\n` +
|
|
35
|
+
const key = anthropic['key'];
|
|
36
|
+
if (!key || typeof key !== 'string') {
|
|
37
|
+
throw new ConfigError(`\nMissing or invalid 'anthropic.key' in ${CONFIG_FILE}\n` +
|
|
39
38
|
'Please add your Anthropic API key:\n' +
|
|
40
39
|
'anthropic:\n' +
|
|
41
|
-
'
|
|
40
|
+
' key: sk-ant-...');
|
|
42
41
|
}
|
|
43
42
|
const validatedConfig = {
|
|
44
43
|
anthropic: {
|
|
45
|
-
|
|
44
|
+
key,
|
|
46
45
|
},
|
|
47
46
|
};
|
|
48
47
|
// Optional model
|
|
@@ -57,7 +56,7 @@ export function loadConfig() {
|
|
|
57
56
|
'Please create it with your Anthropic API key.\n' +
|
|
58
57
|
'Example:\n\n' +
|
|
59
58
|
'anthropic:\n' +
|
|
60
|
-
'
|
|
59
|
+
' key: sk-ant-...\n' +
|
|
61
60
|
' model: claude-haiku-4-5-20251001\n');
|
|
62
61
|
}
|
|
63
62
|
const content = readFileSync(CONFIG_FILE, 'utf-8');
|
|
@@ -72,10 +71,10 @@ export function configExists() {
|
|
|
72
71
|
}
|
|
73
72
|
export function mergeConfig(existingContent, sectionName, newValues) {
|
|
74
73
|
const parsed = existingContent.trim()
|
|
75
|
-
? YAML.parse(existingContent)
|
|
74
|
+
? YAML.parse(existingContent)
|
|
76
75
|
: {};
|
|
77
76
|
// Update or add section
|
|
78
|
-
const section = parsed[sectionName]
|
|
77
|
+
const section = parsed[sectionName] ?? {};
|
|
79
78
|
for (const [key, value] of Object.entries(newValues)) {
|
|
80
79
|
section[key] = value;
|
|
81
80
|
}
|
|
@@ -89,13 +88,10 @@ export function mergeConfig(existingContent, sectionName, newValues) {
|
|
|
89
88
|
// Convert back to YAML
|
|
90
89
|
return YAML.stringify(sortedConfig);
|
|
91
90
|
}
|
|
92
|
-
export function saveConfig(
|
|
91
|
+
export function saveConfig(section, config) {
|
|
93
92
|
const existingContent = existsSync(CONFIG_FILE)
|
|
94
93
|
? readFileSync(CONFIG_FILE, 'utf-8')
|
|
95
94
|
: '';
|
|
96
|
-
const newContent = mergeConfig(existingContent,
|
|
97
|
-
'api-key': apiKey,
|
|
98
|
-
model: model,
|
|
99
|
-
});
|
|
95
|
+
const newContent = mergeConfig(existingContent, section, config);
|
|
100
96
|
writeFileSync(CONFIG_FILE, newContent, 'utf-8');
|
|
101
97
|
}
|
package/dist/services/skills.js
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import { resolve } from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
import { dirname } from 'path';
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = dirname(__filename);
|
|
7
|
+
class ToolRegistry {
|
|
8
|
+
tools = new Map();
|
|
9
|
+
register(name, config) {
|
|
10
|
+
this.tools.set(name, config);
|
|
11
|
+
}
|
|
12
|
+
getTool(name) {
|
|
13
|
+
return this.tools.get(name);
|
|
14
|
+
}
|
|
15
|
+
getInstructions(name) {
|
|
16
|
+
const config = this.getTool(name);
|
|
17
|
+
if (!config) {
|
|
18
|
+
throw new Error(`Tool '${name}' not found in registry`);
|
|
19
|
+
}
|
|
20
|
+
const instructionsPath = resolve(__dirname, '..', config.instructionsPath);
|
|
21
|
+
return readFileSync(instructionsPath, 'utf-8');
|
|
22
|
+
}
|
|
23
|
+
getSchema(name) {
|
|
24
|
+
const config = this.getTool(name);
|
|
25
|
+
if (!config) {
|
|
26
|
+
throw new Error(`Tool '${name}' not found in registry`);
|
|
27
|
+
}
|
|
28
|
+
return config.schema;
|
|
29
|
+
}
|
|
30
|
+
hasTool(name) {
|
|
31
|
+
return this.tools.has(name);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// Create singleton instance
|
|
35
|
+
export const toolRegistry = new ToolRegistry();
|
|
36
|
+
// Register built-in tools
|
|
37
|
+
import { planTool } from '../tools/plan.tool.js';
|
|
38
|
+
toolRegistry.register('plan', {
|
|
39
|
+
schema: planTool,
|
|
40
|
+
instructionsPath: 'config/PLAN.md',
|
|
41
|
+
});
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export const planTool = {
|
|
2
|
+
name: 'plan',
|
|
3
|
+
description: 'Plan and structure tasks from a user command. Break down the request into clear, actionable steps with type information and parameters.',
|
|
4
|
+
input_schema: {
|
|
5
|
+
type: 'object',
|
|
6
|
+
properties: {
|
|
7
|
+
tasks: {
|
|
8
|
+
type: 'array',
|
|
9
|
+
description: 'Array of planned tasks to execute',
|
|
10
|
+
items: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
action: {
|
|
14
|
+
type: 'string',
|
|
15
|
+
description: 'Clear description of what needs to be done in this task',
|
|
16
|
+
},
|
|
17
|
+
type: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
description: 'Type of task: "config" (settings), "plan" (planning), "execute" (shell/programs/finding files), "answer" (questions), "report" (summaries)',
|
|
20
|
+
},
|
|
21
|
+
params: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
description: 'Task-specific parameters (e.g., command, path, url, etc.)',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
required: ['action'],
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
required: ['tasks'],
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/ui/Command.js
CHANGED
|
@@ -2,18 +2,29 @@ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import { useEffect, useState } from 'react';
|
|
3
3
|
import { Box, Text } from 'ink';
|
|
4
4
|
import { Spinner } from './Spinner.js';
|
|
5
|
-
const MIN_PROCESSING_TIME =
|
|
6
|
-
export function Command({
|
|
7
|
-
const
|
|
8
|
-
const [
|
|
9
|
-
const [
|
|
10
|
-
const [
|
|
5
|
+
const MIN_PROCESSING_TIME = 1000; // purely for visual effect
|
|
6
|
+
export function Command({ command, state, service, tasks, error: errorProp, systemPrompt: systemPromptProp, }) {
|
|
7
|
+
const done = state?.done ?? false;
|
|
8
|
+
const [processedTasks, setProcessedTasks] = useState(tasks || []);
|
|
9
|
+
const [systemPrompt, setSystemPrompt] = useState(systemPromptProp);
|
|
10
|
+
const [error, setError] = useState(state?.error || errorProp || null);
|
|
11
|
+
const [isLoading, setIsLoading] = useState(state?.isLoading ?? !done);
|
|
11
12
|
useEffect(() => {
|
|
13
|
+
// Skip processing if done (showing historical/final state)
|
|
14
|
+
if (done) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
// Skip processing if no service available
|
|
18
|
+
if (!service) {
|
|
19
|
+
setError('No service available');
|
|
20
|
+
setIsLoading(false);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
12
23
|
let mounted = true;
|
|
13
|
-
async function process() {
|
|
24
|
+
async function process(svc) {
|
|
14
25
|
const startTime = Date.now();
|
|
15
26
|
try {
|
|
16
|
-
const result = await
|
|
27
|
+
const result = await svc.processWithTool(command, 'plan');
|
|
17
28
|
const elapsed = Date.now() - startTime;
|
|
18
29
|
const remainingTime = Math.max(0, MIN_PROCESSING_TIME - elapsed);
|
|
19
30
|
await new Promise((resolve) => setTimeout(resolve, remainingTime));
|
|
@@ -33,10 +44,10 @@ export function Command({ rawCommand, claudeService }) {
|
|
|
33
44
|
}
|
|
34
45
|
}
|
|
35
46
|
}
|
|
36
|
-
process();
|
|
47
|
+
process(service);
|
|
37
48
|
return () => {
|
|
38
49
|
mounted = false;
|
|
39
50
|
};
|
|
40
|
-
}, [
|
|
41
|
-
return (_jsxs(Box, { alignSelf: "flex-start", marginBottom: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsxs(Text, { color: "gray", children: ["> pls ",
|
|
51
|
+
}, [command, done, service]);
|
|
52
|
+
return (_jsxs(Box, { alignSelf: "flex-start", marginBottom: 1, flexDirection: "column", 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] }) })), processedTasks.length > 0 && (_jsx(Box, { flexDirection: "column", children: processedTasks.map((task, index) => (_jsxs(Box, { children: [_jsx(Text, { color: "whiteBright", children: ' - ' }), _jsx(Text, { color: "white", children: task.action }), task.type && _jsxs(Text, { color: "gray", children: [" (", task.type, ")"] })] }, index))) }))] }));
|
|
42
53
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import TextInput from 'ink-text-input';
|
|
5
|
+
export function Configure({ state, key: keyProp, model: modelProp, onComplete, }) {
|
|
6
|
+
const done = state?.done ?? false;
|
|
7
|
+
const [step, setStep] = React.useState(state?.step ?? (done ? 'done' : 'key'));
|
|
8
|
+
const [key, setKey] = React.useState(keyProp || '');
|
|
9
|
+
const [model, setModel] = React.useState(modelProp || 'claude-haiku-4-5-20251001');
|
|
10
|
+
const handleKeySubmit = (value) => {
|
|
11
|
+
setKey(value);
|
|
12
|
+
setStep('model');
|
|
13
|
+
};
|
|
14
|
+
const handleModelSubmit = (value) => {
|
|
15
|
+
const finalModel = value.trim() || 'claude-haiku-4-5-20251001';
|
|
16
|
+
setModel(finalModel);
|
|
17
|
+
setStep('done');
|
|
18
|
+
if (onComplete) {
|
|
19
|
+
onComplete({ key, model: finalModel });
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
return (_jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [!done && _jsx(Text, { children: "Configuration required." }), !done && (_jsx(Box, { children: _jsxs(Text, { color: "whiteBright", dimColor: true, children: ['==>', " Get your API key from: https://platform.claude.com/"] }) })), _jsx(Box, { marginTop: done ? 0 : 1, children: _jsx(Text, { children: "Anthropic API key:" }) }), _jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: "> " }), step === 'key' && !done ? (_jsx(TextInput, { value: key, onChange: setKey, onSubmit: handleKeySubmit, mask: "*" })) : (_jsx(Text, { dimColor: true, children: '*'.repeat(12) }))] }), (step === 'model' || step === 'done') && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Box, { children: _jsxs(Text, { children: ["Model", ' ', !done && (_jsx(Text, { dimColor: true, children: "(default: claude-haiku-4-5-20251001)" })), ":"] }) }), _jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: "> " }), step === 'model' && !done ? (_jsx(TextInput, { value: model, onChange: setModel, onSubmit: handleModelSubmit })) : (_jsx(Text, { dimColor: true, children: model }))] })] })), step === 'done' && !done && (_jsx(Box, { marginY: 1, children: _jsx(Text, { color: "green", children: "\u2713 Configuration saved" }) }))] }));
|
|
23
|
+
}
|
package/dist/ui/History.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Box } from 'ink';
|
|
3
|
+
import { renderComponent } from './renderComponent.js';
|
|
3
4
|
export function History({ items }) {
|
|
4
5
|
if (items.length === 0) {
|
|
5
6
|
return null;
|
|
6
7
|
}
|
|
7
|
-
return (_jsx(Box, { flexDirection: "column", gap: 1, children: items.map((item, index) => (_jsx(Box, { children: item }, index))) }));
|
|
8
|
+
return (_jsx(Box, { flexDirection: "column", gap: 1, children: items.map((item, index) => (_jsx(Box, { children: renderComponent(item) }, `${item.name}-${index}`))) }));
|
|
8
9
|
}
|
package/dist/ui/Main.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Box } from 'ink';
|
|
4
|
+
import { History } from './History.js';
|
|
5
|
+
import { renderComponent } from './renderComponent.js';
|
|
6
|
+
export const Main = ({ app, command, service, isReady, onConfigured, }) => {
|
|
7
|
+
const [history, setHistory] = React.useState([]);
|
|
8
|
+
const [current, setCurrent] = React.useState(null);
|
|
9
|
+
React.useEffect(() => {
|
|
10
|
+
// Initialize history and current component based on props
|
|
11
|
+
if (!isReady) {
|
|
12
|
+
// Not configured - show welcome in history, configure as current
|
|
13
|
+
setHistory([
|
|
14
|
+
{
|
|
15
|
+
name: 'welcome',
|
|
16
|
+
props: {
|
|
17
|
+
app,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
]);
|
|
21
|
+
setCurrent({
|
|
22
|
+
name: 'configure',
|
|
23
|
+
state: {
|
|
24
|
+
done: false,
|
|
25
|
+
step: 'key',
|
|
26
|
+
},
|
|
27
|
+
props: {
|
|
28
|
+
onComplete: onConfigured,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
else if (command && service) {
|
|
33
|
+
setCurrent({
|
|
34
|
+
name: 'command',
|
|
35
|
+
state: {
|
|
36
|
+
done: false,
|
|
37
|
+
isLoading: true,
|
|
38
|
+
},
|
|
39
|
+
props: {
|
|
40
|
+
command,
|
|
41
|
+
service,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
setCurrent({
|
|
47
|
+
name: 'welcome',
|
|
48
|
+
props: {
|
|
49
|
+
app,
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}, [isReady, command, service, app, onConfigured]);
|
|
54
|
+
return (_jsxs(Box, { marginTop: 1, flexDirection: "column", gap: 1, children: [_jsx(History, { items: history }), current && renderComponent(current)] }));
|
|
55
|
+
};
|
package/dist/ui/Welcome.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { Text, Box } from 'ink';
|
|
3
|
-
export function Welcome({
|
|
3
|
+
export function Welcome({ app: app }) {
|
|
4
4
|
const descriptionLines = app.description
|
|
5
5
|
.split('. ')
|
|
6
6
|
.map((line) => line.replace(/\.$/, ''))
|
|
@@ -9,5 +9,5 @@ export function Welcome({ info: app }) {
|
|
|
9
9
|
const words = app.name
|
|
10
10
|
.split('-')
|
|
11
11
|
.map((word) => word.charAt(0).toUpperCase() + word.slice(1));
|
|
12
|
-
return (_jsx(Box, { alignSelf: "flex-start", children: _jsxs(Box, { borderStyle: "round", borderColor: "green", paddingX: 3, paddingY: 1, flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, gap: 1, children: [words.map((word, index) => (_jsx(Text, { color: "greenBright", bold: true, children: word }, index))), _jsxs(Text, { color: "whiteBright", dimColor: true, children: ["v", app.version] }), app.isDev && _jsx(Text, { color: "yellowBright", children: "dev" })] }), descriptionLines.map((line, index) => (_jsx(Box, { children: _jsxs(Text, { color: "white", children: [line, "."] }) }, index))), _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]" })] })] })] })] }) }));
|
|
12
|
+
return (_jsx(Box, { alignSelf: "flex-start", marginBottom: 1, children: _jsxs(Box, { borderStyle: "round", borderColor: "green", paddingX: 3, paddingY: 1, flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, gap: 1, children: [words.map((word, index) => (_jsx(Text, { color: "greenBright", bold: true, children: word }, index))), _jsxs(Text, { color: "whiteBright", dimColor: true, children: ["v", app.version] }), app.isDev && _jsx(Text, { color: "yellowBright", children: "dev" })] }), descriptionLines.map((line, index) => (_jsx(Box, { children: _jsxs(Text, { color: "white", children: [line, "."] }) }, index))), _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]" })] })] })] })] }) }));
|
|
13
13
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Welcome } from './Welcome.js';
|
|
3
|
+
import { Configure } from './Configure.js';
|
|
4
|
+
import { Command } from './Command.js';
|
|
5
|
+
export function renderComponent(def) {
|
|
6
|
+
switch (def.name) {
|
|
7
|
+
case 'welcome':
|
|
8
|
+
return _jsx(Welcome, { ...def.props });
|
|
9
|
+
case 'configure':
|
|
10
|
+
return (_jsx(Configure, { ...def.props, state: 'state' in def ? def.state : undefined }));
|
|
11
|
+
case 'command':
|
|
12
|
+
return (_jsx(Command, { ...def.props, state: 'state' in def ? def.state : undefined }));
|
|
13
|
+
}
|
|
14
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prompt-language-shell",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
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",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"prepare": "npm run build",
|
|
17
17
|
"test": "vitest run",
|
|
18
18
|
"test:watch": "vitest",
|
|
19
|
-
"format": "prettier --write
|
|
20
|
-
"format:check": "prettier --check
|
|
19
|
+
"format": "prettier --write '**/*.{ts,tsx}'",
|
|
20
|
+
"format:check": "prettier --check '**/*.{ts,tsx}'",
|
|
21
21
|
"lint": "eslint .",
|
|
22
22
|
"lint:fix": "eslint --fix ."
|
|
23
23
|
},
|