prompt-language-shell 0.7.6 → 0.8.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.
@@ -165,6 +165,7 @@ export class AnthropicService {
165
165
  });
166
166
  return {
167
167
  message: input.message,
168
+ summary: input.summary,
168
169
  tasks: [],
169
170
  commands: input.commands,
170
171
  debug,
@@ -1,5 +1,6 @@
1
1
  import { FeedbackType, TaskType } from '../types/types.js';
2
2
  import { DebugLevel } from './configuration.js';
3
+ import { ExecutionStatus } from './shell.js';
3
4
  /**
4
5
  * Base color palette - raw color values with descriptive names.
5
6
  * All colors used in the interface are defined here.
@@ -7,7 +8,6 @@ import { DebugLevel } from './configuration.js';
7
8
  export const Palette = {
8
9
  White: '#ffffff',
9
10
  AshGray: '#d0d0d0',
10
- PaleGreen: '#a8dcbc',
11
11
  Gray: '#888888',
12
12
  DarkGray: '#666666',
13
13
  CharcoalGray: '#282828',
@@ -15,8 +15,8 @@ export const Palette = {
15
15
  LightGreen: '#65b595',
16
16
  BrightGreen: '#3e9a3e',
17
17
  Yellow: '#cccc5c',
18
- LightYellow: '#d4d47a',
19
18
  Orange: '#f48c80',
19
+ MediumOrange: '#d07560',
20
20
  DarkOrange: '#ab5e40',
21
21
  BurntOrange: '#cc7a5c',
22
22
  Red: '#cc5c5c',
@@ -46,7 +46,7 @@ export const Colors = {
46
46
  Status: {
47
47
  Success: Palette.BrightGreen,
48
48
  Error: Palette.Red,
49
- Warning: Palette.Orange,
49
+ Warning: Palette.MediumOrange,
50
50
  Info: Palette.Cyan,
51
51
  },
52
52
  Label: {
@@ -129,7 +129,7 @@ const taskColors = {
129
129
  const feedbackColors = {
130
130
  [FeedbackType.Info]: Colors.Status.Info,
131
131
  [FeedbackType.Succeeded]: Colors.Status.Success,
132
- [FeedbackType.Aborted]: Colors.Status.Warning,
132
+ [FeedbackType.Aborted]: Palette.MediumOrange,
133
133
  [FeedbackType.Failed]: Colors.Status.Error,
134
134
  };
135
135
  /**
@@ -210,3 +210,69 @@ export function getTaskTypeLabel(type, debug) {
210
210
  }
211
211
  return type;
212
212
  }
213
+ /**
214
+ * Status icons for execution states
215
+ */
216
+ export const STATUS_ICONS = {
217
+ [ExecutionStatus.Pending]: '- ',
218
+ [ExecutionStatus.Running]: '• ',
219
+ [ExecutionStatus.Success]: '✓ ',
220
+ [ExecutionStatus.Failed]: '✗ ',
221
+ [ExecutionStatus.Aborted]: '⊘ ',
222
+ [ExecutionStatus.Cancelled]: '⊘ ',
223
+ };
224
+ /**
225
+ * Get colors for different execution status states.
226
+ *
227
+ * Returns color scheme for:
228
+ * - Icon: Status indicator symbol
229
+ * - Description: Task description text
230
+ * - Command: Command text
231
+ * - Symbol: Command prefix symbol
232
+ */
233
+ export function getStatusColors(status) {
234
+ switch (status) {
235
+ case ExecutionStatus.Pending:
236
+ return {
237
+ icon: Palette.Gray,
238
+ description: Palette.Gray,
239
+ command: Palette.DarkGray,
240
+ symbol: Palette.DarkGray,
241
+ };
242
+ case ExecutionStatus.Running:
243
+ return {
244
+ icon: Palette.Gray,
245
+ description: getTextColor(true),
246
+ command: Palette.LightGreen,
247
+ symbol: Palette.AshGray,
248
+ };
249
+ case ExecutionStatus.Success:
250
+ return {
251
+ icon: Colors.Status.Success,
252
+ description: Palette.AshGray,
253
+ command: Palette.Gray,
254
+ symbol: Palette.Gray,
255
+ };
256
+ case ExecutionStatus.Failed:
257
+ return {
258
+ icon: Colors.Status.Error,
259
+ description: Colors.Status.Error,
260
+ command: Colors.Status.Error,
261
+ symbol: Palette.Gray,
262
+ };
263
+ case ExecutionStatus.Aborted:
264
+ return {
265
+ icon: Palette.MediumOrange,
266
+ description: Palette.Gray,
267
+ command: Palette.MediumOrange,
268
+ symbol: Palette.Gray,
269
+ };
270
+ case ExecutionStatus.Cancelled:
271
+ return {
272
+ icon: Palette.DarkGray,
273
+ description: Palette.DarkGray,
274
+ command: Palette.DarkGray,
275
+ symbol: Palette.DarkGray,
276
+ };
277
+ }
278
+ }
@@ -1,8 +1,8 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { existsSync, readFileSync } from 'node:fs';
3
- import { ComponentName } from '../types/types.js';
4
- import { ComponentStatus, } from '../types/components.js';
5
3
  import { parse as parseYaml } from 'yaml';
4
+ import { ComponentStatus, } from '../types/components.js';
5
+ import { ComponentName } from '../types/types.js';
6
6
  import { ConfigDefinitionType, getConfigPath, getConfigSchema, loadConfig, } from './configuration.js';
7
7
  import { getConfirmationMessage } from './messages.js';
8
8
  import { StepType } from '../ui/Config.js';
@@ -180,7 +180,11 @@ export function createConfigDefinition(onFinished, onAborted) {
180
180
  id: randomUUID(),
181
181
  name: ComponentName.Config,
182
182
  status: ComponentStatus.Awaiting,
183
- state: {},
183
+ state: {
184
+ values: {},
185
+ completedStep: 0,
186
+ selectedIndex: 0,
187
+ },
184
188
  props: {
185
189
  steps: createConfigSteps(),
186
190
  onFinished,
@@ -196,7 +200,11 @@ export function createConfigDefinitionWithKeys(keys, onFinished, onAborted) {
196
200
  id: randomUUID(),
197
201
  name: ComponentName.Config,
198
202
  status: ComponentStatus.Awaiting,
199
- state: {},
203
+ state: {
204
+ values: {},
205
+ completedStep: 0,
206
+ selectedIndex: 0,
207
+ },
200
208
  props: {
201
209
  steps: createConfigStepsFromSchema(keys),
202
210
  onFinished,
@@ -338,7 +346,15 @@ export function createExecuteDefinition(tasks, service) {
338
346
  id: randomUUID(),
339
347
  name: ComponentName.Execute,
340
348
  status: ComponentStatus.Awaiting,
341
- state: {},
349
+ state: {
350
+ error: null,
351
+ message: '',
352
+ summary: '',
353
+ taskInfos: [],
354
+ completed: 0,
355
+ taskExecutionTimes: [],
356
+ completionMessage: null,
357
+ },
342
358
  props: {
343
359
  tasks,
344
360
  service,
@@ -350,7 +366,12 @@ export function createValidateDefinition(missingConfig, userRequest, service, on
350
366
  id: randomUUID(),
351
367
  name: ComponentName.Validate,
352
368
  status: ComponentStatus.Awaiting,
353
- state: {},
369
+ state: {
370
+ error: null,
371
+ completionMessage: null,
372
+ configRequirements: null,
373
+ validated: false,
374
+ },
354
375
  props: {
355
376
  missingConfig,
356
377
  userRequest,
@@ -361,3 +382,11 @@ export function createValidateDefinition(missingConfig, userRequest, service, on
361
382
  },
362
383
  };
363
384
  }
385
+ /**
386
+ * Add debug components to timeline if present in result
387
+ */
388
+ export function addDebugToTimeline(debugComponents, handlers) {
389
+ if (debugComponents && debugComponents.length > 0 && handlers) {
390
+ handlers.addToTimeline(...debugComponents);
391
+ }
392
+ }
@@ -1,9 +1,8 @@
1
- import { TaskType } from '../types/types.js';
1
+ import { FeedbackType, TaskType } from '../types/types.js';
2
2
  import { createAnswerDefinition, createConfigDefinitionWithKeys, createConfirmDefinition, createExecuteDefinition, createFeedback, createIntrospectDefinition, createMessage, createScheduleDefinition, createValidateDefinition, } from './components.js';
3
3
  import { saveConfig, unflattenConfig } from './configuration.js';
4
- import { FeedbackType } from '../types/types.js';
5
- import { validateExecuteTasks } from './validator.js';
6
4
  import { getCancellationMessage, getMixedTaskTypesError, getUnknownRequestMessage, } from './messages.js';
5
+ import { validateExecuteTasks } from './validator.js';
7
6
  /**
8
7
  * Determine the operation name based on task types
9
8
  */
@@ -5,6 +5,7 @@ export var ExecutionStatus;
5
5
  ExecutionStatus["Success"] = "success";
6
6
  ExecutionStatus["Failed"] = "failed";
7
7
  ExecutionStatus["Aborted"] = "aborted";
8
+ ExecutionStatus["Cancelled"] = "cancelled";
8
9
  })(ExecutionStatus || (ExecutionStatus = {}));
9
10
  export var ExecutionResult;
10
11
  (function (ExecutionResult) {
@@ -1,3 +1,9 @@
1
+ /**
2
+ * Calculates elapsed time from a start timestamp, rounded to seconds.
3
+ */
4
+ export function calculateElapsed(start) {
5
+ return Math.floor((Date.now() - start) / 1000) * 1000;
6
+ }
1
7
  /**
2
8
  * Formats a duration in milliseconds to a human-readable string.
3
9
  * Uses correct singular/plural forms.
@@ -88,7 +88,12 @@ these commands specifically for their environment and workflow.
88
88
  Return a structured response with commands to execute:
89
89
 
90
90
  **Response structure:**
91
- - **message**: Brief status message (max 64 characters, end with period)
91
+ - **message**: Brief status message in imperative mood (max 64 characters,
92
+ end with colon)
93
+ - **summary**: Natural language summary as if execution has finished,
94
+ like a concierge would report (max 48 characters, no period, time will
95
+ be appended). Use varied expressions and synonyms, not necessarily the
96
+ same verb as the message. MUST NOT be empty.
92
97
  - **commands**: Array of command objects to execute sequentially
93
98
 
94
99
  **Command object structure:**
@@ -133,7 +138,8 @@ Task: {
133
138
 
134
139
  Response:
135
140
  ```
136
- message: "Creating the file."
141
+ message: "Create the file:"
142
+ summary: "File created"
137
143
  commands:
138
144
  - description: "Create test.txt"
139
145
  command: "touch test.txt"
@@ -148,7 +154,8 @@ Task: {
148
154
 
149
155
  Response:
150
156
  ```
151
- message: "Listing directory contents."
157
+ message: "List directory contents:"
158
+ summary: "I've listed the directory contents"
152
159
  commands:
153
160
  - description: "List files with details"
154
161
  command: "ls -la"
@@ -167,7 +174,8 @@ Tasks:
167
174
 
168
175
  Response:
169
176
  ```
170
- message: "Setting up the project."
177
+ message: "Set up the project:"
178
+ summary: "Project ready to go"
171
179
  commands:
172
180
  - description: "Create project directory"
173
181
  command: "mkdir -p my-project"
@@ -188,7 +196,8 @@ Task: {
188
196
 
189
197
  Response:
190
198
  ```
191
- message: "Installing dependencies."
199
+ message: "Install dependencies:"
200
+ summary: "Dependencies installed successfully"
192
201
  commands:
193
202
  - description: "Install npm packages"
194
203
  command: "npm install"
@@ -224,7 +233,8 @@ The "Process Data" skill's Execution section specifies:
224
233
 
225
234
  Response (using skill's Execution commands):
226
235
  ```
227
- message: "Processing sales data."
236
+ message: "Process sales data:"
237
+ summary: "Sales data transformed and exported"
228
238
  commands:
229
239
  - description: "Load the sales dataset"
230
240
  command: "curl -O https://data.example.com/sales.csv"
@@ -250,7 +260,8 @@ Task: {
250
260
 
251
261
  Response:
252
262
  ```
253
- message: "Creating backup."
263
+ message: "Create backup:"
264
+ summary: "Backup complete"
254
265
  commands:
255
266
  - description: "Copy config directory"
256
267
  command: "cp -r ~/.config/app ~/.config/app.backup"
@@ -265,7 +276,8 @@ Task: {
265
276
 
266
277
  Response:
267
278
  ```
268
- message: "Checking disk space."
279
+ message: "Check disk space:"
280
+ summary: "Disk space verified"
269
281
  commands:
270
282
  - description: "Show disk usage"
271
283
  command: "df -h"
@@ -7,13 +7,17 @@ task structures with high-level tasks and their subtasks.
7
7
  ## Response Format
8
8
 
9
9
  Every response MUST include a brief message (single sentence, max 64
10
- characters, ending with period) that introduces the schedule.
10
+ characters, ending with period) that introduces the schedule. Use
11
+ either imperative mood or present tense statements, but NEVER use
12
+ present continuous ("-ing" form).
11
13
 
12
- **Examples**: "Here's the schedule." / "I've organized the work." /
13
- "This is how I'll structure it."
14
+ **Examples**: "Build the application." / "Here's the schedule." /
15
+ "Deploy to production." / "I've organized the work."
14
16
 
15
17
  **Critical rules**:
16
18
  - Message is MANDATORY
19
+ - Use imperative mood OR present tense statements
20
+ - NEVER use present continuous ("-ing" form)
17
21
  - NEVER repeat the same message
18
22
  - ALWAYS end with period (.)
19
23
  - Vary phrasing naturally
@@ -8,6 +8,10 @@ export const executeTool = {
8
8
  type: 'string',
9
9
  description: 'Brief status message about the execution. Must be a single sentence, maximum 64 characters, ending with a period.',
10
10
  },
11
+ summary: {
12
+ type: 'string',
13
+ description: 'Natural language summary as if execution has finished, like a concierge would report. Shown after execution completes with time appended. Use varied expressions and synonyms, not necessarily the same verb as the message. Must be a single sentence without period, maximum 48 characters. MUST NOT be empty. Example: "Project ready to go" (time will be appended as " in X seconds").',
14
+ },
11
15
  commands: {
12
16
  type: 'array',
13
17
  description: 'Array of commands to execute sequentially',
@@ -39,6 +43,6 @@ export const executeTool = {
39
43
  },
40
44
  },
41
45
  },
42
- required: ['message', 'commands'],
46
+ required: ['message', 'summary', 'commands'],
43
47
  },
44
48
  };
package/dist/ui/Answer.js CHANGED
@@ -3,6 +3,7 @@ import { useEffect, useState } from 'react';
3
3
  import { Box, Text } from 'ink';
4
4
  import { ComponentStatus } from '../types/components.js';
5
5
  import { Colors, getTextColor } from '../services/colors.js';
6
+ import { addDebugToTimeline } from '../services/components.js';
6
7
  import { useInput } from '../services/keyboard.js';
7
8
  import { formatErrorMessage } from '../services/messages.js';
8
9
  import { withMinimumTime } from '../services/timing.js';
@@ -33,11 +34,15 @@ export function Answer({ question, state, status, service, handlers, }) {
33
34
  // Call answer tool with minimum processing time for UX polish
34
35
  const result = await withMinimumTime(() => svc.processWithTool(question, 'answer'), MINIMUM_PROCESSING_TIME);
35
36
  if (mounted) {
37
+ // Add debug components to timeline if present
38
+ addDebugToTimeline(result.debug, handlers);
36
39
  // Extract answer from result
37
40
  const answerText = result.answer || '';
38
41
  setAnswer(answerText);
39
42
  // Update component state so answer persists in timeline
40
- handlers?.updateState({ answer: answerText });
43
+ handlers?.updateState({
44
+ answer: answerText,
45
+ });
41
46
  // Signal completion
42
47
  handlers?.completeActive();
43
48
  }
@@ -46,6 +51,9 @@ export function Answer({ question, state, status, service, handlers, }) {
46
51
  if (mounted) {
47
52
  const errorMessage = formatErrorMessage(err);
48
53
  setError(errorMessage);
54
+ handlers?.updateState({
55
+ error: errorMessage,
56
+ });
49
57
  handlers?.onError(errorMessage);
50
58
  }
51
59
  }
@@ -4,9 +4,9 @@ import { Box, Text } from 'ink';
4
4
  import { ComponentStatus, } from '../types/components.js';
5
5
  import { TaskType } from '../types/types.js';
6
6
  import { Colors } from '../services/colors.js';
7
- import { createScheduleDefinition } from '../services/components.js';
8
- import { formatErrorMessage } from '../services/messages.js';
7
+ import { addDebugToTimeline, createScheduleDefinition, } from '../services/components.js';
9
8
  import { useInput } from '../services/keyboard.js';
9
+ import { formatErrorMessage } from '../services/messages.js';
10
10
  import { handleRefinement } from '../services/refinement.js';
11
11
  import { routeTasksWithConfirm } from '../services/router.js';
12
12
  import { ensureMinimumTime } from '../services/timing.js';
@@ -53,12 +53,10 @@ export function Command({ command, state, status, service, handlers, onAborted,
53
53
  // Add debug components to timeline if present
54
54
  // If we delegated to configure, include both schedule and configure debug
55
55
  // If not, only include schedule debug (result.debug is same as scheduleDebug)
56
- const allDebug = allConfig
56
+ const debugComponents = allConfig
57
57
  ? [...scheduleDebug, ...(result.debug || [])]
58
58
  : scheduleDebug;
59
- if (allDebug.length > 0) {
60
- handlers?.addToTimeline(...allDebug);
61
- }
59
+ addDebugToTimeline(debugComponents, handlers);
62
60
  // Save result to state for timeline display
63
61
  handlers?.updateState({
64
62
  message: result.message,
@@ -90,6 +88,9 @@ export function Command({ command, state, status, service, handlers, onAborted,
90
88
  if (mounted) {
91
89
  const errorMessage = formatErrorMessage(err);
92
90
  setError(errorMessage);
91
+ handlers?.updateState({
92
+ error: errorMessage,
93
+ });
93
94
  handlers?.onError(errorMessage);
94
95
  }
95
96
  }
@@ -3,16 +3,16 @@ import { memo } from 'react';
3
3
  import { ComponentName } from '../types/types.js';
4
4
  import { Answer } from './Answer.js';
5
5
  import { Command } from './Command.js';
6
- import { Confirm } from './Confirm.js';
7
6
  import { Config } from './Config.js';
7
+ import { Confirm } from './Confirm.js';
8
8
  import { Debug } from './Debug.js';
9
9
  import { Execute } from './Execute.js';
10
10
  import { Feedback } from './Feedback.js';
11
11
  import { Introspect } from './Introspect.js';
12
12
  import { Message } from './Message.js';
13
- import { Schedule } from './Schedule.js';
14
13
  import { Refinement } from './Refinement.js';
15
14
  import { Report } from './Report.js';
15
+ import { Schedule } from './Schedule.js';
16
16
  import { Validate } from './Validate.js';
17
17
  import { Welcome } from './Welcome.js';
18
18
  export const Component = memo(function Component({ def, debug, }) {
package/dist/ui/Config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
2
+ import { useEffect, useState } from 'react';
3
3
  import { Box, Text, useFocus } from 'ink';
4
4
  import TextInput from 'ink-text-input';
5
5
  import { ComponentStatus } from '../types/components.js';
@@ -17,6 +17,10 @@ function TextStep({ value, placeholder, validate, onChange, onSubmit, }) {
17
17
  const [inputValue, setInputValue] = useState(value);
18
18
  const [validationFailed, setValidationFailed] = useState(false);
19
19
  const { isFocused } = useFocus({ autoFocus: true });
20
+ // Sync internal state with prop changes
21
+ useEffect(() => {
22
+ setInputValue(value);
23
+ }, [value]);
20
24
  const handleChange = (newValue) => {
21
25
  setInputValue(newValue);
22
26
  onChange(newValue);
@@ -92,8 +96,20 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
92
96
  });
93
97
  return initial;
94
98
  });
95
- const [inputValue, setInputValue] = useState('');
99
+ const [inputValue, setInputValue] = useState(() => {
100
+ // Initialize with the current step's value if available
101
+ if (isActive && step < steps.length) {
102
+ const stepConfig = steps[step];
103
+ const configKey = stepConfig.path || stepConfig.key;
104
+ return values[configKey] || '';
105
+ }
106
+ return '';
107
+ });
96
108
  const [selectedIndex, setSelectedIndex] = useState(() => {
109
+ // If not active, use saved state
110
+ if (!isActive && state?.selectedIndex !== undefined) {
111
+ return state.selectedIndex;
112
+ }
97
113
  // Initialize selectedIndex based on current step's defaultIndex
98
114
  if (isActive &&
99
115
  step < steps.length &&
@@ -108,6 +124,16 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
108
124
  }
109
125
  return value.replace(/\n/g, '').trim();
110
126
  };
127
+ // Update inputValue when step changes
128
+ useEffect(() => {
129
+ if (isActive && step < steps.length) {
130
+ const stepConfig = steps[step];
131
+ const configKey = stepConfig.path || stepConfig.key;
132
+ const value = values[configKey] || '';
133
+ setInputValue(value);
134
+ }
135
+ // eslint-disable-next-line react-hooks/exhaustive-deps
136
+ }, [step, isActive, steps]);
111
137
  useInput((_, key) => {
112
138
  if (!isActive || step >= steps.length)
113
139
  return;
@@ -137,6 +163,7 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
137
163
  handlers?.updateState({
138
164
  values,
139
165
  completedStep: step,
166
+ selectedIndex,
140
167
  });
141
168
  if (onAborted) {
142
169
  onAborted('configuration');
@@ -197,6 +224,7 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
197
224
  const stateUpdate = {
198
225
  values: newValues,
199
226
  completedStep: steps.length,
227
+ selectedIndex,
200
228
  };
201
229
  handlers?.updateState(stateUpdate);
202
230
  // Call onFinished callback and handle result
@@ -219,6 +247,7 @@ export function Config({ steps, state, status, debug = DebugLevel.None, handlers
219
247
  const stateUpdate = {
220
248
  values: newValues,
221
249
  completedStep: step + 1,
250
+ selectedIndex,
222
251
  };
223
252
  handlers?.updateState(stateUpdate);
224
253
  const nextStep = step + 1;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
3
  import { Box, Text } from 'ink';
4
- import { ComponentStatus, } from '../types/components.js';
4
+ import { ComponentStatus } from '../types/components.js';
5
5
  import { Colors, Palette } from '../services/colors.js';
6
6
  import { useInput } from '../services/keyboard.js';
7
7
  import { UserQuery } from './UserQuery.js';