prompt-language-shell 0.4.9 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md 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
- Your command will be interpreted and organized into a list of tasks:
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
- > pls change dir to ~
27
- - Change directory to the home folder
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
- > pls install deps, run tests and deploy
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
- - Deploy to server
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
- You can extend `pls` with custom workflows by creating markdown files in `~/.pls/skills/`. Skills define domain-specific operations, parameters, and steps that guide both planning and execution of tasks.
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
- Your skills are referenced when planning requests, enabling `pls` to understand specific workflows, create and execute structured plans tailored to your environment.
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
 
@@ -75,12 +75,13 @@ These MUST appear FIRST, in this EXACT sequence:
75
75
  3. **Answer** ← ALWAYS THIRD
76
76
  4. **Execute** ← ALWAYS FOURTH
77
77
 
78
- ### Position 5-6: Indirect Workflow Capabilities
78
+ ### Position 5-7: Indirect Workflow Capabilities
79
79
 
80
80
  These MUST appear AFTER Execute and BEFORE user skills:
81
81
 
82
82
  5. **Plan** ← NEVER FIRST, ALWAYS position 5 (after Execute)
83
- 6. **Report** ← NEVER FIRST, ALWAYS position 6 (after Plan)
83
+ 6. **Validate** ← ALWAYS position 6 (after Plan)
84
+ 7. **Report** ← NEVER FIRST, ALWAYS position 7 (after Validate)
84
85
 
85
86
  ### 3. User-Defined Skills
86
87
 
@@ -132,7 +133,9 @@ Examples:
132
133
 
133
134
  When user asks "list your skills", create an introductory message like "here
134
135
  are my capabilities:" followed by tasks for built-in capabilities (Introspect,
135
- Config, Answer, Execute), then indirect workflow capabilities (Plan, Report).
136
+ Config, Answer, Execute), then indirect workflow capabilities (Plan, Validate,
137
+ Report).
138
+
136
139
  Each task uses type "introspect" with an action describing the capability.
137
140
 
138
141
  ### Example 2: Filtered Skills
@@ -146,8 +149,9 @@ with its description.
146
149
 
147
150
  When user asks "what can you do" and user-defined skills like "process data"
148
151
  and "backup files" exist, create an introductory message like "i can help with
149
- these operations:" followed by all built-in capabilities plus the user-defined
150
- skills. Each capability and skill becomes a task with type "introspect".
152
+ these operations:" followed by all built-in capabilities (Introspect, Config,
153
+ Answer, Execute, Validate, Plan, Report) plus the user-defined skills. Each
154
+ capability and skill becomes a task with type "introspect".
151
155
 
152
156
  ## Final Validation
153
157
 
@@ -113,6 +113,26 @@ executable operations.
113
113
  Extract the individual steps from the skill's "Execution" or "Steps"
114
114
  section (prefer Execution if available)
115
115
  - Replace ALL parameter placeholders with the specified value
116
+ - **CRITICAL - Variant Placeholder Resolution**: If the execution commands
117
+ contain variant placeholders (any uppercase word in a placeholder path,
118
+ e.g., {section.VARIANT.property}, {project.TARGET.path}, {env.TYPE.name}),
119
+ you MUST:
120
+ 1. Identify the variant name from the user's request (e.g., "alpha", "beta")
121
+ 2. Normalize the variant to lowercase (e.g., "alpha", "beta")
122
+ 3. Replace the uppercase placeholder component with the actual variant name
123
+ in ALL task actions
124
+ 4. Examples:
125
+ - User says "process alpha target" → variant is "alpha"
126
+ - Execution line: `cd {project.VARIANT.path}`
127
+ - Task action MUST be: `cd {project.alpha.path}` (NOT `cd {project.VARIANT.path}`)
128
+ - User says "deploy to staging environment" → variant is "staging"
129
+ - Execution line: `setup {env.TYPE.config}`
130
+ - Task action MUST be: `setup {env.staging.config}` (NOT `setup {env.TYPE.config}`)
131
+ 5. This applies to ALL placeholders in task actions, whether from direct
132
+ execution lines or from referenced skills (e.g., [Navigate To Target])
133
+ 6. The uppercase word can be ANY name (VARIANT, TARGET, TYPE, PRODUCT, etc.) -
134
+ all uppercase path components indicate variant placeholders that must
135
+ be resolved
116
136
 
