prompt-language-shell 0.5.0 → 0.6.0
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 +114 -10
- package/dist/config/ANSWER.md +4 -0
- package/dist/config/INTROSPECT.md +4 -3
- package/dist/config/PLAN.md +23 -0
- package/dist/config/VALIDATE.md +12 -11
- package/dist/services/components.js +54 -64
- package/dist/services/configuration.js +84 -0
- package/dist/services/messages.js +22 -0
- package/dist/services/queue.js +2 -2
- package/dist/services/refinement.js +36 -0
- package/dist/services/task-router.js +135 -0
- package/dist/types/types.js +0 -1
- package/dist/ui/Answer.js +18 -27
- package/dist/ui/Command.js +45 -27
- package/dist/ui/Component.js +23 -50
- package/dist/ui/Config.js +49 -24
- package/dist/ui/Confirm.js +17 -11
- package/dist/ui/Execute.js +66 -45
- package/dist/ui/Feedback.js +1 -1
- package/dist/ui/Introspect.js +27 -23
- package/dist/ui/Main.js +71 -100
- package/dist/ui/Message.js +1 -1
- package/dist/ui/Plan.js +54 -32
- package/dist/ui/Refinement.js +6 -7
- package/dist/ui/Report.js +1 -1
- package/dist/ui/UserQuery.js +6 -0
- package/dist/ui/Validate.js +49 -19
- package/dist/ui/Welcome.js +12 -5
- package/dist/ui/Workflow.js +119 -0
- package/package.json +1 -1
- package/dist/handlers/answer.js +0 -21
- package/dist/handlers/command.js +0 -34
- package/dist/handlers/config.js +0 -88
- package/dist/handlers/execute.js +0 -46
- package/dist/handlers/execution.js +0 -140
- package/dist/handlers/introspect.js +0 -21
- package/dist/handlers/plan.js +0 -79
- package/dist/types/handlers.js +0 -1
- package/dist/ui/AnswerDisplay.js +0 -8
- package/dist/ui/Column.js +0 -7
package/README.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
Your personal command-line concierge. Ask politely, and it gets things done.
|
|
4
4
|
|
|
5
|
+
> **Note:** This project is in early preview. Features and APIs may change as
|
|
6
|
+
> development continues.
|
|
7
|
+
|
|
5
8
|
## Installation
|
|
6
9
|
|
|
7
10
|
```bash
|
|
@@ -14,30 +17,74 @@ On first run, `pls` walks you through a quick setup. Your settings will be saved
|
|
|
14
17
|
|
|
15
18
|
## Usage
|
|
16
19
|
|
|
17
|
-
Type `pls` followed by your request in natural language
|
|
20
|
+
Type `pls` followed by your request in natural language.
|
|
21
|
+
|
|
22
|
+
To see what `pls` can
|
|
23
|
+
do, start by listing available capabilities:
|
|
18
24
|
|
|
19
|
-
```bash
|
|
20
|
-
pls change dir to ~
|
|
21
25
|
```
|
|
26
|
+
$ pls list skills
|
|
27
|
+
|
|
28
|
+
Here's what I can help with:
|
|
29
|
+
|
|
30
|
+
- Introspect - list available capabilities and skills
|
|
31
|
+
- Config - manage and configure system settings
|
|
32
|
+
- Answer - respond to questions and provide information
|
|
33
|
+
- Execute - run shell commands and process operations
|
|
34
|
+
```
|
|
22
35
|
|
|
23
|
-
|
|
36
|
+
Skills are custom workflows you can define to teach `pls` about your specific
|
|
37
|
+
projects and commands. Once defined, you can use them naturally:
|
|
24
38
|
|
|
25
39
|
```
|
|
26
|
-
|
|
27
|
-
|
|
40
|
+
$ pls build project
|
|
41
|
+
|
|
42
|
+
Here's my plan.
|
|
43
|
+
|
|
44
|
+
- Navigate to project directory
|
|
45
|
+
- Compile source code
|
|
28
46
|
```
|
|
29
47
|
|
|
30
48
|
You can provide multiple requests at once:
|
|
31
49
|
|
|
32
50
|
```
|
|
33
|
-
|
|
51
|
+
$ pls install deps, run tests and build
|
|
52
|
+
|
|
53
|
+
Here's what I'll do.
|
|
54
|
+
|
|
34
55
|
- Install dependencies
|
|
35
56
|
- Run tests
|
|
36
|
-
-
|
|
57
|
+
- Build the project
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
When `pls` needs clarification, it will present options to choose from:
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
$ pls deploy
|
|
64
|
+
|
|
65
|
+
Let me clarify.
|
|
66
|
+
|
|
67
|
+
→ Choose which environment to deploy to:
|
|
68
|
+
- Deploy to staging
|
|
69
|
+
- Deploy to production
|
|
37
70
|
```
|
|
38
71
|
|
|
39
72
|
Run `pls` without arguments to see the welcome screen.
|
|
40
73
|
|
|
74
|
+
## How It Works
|
|
75
|
+
|
|
76
|
+
When you make a request, `pls` interprets your intent and creates a structured
|
|
77
|
+
plan breaking down the work into individual tasks. You'll see this plan
|
|
78
|
+
displayed in your terminal before anything executes.
|
|
79
|
+
|
|
80
|
+
After reviewing the plan, you can confirm to proceed or cancel if something
|
|
81
|
+
doesn't look right. Once confirmed, `pls` executes each task sequentially and
|
|
82
|
+
shows real-time progress and results.
|
|
83
|
+
|
|
84
|
+
If you've defined custom skills, `pls` uses them to understand your
|
|
85
|
+
project-specific workflows and translate high-level requests into the exact
|
|
86
|
+
commands your environment requires.
|
|
87
|
+
|
|
41
88
|
## Configuration
|
|
42
89
|
|
|
43
90
|
Your configuration is stored in `~/.plsrc` as a YAML file. Supported settings:
|
|
@@ -47,9 +94,66 @@ Your configuration is stored in `~/.plsrc` as a YAML file. Supported settings:
|
|
|
47
94
|
|
|
48
95
|
## Skills
|
|
49
96
|
|
|
50
|
-
|
|
97
|
+
Skills let you teach `pls` about your project-specific workflows. Create
|
|
98
|
+
markdown files in `~/.pls/skills/` to define custom operations that `pls` can
|
|
99
|
+
understand and execute.
|
|
100
|
+
|
|
101
|
+
### Structure
|
|
102
|
+
|
|
103
|
+
Each skill file uses a simple markdown format:
|
|
104
|
+
|
|
105
|
+
- **Name**: What you call this workflow (e.g., "Build Project")
|
|
106
|
+
- **Description**: What it does and any variants or options
|
|
107
|
+
- **Steps**: What needs to happen, in order
|
|
108
|
+
- **Execution** (optional): The actual shell commands to run
|
|
109
|
+
|
|
110
|
+
### Example
|
|
111
|
+
|
|
112
|
+
Here's a skill that builds different project variants:
|
|
113
|
+
|
|
114
|
+
```markdown
|
|
115
|
+
### Name
|
|
116
|
+
Build Project
|
|
117
|
+
|
|
118
|
+
### Description
|
|
119
|
+
Build a project in different configurations:
|
|
120
|
+
- dev (debug build with source maps)
|
|
121
|
+
- prod (optimized build)
|
|
122
|
+
- test (with test coverage)
|
|
123
|
+
|
|
124
|
+
### Steps
|
|
125
|
+
- Navigate to the project directory
|
|
126
|
+
- Install dependencies if needed
|
|
127
|
+
- Run the {ENV} build script
|
|
128
|
+
- Generate build artifacts
|
|
51
129
|
|
|
52
|
-
|
|
130
|
+
### Execution
|
|
131
|
+
- cd ~/projects/next
|
|
132
|
+
- npm install
|
|
133
|
+
- npm run build:{ENV}
|
|
134
|
+
- cp -r dist/ builds/{ENV}/
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
With this skill defined, you can use natural language like:
|
|
138
|
+
```
|
|
139
|
+
$ pls build project for production
|
|
140
|
+
$ pls build dev environment
|
|
141
|
+
$ pls build with testing enabled
|
|
142
|
+
```
|
|
143
|
+
The `{ENV}` placeholder gets replaced with the variant you specify.
|
|
144
|
+
Instead of remembering the exact commands and paths for each environment, just
|
|
145
|
+
tell `pls` what you want in plain English. The Execution section ensures the right commands run every time.
|
|
146
|
+
|
|
147
|
+
### Keep It Short
|
|
148
|
+
|
|
149
|
+
Skills also work with concise commands. Once you've taught `pls` about your
|
|
150
|
+
workflow, you can use minimal phrasing:
|
|
151
|
+
|
|
152
|
+
```
|
|
153
|
+
$ pls build prod
|
|
154
|
+
$ pls build dev
|
|
155
|
+
$ pls build test
|
|
156
|
+
```
|
|
53
157
|
|
|
54
158
|
## Development
|
|
55
159
|
|
package/dist/config/ANSWER.md
CHANGED
|
@@ -42,6 +42,8 @@ Provide a direct, helpful answer following these strict formatting rules:
|
|
|
42
42
|
- Break long sentences naturally at phrase boundaries
|
|
43
43
|
- If the answer requires more than 4 lines, prioritize the most essential
|
|
44
44
|
information
|
|
45
|
+
- **Do NOT use citation tags** like `<cite>` or any HTML/XML markup
|
|
46
|
+
- Provide direct answers in plain text only
|
|
45
47
|
|
|
46
48
|
## Examples
|
|
47
49
|
|
|
@@ -112,8 +114,10 @@ They enable cleaner, more reusable component logic.
|
|
|
112
114
|
❌ Including unnecessary details
|
|
113
115
|
❌ Using overly technical jargon without explanation
|
|
114
116
|
❌ Repeating the question in the answer
|
|
117
|
+
❌ Using citation tags like `<cite>` or any HTML/XML markup
|
|
115
118
|
|
|
116
119
|
✅ Direct, concise answers
|
|
117
120
|
✅ Proper line breaks at natural phrase boundaries
|
|
118
121
|
✅ Essential information only
|
|
119
122
|
✅ Clear, accessible language
|
|
123
|
+
✅ Plain text only - no markup tags
|
|
@@ -133,9 +133,10 @@ Examples:
|
|
|
133
133
|
|
|
134
134
|
When user asks "list your skills", create an introductory message like "here
|
|
135
135
|
are my capabilities:" followed by tasks for built-in capabilities (Introspect,
|
|
136
|
-
Config, Answer, Execute), then indirect workflow capabilities (
|
|
137
|
-
Report).
|
|
138
|
-
|
|
136
|
+
Config, Answer, Execute), then indirect workflow capabilities (Plan, Validate,
|
|
137
|
+
Report).
|
|
138
|
+
|
|
139
|
+
Each task uses type "introspect" with an action describing the capability.
|
|
139
140
|
|
|
140
141
|
### Example 2: Filtered Skills
|
|
141
142
|
|
package/dist/config/PLAN.md
CHANGED
|
@@ -280,6 +280,29 @@ Examples that should be aborted as offensive:
|
|
|
280
280
|
- Requests to create malware or exploit vulnerabilities
|
|
281
281
|
- Requests with offensive, discriminatory, or abusive language
|
|
282
282
|
|
|
283
|
+
**CRITICAL: Distinguishing Questions from Actions**
|
|
284
|
+
|
|
285
|
+
User requests fall into two categories:
|
|
286
|
+
|
|
287
|
+
1. **Information requests (questions)** - Must use question keywords:
|
|
288
|
+
- "explain", "answer", "describe", "tell me", "say", "what is", "what are",
|
|
289
|
+
"how does", "how do", "find", "search", "lookup"
|
|
290
|
+
- Example: "pls explain TypeScript" → answer type
|
|
291
|
+
- Example: "pls what is the weather" → answer type
|
|
292
|
+
|
|
293
|
+
2. **Action requests (commands)** - Must match available skills:
|
|
294
|
+
- Verbs like "test", "deploy", "process", "backup", "sync"
|
|
295
|
+
- If verb matches a skill → use that skill
|
|
296
|
+
- If verb does NOT match any skill → use "ignore" type
|
|
297
|
+
- Example: "pls test" with no test skill → ignore type
|
|
298
|
+
- Example: "pls reverberate" with no reverberate skill → ignore type
|
|
299
|
+
- Example: "pls shut down" with no shutdown skill → ignore type
|
|
300
|
+
|
|
301
|
+
**Critical rule:** Requests using action verbs that don't match question
|
|
302
|
+
keywords AND don't match any available skills should ALWAYS be classified
|
|
303
|
+
as "ignore" type. Do NOT try to infer or create generic execute tasks for
|
|
304
|
+
unrecognized verbs.
|
|
305
|
+
|
|
283
306
|
**For requests with clear intent:**
|
|
284
307
|
|
|
285
308
|
1. **Introspection requests** - Use "introspect" type when request asks about
|
package/dist/config/VALIDATE.md
CHANGED
|
@@ -29,12 +29,12 @@ For each CONFIG task, create a natural language description that:
|
|
|
29
29
|
|
|
30
30
|
## Description Format
|
|
31
31
|
|
|
32
|
-
**Format:** "Brief description" (
|
|
32
|
+
**Format:** "Brief description" (DO NOT include {config.path}!)
|
|
33
33
|
|
|
34
34
|
The description should:
|
|
35
35
|
- Start with what the config value represents (e.g., "Path to...", "URL for...", "Name of...")
|
|
36
36
|
- Be SHORT and direct - no extra details or variant explanations
|
|
37
|
-
-
|
|
37
|
+
- NEVER include the config path in curly brackets like {config.path}
|
|
38
38
|
|
|
39
39
|
## Examples
|
|
40
40
|
|
|
@@ -50,7 +50,7 @@ The description should:
|
|
|
50
50
|
message: ""
|
|
51
51
|
tasks: [
|
|
52
52
|
{
|
|
53
|
-
action: "Path to Alpha repository
|
|
53
|
+
action: "Path to Alpha repository",
|
|
54
54
|
type: "config",
|
|
55
55
|
params: { key: "project.alpha.repo" }
|
|
56
56
|
}
|
|
@@ -69,7 +69,7 @@ tasks: [
|
|
|
69
69
|
message: ""
|
|
70
70
|
tasks: [
|
|
71
71
|
{
|
|
72
|
-
action: "Staging environment URL
|
|
72
|
+
action: "Staging environment URL",
|
|
73
73
|
type: "config",
|
|
74
74
|
params: { key: "env.staging.url" }
|
|
75
75
|
}
|
|
@@ -88,7 +88,7 @@ tasks: [
|
|
|
88
88
|
message: ""
|
|
89
89
|
tasks: [
|
|
90
90
|
{
|
|
91
|
-
action: "Path to Beta workspace
|
|
91
|
+
action: "Path to Beta workspace",
|
|
92
92
|
type: "config",
|
|
93
93
|
params: { key: "workspace.beta.path" }
|
|
94
94
|
}
|
|
@@ -98,10 +98,10 @@ tasks: [
|
|
|
98
98
|
## Guidelines
|
|
99
99
|
|
|
100
100
|
1. **Use skill context**: Read the skill's Description section to understand what the variant represents
|
|
101
|
-
2. **Be specific**: Don't just say "Repository path" - say "Alpha
|
|
102
|
-
3. **Add helpful details**: Include information from the description
|
|
103
|
-
4. **Keep it concise**: One
|
|
104
|
-
5. **
|
|
101
|
+
2. **Be specific**: Don't just say "Repository path" - say "Path to Alpha repository"
|
|
102
|
+
3. **Add helpful details**: Include information from the description when relevant
|
|
103
|
+
4. **Keep it concise**: One brief phrase that clearly explains what's needed
|
|
104
|
+
5. **Never include the path**: Do not append `{config.path}` - it's shown separately in debug mode
|
|
105
105
|
|
|
106
106
|
## Common Config Types
|
|
107
107
|
|
|
@@ -122,7 +122,7 @@ Return a message field (can be empty string) and an array of CONFIG tasks:
|
|
|
122
122
|
message: ""
|
|
123
123
|
tasks: [
|
|
124
124
|
{
|
|
125
|
-
action: "Natural description
|
|
125
|
+
action: "Natural description without config path",
|
|
126
126
|
type: "config",
|
|
127
127
|
params: { key: "config.path" }
|
|
128
128
|
},
|
|
@@ -136,4 +136,5 @@ tasks: [
|
|
|
136
136
|
- All tasks must include params.key with the config path
|
|
137
137
|
- Descriptions should be helpful and contextual, not just technical
|
|
138
138
|
- Use information from Available Skills section to provide context
|
|
139
|
-
- Keep descriptions to one
|
|
139
|
+
- Keep descriptions to one brief phrase (3-6 words)
|
|
140
|
+
- NEVER include the config path in the action/description - it's shown separately
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
3
|
import { ComponentName } from '../types/types.js';
|
|
3
|
-
import {
|
|
4
|
+
import { parse as parseYaml } from 'yaml';
|
|
5
|
+
import { getConfigPath, getConfigSchema, loadConfig, } from './configuration.js';
|
|
4
6
|
import { getConfirmationMessage } from './messages.js';
|
|
5
7
|
import { StepType } from '../ui/Config.js';
|
|
6
|
-
export function markAsDone(component) {
|
|
7
|
-
return { ...component, state: { ...component.state, done: true } };
|
|
8
|
-
}
|
|
9
8
|
export function createWelcomeDefinition(app) {
|
|
10
9
|
return {
|
|
11
10
|
id: randomUUID(),
|
|
@@ -58,27 +57,43 @@ function getValidator(definition) {
|
|
|
58
57
|
export function createConfigStepsFromSchema(keys) {
|
|
59
58
|
const schema = getConfigSchema();
|
|
60
59
|
let currentConfig = null;
|
|
60
|
+
let rawConfig = null;
|
|
61
|
+
// Load validated config (may fail if config has validation errors)
|
|
61
62
|
try {
|
|
62
63
|
currentConfig = loadConfig();
|
|
63
64
|
}
|
|
64
65
|
catch {
|
|
65
|
-
// Config doesn't exist
|
|
66
|
+
// Config doesn't exist or has validation errors, use defaults
|
|
67
|
+
}
|
|
68
|
+
// Load raw config separately (for discovered keys not in schema)
|
|
69
|
+
try {
|
|
70
|
+
const configFile = getConfigPath();
|
|
71
|
+
if (existsSync(configFile)) {
|
|
72
|
+
const content = readFileSync(configFile, 'utf-8');
|
|
73
|
+
rawConfig = parseYaml(content);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Config file doesn't exist or can't be parsed
|
|
66
78
|
}
|
|
67
79
|
return keys.map((key) => {
|
|
68
80
|
// Check if key is in schema (built-in config)
|
|
69
81
|
if (!(key in schema)) {
|
|
70
|
-
// Key is not in schema - it's from a skill
|
|
71
|
-
// Create a simple text step with
|
|
82
|
+
// Key is not in schema - it's from a skill or discovered config
|
|
83
|
+
// Create a simple text step with the full path as description
|
|
72
84
|
const keyParts = key.split('.');
|
|
73
85
|
const shortKey = keyParts[keyParts.length - 1];
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
86
|
+
// Load current value if it exists (use rawConfig since discovered keys aren't in validated config)
|
|
87
|
+
const currentValue = getConfigValue(rawConfig, key);
|
|
88
|
+
const value = currentValue !== undefined && typeof currentValue === 'string'
|
|
89
|
+
? currentValue
|
|
90
|
+
: null;
|
|
77
91
|
return {
|
|
78
|
-
description:
|
|
92
|
+
description: key,
|
|
79
93
|
key: shortKey,
|
|
94
|
+
path: key,
|
|
80
95
|
type: StepType.Text,
|
|
81
|
-
value
|
|
96
|
+
value,
|
|
82
97
|
validate: () => true, // Accept any string for now
|
|
83
98
|
};
|
|
84
99
|
}
|
|
@@ -98,6 +113,7 @@ export function createConfigStepsFromSchema(keys) {
|
|
|
98
113
|
return {
|
|
99
114
|
description: definition.description,
|
|
100
115
|
key: shortKey,
|
|
116
|
+
path: key,
|
|
101
117
|
type: StepType.Text,
|
|
102
118
|
value,
|
|
103
119
|
validate: getValidator(definition),
|
|
@@ -112,6 +128,7 @@ export function createConfigStepsFromSchema(keys) {
|
|
|
112
128
|
return {
|
|
113
129
|
description: definition.description,
|
|
114
130
|
key: shortKey,
|
|
131
|
+
path: key,
|
|
115
132
|
type: StepType.Text,
|
|
116
133
|
value,
|
|
117
134
|
validate: getValidator(definition),
|
|
@@ -127,6 +144,7 @@ export function createConfigStepsFromSchema(keys) {
|
|
|
127
144
|
return {
|
|
128
145
|
description: definition.description,
|
|
129
146
|
key: shortKey,
|
|
147
|
+
path: key,
|
|
130
148
|
type: StepType.Selection,
|
|
131
149
|
options: definition.values.map((value) => ({
|
|
132
150
|
label: value,
|
|
@@ -143,6 +161,7 @@ export function createConfigStepsFromSchema(keys) {
|
|
|
143
161
|
return {
|
|
144
162
|
description: definition.description,
|
|
145
163
|
key: shortKey,
|
|
164
|
+
path: key,
|
|
146
165
|
type: StepType.Selection,
|
|
147
166
|
options: [
|
|
148
167
|
{ label: 'Yes', value: 'true' },
|
|
@@ -159,7 +178,7 @@ export function createConfigDefinition(onFinished, onAborted) {
|
|
|
159
178
|
return {
|
|
160
179
|
id: randomUUID(),
|
|
161
180
|
name: ComponentName.Config,
|
|
162
|
-
state: {
|
|
181
|
+
state: {},
|
|
163
182
|
props: {
|
|
164
183
|
steps: createConfigSteps(),
|
|
165
184
|
onFinished,
|
|
@@ -174,7 +193,7 @@ export function createConfigDefinitionWithKeys(keys, onFinished, onAborted) {
|
|
|
174
193
|
return {
|
|
175
194
|
id: randomUUID(),
|
|
176
195
|
name: ComponentName.Config,
|
|
177
|
-
state: {
|
|
196
|
+
state: {},
|
|
178
197
|
props: {
|
|
179
198
|
steps: createConfigStepsFromSchema(keys),
|
|
180
199
|
onFinished,
|
|
@@ -182,29 +201,22 @@ export function createConfigDefinitionWithKeys(keys, onFinished, onAborted) {
|
|
|
182
201
|
},
|
|
183
202
|
};
|
|
184
203
|
}
|
|
185
|
-
export function createCommandDefinition(command, service
|
|
204
|
+
export function createCommandDefinition(command, service) {
|
|
186
205
|
return {
|
|
187
206
|
id: randomUUID(),
|
|
188
207
|
name: ComponentName.Command,
|
|
189
|
-
state: {
|
|
190
|
-
done: false,
|
|
191
|
-
isLoading: true,
|
|
192
|
-
},
|
|
208
|
+
state: {},
|
|
193
209
|
props: {
|
|
194
210
|
command,
|
|
195
211
|
service,
|
|
196
|
-
onError,
|
|
197
|
-
onComplete,
|
|
198
|
-
onAborted,
|
|
199
212
|
},
|
|
200
213
|
};
|
|
201
214
|
}
|
|
202
|
-
export function createPlanDefinition(message, tasks,
|
|
215
|
+
export function createPlanDefinition(message, tasks, onSelectionConfirmed) {
|
|
203
216
|
return {
|
|
204
217
|
id: randomUUID(),
|
|
205
218
|
name: ComponentName.Plan,
|
|
206
219
|
state: {
|
|
207
|
-
done: false,
|
|
208
220
|
highlightedIndex: null,
|
|
209
221
|
currentDefineGroupIndex: 0,
|
|
210
222
|
completedSelections: [],
|
|
@@ -213,7 +225,6 @@ export function createPlanDefinition(message, tasks, onAborted, onSelectionConfi
|
|
|
213
225
|
message,
|
|
214
226
|
tasks,
|
|
215
227
|
onSelectionConfirmed,
|
|
216
|
-
onAborted,
|
|
217
228
|
},
|
|
218
229
|
};
|
|
219
230
|
}
|
|
@@ -240,7 +251,7 @@ export function createRefinement(text, onAborted) {
|
|
|
240
251
|
return {
|
|
241
252
|
id: randomUUID(),
|
|
242
253
|
name: ComponentName.Refinement,
|
|
243
|
-
state: {
|
|
254
|
+
state: {},
|
|
244
255
|
props: {
|
|
245
256
|
text,
|
|
246
257
|
onAborted,
|
|
@@ -251,7 +262,7 @@ export function createConfirmDefinition(onConfirmed, onCancelled) {
|
|
|
251
262
|
return {
|
|
252
263
|
id: randomUUID(),
|
|
253
264
|
name: ComponentName.Confirm,
|
|
254
|
-
state: {
|
|
265
|
+
state: {},
|
|
255
266
|
props: {
|
|
256
267
|
message: getConfirmationMessage(),
|
|
257
268
|
onConfirmed,
|
|
@@ -259,20 +270,14 @@ export function createConfirmDefinition(onConfirmed, onCancelled) {
|
|
|
259
270
|
},
|
|
260
271
|
};
|
|
261
272
|
}
|
|
262
|
-
export function createIntrospectDefinition(tasks, service
|
|
273
|
+
export function createIntrospectDefinition(tasks, service) {
|
|
263
274
|
return {
|
|
264
275
|
id: randomUUID(),
|
|
265
276
|
name: ComponentName.Introspect,
|
|
266
|
-
state: {
|
|
267
|
-
done: false,
|
|
268
|
-
isLoading: true,
|
|
269
|
-
},
|
|
277
|
+
state: {},
|
|
270
278
|
props: {
|
|
271
279
|
tasks,
|
|
272
280
|
service,
|
|
273
|
-
onError,
|
|
274
|
-
onComplete,
|
|
275
|
-
onAborted,
|
|
276
281
|
},
|
|
277
282
|
};
|
|
278
283
|
}
|
|
@@ -286,49 +291,37 @@ export function createReportDefinition(message, capabilities) {
|
|
|
286
291
|
},
|
|
287
292
|
};
|
|
288
293
|
}
|
|
289
|
-
export function createAnswerDefinition(question, service
|
|
294
|
+
export function createAnswerDefinition(question, service) {
|
|
290
295
|
return {
|
|
291
296
|
id: randomUUID(),
|
|
292
297
|
name: ComponentName.Answer,
|
|
293
|
-
state: {
|
|
294
|
-
done: false,
|
|
295
|
-
isLoading: true,
|
|
296
|
-
},
|
|
298
|
+
state: {},
|
|
297
299
|
props: {
|
|
298
300
|
question,
|
|
299
301
|
service,
|
|
300
|
-
onError,
|
|
301
|
-
onComplete,
|
|
302
|
-
onAborted,
|
|
303
|
-
},
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
export function createAnswerDisplayDefinition(answer) {
|
|
307
|
-
return {
|
|
308
|
-
id: randomUUID(),
|
|
309
|
-
name: ComponentName.AnswerDisplay,
|
|
310
|
-
props: {
|
|
311
|
-
answer,
|
|
312
302
|
},
|
|
313
303
|
};
|
|
314
304
|
}
|
|
315
305
|
export function isStateless(component) {
|
|
316
306
|
return !('state' in component);
|
|
317
307
|
}
|
|
318
|
-
|
|
308
|
+
/**
|
|
309
|
+
* Mark a component as done. Returns the component to be added to timeline.
|
|
310
|
+
* Components use handlers.updateState to save their state before completion,
|
|
311
|
+
* so this function simply returns the component as-is.
|
|
312
|
+
*/
|
|
313
|
+
export function markAsDone(component) {
|
|
314
|
+
// State already updated via handlers.updateState
|
|
315
|
+
return component;
|
|
316
|
+
}
|
|
317
|
+
export function createExecuteDefinition(tasks, service) {
|
|
319
318
|
return {
|
|
320
319
|
id: randomUUID(),
|
|
321
320
|
name: ComponentName.Execute,
|
|
322
|
-
state: {
|
|
323
|
-
done: false,
|
|
324
|
-
isLoading: true,
|
|
325
|
-
},
|
|
321
|
+
state: {},
|
|
326
322
|
props: {
|
|
327
323
|
tasks,
|
|
328
324
|
service,
|
|
329
|
-
onError,
|
|
330
|
-
onComplete,
|
|
331
|
-
onAborted,
|
|
332
325
|
},
|
|
333
326
|
};
|
|
334
327
|
}
|
|
@@ -336,10 +329,7 @@ export function createValidateDefinition(missingConfig, userRequest, service, on
|
|
|
336
329
|
return {
|
|
337
330
|
id: randomUUID(),
|
|
338
331
|
name: ComponentName.Validate,
|
|
339
|
-
state: {
|
|
340
|
-
done: false,
|
|
341
|
-
isLoading: true,
|
|
342
|
-
},
|
|
332
|
+
state: {},
|
|
343
333
|
props: {
|
|
344
334
|
missingConfig,
|
|
345
335
|
userRequest,
|
|
@@ -123,6 +123,7 @@ export function saveConfig(section, config) {
|
|
|
123
123
|
}
|
|
124
124
|
export function saveAnthropicConfig(config) {
|
|
125
125
|
saveConfig('anthropic', config);
|
|
126
|
+
return loadConfig();
|
|
126
127
|
}
|
|
127
128
|
export function saveDebugSetting(debug) {
|
|
128
129
|
saveConfig('settings', { debug });
|
|
@@ -200,6 +201,67 @@ export function getConfigSchema() {
|
|
|
200
201
|
// Future: ...loadSkillSchemas()
|
|
201
202
|
};
|
|
202
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* Get missing required configuration keys
|
|
206
|
+
* Returns array of keys that are required but not present or invalid in config
|
|
207
|
+
*/
|
|
208
|
+
export function getMissingConfigKeys() {
|
|
209
|
+
const schema = getConfigSchema();
|
|
210
|
+
const missing = [];
|
|
211
|
+
let currentConfig = null;
|
|
212
|
+
try {
|
|
213
|
+
currentConfig = loadConfig();
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
// Config doesn't exist
|
|
217
|
+
}
|
|
218
|
+
for (const [key, definition] of Object.entries(schema)) {
|
|
219
|
+
if (!definition.required) {
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
// Get current value for this key
|
|
223
|
+
const parts = key.split('.');
|
|
224
|
+
let value = currentConfig;
|
|
225
|
+
for (const part of parts) {
|
|
226
|
+
if (value && typeof value === 'object' && part in value) {
|
|
227
|
+
value = value[part];
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
value = undefined;
|
|
231
|
+
break;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
// Check if value is missing or invalid
|
|
235
|
+
if (value === undefined || value === null) {
|
|
236
|
+
missing.push(key);
|
|
237
|
+
continue;
|
|
238
|
+
}
|
|
239
|
+
// Validate based on type
|
|
240
|
+
let isValid = false;
|
|
241
|
+
switch (definition.type) {
|
|
242
|
+
case 'regexp':
|
|
243
|
+
isValid = typeof value === 'string' && definition.pattern.test(value);
|
|
244
|
+
break;
|
|
245
|
+
case 'string':
|
|
246
|
+
isValid = typeof value === 'string';
|
|
247
|
+
break;
|
|
248
|
+
case 'enum':
|
|
249
|
+
isValid =
|
|
250
|
+
typeof value === 'string' && definition.values.includes(value);
|
|
251
|
+
break;
|
|
252
|
+
case 'number':
|
|
253
|
+
isValid = typeof value === 'number';
|
|
254
|
+
break;
|
|
255
|
+
case 'boolean':
|
|
256
|
+
isValid = typeof value === 'boolean';
|
|
257
|
+
break;
|
|
258
|
+
}
|
|
259
|
+
if (!isValid) {
|
|
260
|
+
missing.push(key);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return missing;
|
|
264
|
+
}
|
|
203
265
|
/**
|
|
204
266
|
* Get available config structure for CONFIG tool
|
|
205
267
|
* Returns keys with descriptions only (no values for privacy)
|
|
@@ -246,3 +308,25 @@ export function getAvailableConfigStructure() {
|
|
|
246
308
|
}
|
|
247
309
|
return structure;
|
|
248
310
|
}
|
|
311
|
+
/**
|
|
312
|
+
* Unflatten dotted keys into nested structure
|
|
313
|
+
* Example: { "product.alpha.path": "value" } -> { product: { alpha: { path: "value" } } }
|
|
314
|
+
*/
|
|
315
|
+
export function unflattenConfig(dotted) {
|
|
316
|
+
const result = {};
|
|
317
|
+
for (const [dottedKey, value] of Object.entries(dotted)) {
|
|
318
|
+
const parts = dottedKey.split('.');
|
|
319
|
+
const section = parts[0];
|
|
320
|
+
// Initialize section if needed
|
|
321
|
+
result[section] = result[section] ?? {};
|
|
322
|
+
// Build nested structure for this section
|
|
323
|
+
let current = result[section];
|
|
324
|
+
for (let i = 1; i < parts.length - 1; i++) {
|
|
325
|
+
current[parts[i]] = current[parts[i]] ?? {};
|
|
326
|
+
current = current[parts[i]];
|
|
327
|
+
}
|
|
328
|
+
// Set final value
|
|
329
|
+
current[parts[parts.length - 1]] = value;
|
|
330
|
+
}
|
|
331
|
+
return result;
|
|
332
|
+
}
|