117
137
  4. **Handle partial execution:**
118
138
  - Keywords indicating partial execution: "only", "just", specific verbs
@@ -129,11 +149,16 @@ executable operations.
129
149
  - type: category of operation (if the skill specifies it or you can infer it)
130
150
  - params: MUST include:
131
151
  - skill: the skill name (REQUIRED for all skill-based tasks)
132
- - All parameter values used in the step (e.g., target, environment, etc.)
152
+ - variant: the resolved variant value (REQUIRED if skill has variant placeholders)
153
+ - All other parameter values used in the step (e.g., target, environment, etc.)
133
154
  - Any other specific parameters mentioned in the step
134
155
  - NEVER replace the skill's detailed steps with a generic restatement
135
156
  - The params.skill field is CRITICAL for execution to use the skill's
136
157
  Execution section
158
+ - The params.variant field is CRITICAL for config validation to resolve
159
+ variant placeholders in the skill's Execution section
160
+ - Example: If user selects "Deploy to production" and skill has {env.VARIANT.url},
161
+ params must include variant: "production" so validator can resolve to {env.production.url}
137
162
 
138
163
  6. **Handle additional requirements beyond the skill:**
139
164
  - If the user's query includes additional requirements beyond the skill,
@@ -157,6 +182,19 @@ Example 1 - Skill with parameter, variant specified:
157
182
  params: { skill: "Process Data", target: "Alpha" } }
158
183
  - WRONG: Tasks without params.skill or single task "Process Alpha"
159
184
 
185
+ Example 1b - Skill with variant placeholder in config:
186
+ - Skill name: "Navigate To Target"
187
+ - Skill config defines: target.alpha.path, target.beta.path, target.gamma.path
188
+ - Skill execution: "cd {target.VARIANT.path}"
189
+ - User: "navigate to beta"
190
+ - Variant matched: "beta"
191
+ - Correct task: { action: "Navigate to Beta target directory", type: "execute",
192
+ params: { skill: "Navigate To Target", variant: "beta" } }
193
+ - WRONG: params without variant field
194
+ - WRONG: task action "cd {target.VARIANT.path}" (uppercase VARIANT not resolved!)
195
+ - Note: The config validator will use params.variant="beta" to resolve
196
+ {target.VARIANT.path} → {target.beta.path}, then check if it exists in ~/.plsrc
197
+
160
198
  Example 2 - Skill with parameter, variant NOT specified:
161
199
  - Same skill as Example 1
162
200
  - User: "process"
@@ -0,0 +1,139 @@
1
+ ## Overview
2
+
3
+ You are the validation component of "pls" (please), responsible for validating skill requirements and generating natural language descriptions for missing configuration values.
4
+
5
+ Your role is to help users understand what configuration values are needed and why, using context from skill descriptions to create clear, helpful prompts.
6
+
7
+ ## Input
8
+
9
+ You will receive information about missing configuration values:
10
+ - Config path (e.g., "project.alpha.repo")
11
+ - Skill name that requires this config
12
+ - Variant (if applicable)
13
+ - Config type (string, boolean, number)
14
+
15
+ ## Your Task
16
+
17
+ Generate a response with two required fields:
18
+
19
+ 1. **message**: An empty string `""`
20
+ 2. **tasks**: An array of CONFIG tasks, one for each missing config value
21
+
22
+ For each CONFIG task, create a natural language description that:
23
+
24
+ 1. **Explains what the value is for** using context from the skill's description
25
+ 2. **Keeps it SHORT** - one brief phrase (3-6 words max)
26
+ 3. **Does NOT include the config path** - the path will be shown separately in debug mode
27
+
28
+ **CRITICAL**: You MUST include both the `message` field (set to empty string) and the `tasks` array in your response.
29
+
30
+ ## Description Format
31
+
32
+ **Format:** "Brief description" (NO {config.path} at the end!)
33
+
34
+ The description should:
35
+ - Start with what the config value represents (e.g., "Path to...", "URL for...", "Name of...")
36
+ - Be SHORT and direct - no extra details or variant explanations
37
+ - NOT include the config path in curly brackets - that's added automatically
38
+
39
+ ## Examples
40
+
41
+ ### Example 1: Repository Path
42
+
43
+ **Input:**
44
+ - Config path: `project.alpha.repo`
45
+ - Skill: "Navigate To Project"
46
+ - Variant: "alpha"
47
+
48
+ **Correct output:**
49
+ ```
50
+ message: ""
51
+ tasks: [
52
+ {
53
+ action: "Path to Alpha repository {project.alpha.repo}",
54
+ type: "config",
55
+ params: { key: "project.alpha.repo" }
56
+ }
57
+ ]
58
+ ```
59
+
60
+ ### Example 2: Environment URL
61
+
62
+ **Input:**
63
+ - Config path: `env.staging.url`
64
+ - Skill: "Deploy Service"
65
+ - Variant: "staging"
66
+
67
+ **Correct output:**
68
+ ```
69
+ message: ""
70
+ tasks: [
71
+ {
72
+ action: "Staging environment URL {env.staging.url}",
73
+ type: "config",
74
+ params: { key: "env.staging.url" }
75
+ }
76
+ ]
77
+ ```
78
+
79
+ ### Example 3: Project Directory
80
+
81
+ **Input:**
82
+ - Config path: `workspace.beta.path`
83
+ - Skill: "Process Workspace"
84
+ - Variant: "beta"
85
+
86
+ **Correct output:**
87
+ ```
88
+ message: ""
89
+ tasks: [
90
+ {
91
+ action: "Path to Beta workspace {workspace.beta.path}",
92
+ type: "config",
93
+ params: { key: "workspace.beta.path" }
94
+ }
95
+ ]
96
+ ```
97
+
98
+ ## Guidelines
99
+
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 project repository path"
102
+ 3. **Add helpful details**: Include information from the description (e.g., "legacy implementation")
103
+ 4. **Keep it concise**: One sentence that clearly explains what's needed
104
+ 5. **Always include the path**: End with `{config.path}` for technical reference
105
+
106
+ ## Common Config Types
107
+
108
+ - **repo / repository**: "Path to [name] repository"
109
+ - **path / dir / directory**: "Path to [name] directory"
110
+ - **url**: "[Name] URL"
111
+ - **host**: "[Name] host address"
112
+ - **port**: "[Name] port number"
113
+ - **name**: "Name of [context]"
114
+ - **key / token / secret**: "[Name] authentication key/token/secret"
115
+ - **enabled**: "Enable/disable [feature]"
116
+
117
+ ## Response Format
118
+
119
+ Return a message field (can be empty string) and an array of CONFIG tasks:
120
+
121
+ ```
122
+ message: ""
123
+ tasks: [
124
+ {
125
+ action: "Natural description {config.path}",
126
+ type: "config",
127
+ params: { key: "config.path" }
128
+ },
129
+ // ... more tasks
130
+ ]
131
+ ```
132
+
133
+ ## Important Notes
134
+
135
+ - All tasks must have type "config"
136
+ - All tasks must include params.key with the config path
137
+ - Descriptions should be helpful and contextual, not just technical
138
+ - Use information from Available Skills section to provide context
139
+ - Keep descriptions to one concise sentence
@@ -1,6 +1,6 @@
1
1
  import { ComponentName, FeedbackType } from '../types/types.js';
2
2
  import { createAnthropicService, } from '../services/anthropic.js';
3
- import { createCommandDefinition, createFeedback, markAsDone, } from '../services/components.js';
3
+ import { createCommandDefinition, createExecuteDefinition, createFeedback, markAsDone, } from '../services/components.js';
4
4
  import { saveAnthropicConfig, saveConfig } from '../services/configuration.js';
5
5
  import { FeedbackMessages } from '../services/messages.js';
6
6
  import { exitApp } from '../services/process.js';
@@ -33,18 +33,27 @@ export function createConfigHandlers(ops, handleAborted, command, commandHandler
33
33
  }
34
34
  /**
35
35
  * Creates config execution finished handler for CONFIG skill
36
- * Saves arbitrary config keys and exits
36
+ * Saves arbitrary config keys and optionally continues with execution
37
37
  */
38
- export function createConfigExecutionFinishedHandler(addToTimeline, keys) {
38
+ export function createConfigExecutionFinishedHandler(addToTimeline, keys, tasks, service, executeHandlers) {
39
39
  return (config) => {
40
+ // Group by top-level section
40
41
  const sections = {};
41
42
  for (const fullKey of keys) {
42
43
  const parts = fullKey.split('.');
43
44
  const shortKey = parts[parts.length - 1];
44
- const section = parts.slice(0, -1).join('.');
45
- sections[section] = sections[section] ?? {};
45
+ const topSection = parts[0];
46
+ // Initialize section if needed
47
+ sections[topSection] = sections[topSection] ?? {};
46
48
  if (shortKey in config) {
47
- sections[section][shortKey] = config[shortKey];
49
+ const value = config[shortKey];
50
+ // Build nested structure recursively
51
+ let current = sections[topSection];
52
+ for (let i = 1; i < parts.length - 1; i++) {
53
+ current[parts[i]] = current[parts[i]] ?? {};
54
+ current = current[parts[i]];
55
+ }
56
+ current[parts[parts.length - 1]] = value;
48
57
  }
49
58
  }
50
59
  for (const [section, sectionConfig] of Object.entries(sections)) {
@@ -52,6 +61,14 @@ export function createConfigExecutionFinishedHandler(addToTimeline, keys) {
52
61
  }
53
62
  return withQueueHandler(ComponentName.Config, (first, rest) => {
54
63
  addToTimeline(markAsDone(first), createFeedback(FeedbackType.Succeeded, FeedbackMessages.ConfigurationComplete));
64
+ // If tasks are provided, continue with execution
65
+ if (tasks && service && executeHandlers) {
66
+ return [
67
+ ...rest,
68
+ createExecuteDefinition(tasks, service, executeHandlers.onError, executeHandlers.onComplete, executeHandlers.onAborted),
69
+ ];
70
+ }
71
+ // Otherwise, exit (legacy behavior for initial setup)
55
72
  exitApp(0);
56
73
  return rest;
57
74
  }, false, 0);
@@ -8,6 +8,7 @@ import { withQueueHandler } from '../services/queue.js';
8
8
  * Creates all execute handlers
9
9
  */
10
10
  export function createExecuteHandlers(ops, handleAborted) {
11
+ void handleAborted;
11
12
  const onError = (error) => {
12
13
  ops.setQueue(withQueueHandler(ComponentName.Execute, (first) => {
13
14
  ops.addToTimeline(markAsDone(first), createFeedback(FeedbackType.Failed, error));
@@ -31,8 +32,15 @@ export function createExecuteHandlers(ops, handleAborted) {
31
32
  return [];
32
33
  }));
33
34
  };
34
- const onAborted = () => {
35
- handleAborted('Execution');
35
+ const onAborted = (elapsedTime) => {
36
+ ops.setQueue(withQueueHandler(ComponentName.Execute, (first) => {
37
+ const message = elapsedTime > 0
38
+ ? `The execution was cancelled after ${formatDuration(elapsedTime)}.`
39
+ : 'The execution was cancelled.';
40
+ ops.addToTimeline(markAsDone(first), createFeedback(FeedbackType.Aborted, message));
41
+ exitApp(0);
42
+ return [];
43
+ }));
36
44
  };
37
45
  return { onError, onComplete, onAborted };
38
46
  }
@@ -1,9 +1,11 @@
1
1
  import { ComponentName, FeedbackType, TaskType } from '../types/types.js';
2
- import { createAnswerDefinition, createConfigDefinitionWithKeys, createExecuteDefinition, createFeedback, createIntrospectDefinition, markAsDone, } from '../services/components.js';
2
+ import { createAnswerDefinition, createConfigDefinitionWithKeys, createExecuteDefinition, createFeedback, createIntrospectDefinition, createValidateDefinition, markAsDone, } from '../services/components.js';
3
+ import { StepType } from '../ui/Config.js';
3
4
  import { getCancellationMessage } from '../services/messages.js';
4
5
  import { exitApp } from '../services/process.js';
5
6
  import { withQueueHandler } from '../services/queue.js';
6
7
  import { createConfigExecutionAbortedHandler, createConfigExecutionFinishedHandler, } from './config.js';
8
+ import { validateExecuteTasks } from '../services/execution-validator.js';
7
9
  /**
8
10
  * Creates all execution handlers
9
11
  */
@@ -49,6 +51,71 @@ export function createExecutionHandlers(ops, taskHandlers) {
49
51
  ];
50
52
  }
51
53
  else if (allExecute && tasks.length > 0) {
54
+ // Validate config requirements before execution
55
+ const missingConfig = validateExecuteTasks(tasks);
56
+ if (missingConfig.length > 0) {
57
+ // Config is missing - call VALIDATE tool to get contextual descriptions
58
+ const keys = missingConfig.map((req) => req.path);
59
+ const userRequest = tasks.map((t) => t.action).join(', ');
60
+ ops.addToTimeline(markAsDone(first));
61
+ // Create handlers for Validate completion
62
+ const handleValidateComplete = (configWithDescriptions) => {
63
+ ops.setQueue(withQueueHandler(ComponentName.Validate, (first) => {
64
+ // Create CONFIG component with descriptions from VALIDATE
65
+ const handleConfigFinished = (config) => {
66
+ ops.setQueue(createConfigExecutionFinishedHandler(ops.addToTimeline, keys, tasks, service, taskHandlers.execute)(config));
67
+ };
68
+ const handleConfigAborted = () => {
69
+ ops.setQueue(createConfigExecutionAbortedHandler(ops.addToTimeline)());
70
+ };
71
+ // Create config steps from validated descriptions
72
+ const steps = configWithDescriptions.map((req) => {
73
+ const keyParts = req.path.split('.');
74
+ const shortKey = keyParts[keyParts.length - 1];
75
+ // Extract description without the {path} suffix
76
+ // Format from VALIDATE: "Description {path}"
77
+ let description = req.description || req.path;
78
+ const pathPattern = /\s*\{[^}]+\}\s*$/;
79
+ description = description.replace(pathPattern, '').trim();
80
+ const step = {
81
+ description,
82
+ key: shortKey,
83
+ path: req.path,
84
+ type: StepType.Text,
85
+ value: null,
86
+ validate: () => true,
87
+ };
88
+ return step;
89
+ });
90
+ // Mark Validate as done and move to timeline
91
+ ops.addToTimeline(markAsDone(first));
92
+ return [
93
+ {
94
+ id: crypto.randomUUID(),
95
+ name: ComponentName.Config,
96
+ state: { done: false },
97
+ props: {
98
+ steps,
99
+ onFinished: handleConfigFinished,
100
+ onAborted: handleConfigAborted,
101
+ },
102
+ },
103
+ ];
104
+ }));
105
+ };
106
+ const handleValidateError = (error) => {
107
+ ops.addToTimeline(createFeedback(FeedbackType.Failed, error));
108
+ exitApp(1);
109
+ };
110
+ const handleValidateAborted = () => {
111
+ ops.addToTimeline(createFeedback(FeedbackType.Aborted, 'Configuration validation cancelled'));
112
+ exitApp(0);
113
+ };
114
+ return [
115
+ createValidateDefinition(missingConfig, userRequest, service, handleValidateError, handleValidateComplete, handleValidateAborted),
116
+ ];
117
+ }
118
+ // No missing config - execute directly
52
119
  ops.addToTimeline(markAsDone(first));
53
120
  return [
54
121
  createExecuteDefinition(tasks, service, taskHandlers.execute.onError, taskHandlers.execute.onComplete, taskHandlers.execute.onAborted),
@@ -18,7 +18,8 @@ export class AnthropicService {
18
18
  // Add skills section for applicable tools
19
19
  if (toolName === 'plan' ||
20
20
  toolName === 'introspect' ||
21
- toolName === 'execute') {
21
+ toolName === 'execute' ||
22
+ toolName === 'validate') {
22
23
  const skills = loadSkills();
23
24
  const skillsSection = formatSkillsForPrompt(skills);
24
25
  systemPrompt += skillsSection;
@@ -114,7 +115,7 @@ export class AnthropicService {
114
115
  };
115
116
  }
116
117
  // Handle plan and introspect tool responses
117
- if (!input.message || typeof input.message !== 'string') {
118
+ if (input.message === undefined || typeof input.message !== 'string') {
118
119
  throw new Error('Invalid tool response: missing or invalid message field');
119
120
  }
120
121
  if (!input.tasks || !Array.isArray(input.tasks)) {
@@ -15,8 +15,8 @@ export const Palette = {
15
15
  BrightGreen: '#3e9a3e',
16
16
  Yellow: '#cccc5c',
17
17
  LightYellow: '#d4d47a',
18
- Orange: '#cc9c5c',
19
- DarkOrange: '#a85c3f',
18
+ Orange: '#f48c80',
19
+ DarkOrange: '#ab5e40',
20
20
  BurntOrange: '#cc7a5c',
21
21
  Red: '#cc5c5c',
22
22
  Cyan: '#5c9ccc',
@@ -65,8 +65,22 @@ export function createConfigStepsFromSchema(keys) {
65
65
  // Config doesn't exist yet, use defaults
66
66
  }
67
67
  return keys.map((key) => {
68
+ // Check if key is in schema (built-in config)
68
69
  if (!(key in schema)) {
69
- throw new Error(`Unknown config key: ${key}`);
70
+ // Key is not in schema - it's from a skill
71
+ // Create a simple text step with placeholder description
72
+ const keyParts = key.split('.');
73
+ const shortKey = keyParts[keyParts.length - 1];
74
+ const description = keyParts
75
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
76
+ .join(' ');
77
+ return {
78
+ description: `${description} {${key}}`,
79
+ key: shortKey,
80
+ type: StepType.Text,
81
+ value: null,
82
+ validate: () => true, // Accept any string for now
83
+ };
70
84
  }
71
85
  const definition = schema[key];
72
86
  const currentValue = getConfigValue(currentConfig, key);
@@ -318,3 +332,21 @@ export function createExecuteDefinition(tasks, service, onError, onComplete, onA
318
332
  },
319
333
  };
320
334
  }
335
+ export function createValidateDefinition(missingConfig, userRequest, service, onError, onComplete, onAborted) {
336
+ return {
337
+ id: randomUUID(),
338
+ name: ComponentName.Validate,
339
+ state: {
340
+ done: false,
341
+ isLoading: true,
342
+ },
343
+ props: {
344
+ missingConfig,
345
+ userRequest,
346
+ service,
347
+ onError,
348
+ onComplete,
349
+ onAborted,
350
+ },
351
+ };
352
+ }