prompt-language-shell 1.0.0 → 1.0.4

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.
Files changed (34) hide show
  1. package/README.md +40 -0
  2. package/dist/Main.js +14 -1
  3. package/dist/components/Component.js +16 -0
  4. package/dist/components/controllers/Config.js +62 -125
  5. package/dist/components/controllers/Confirm.js +1 -1
  6. package/dist/components/controllers/Execute.js +50 -10
  7. package/dist/components/controllers/Learn.js +416 -0
  8. package/dist/components/controllers/Schedule.js +6 -14
  9. package/dist/components/controllers/Setting.js +66 -0
  10. package/dist/components/views/Config.js +9 -96
  11. package/dist/components/views/Confirm.js +1 -1
  12. package/dist/components/views/Execute.js +3 -2
  13. package/dist/components/views/Help.js +27 -0
  14. package/dist/components/views/Learn.js +147 -0
  15. package/dist/components/views/Output.js +50 -6
  16. package/dist/components/views/Setting.js +88 -0
  17. package/dist/components/views/StdinInput.js +19 -0
  18. package/dist/components/views/Welcome.js +4 -11
  19. package/dist/execution/runner.js +10 -5
  20. package/dist/services/colors.js +16 -15
  21. package/dist/services/components.js +23 -1
  22. package/dist/services/monitor.js +25 -7
  23. package/dist/services/router.js +12 -1
  24. package/dist/services/shell.js +42 -6
  25. package/dist/services/skills.js +102 -0
  26. package/dist/skills/introspect.md +22 -19
  27. package/dist/skills/schedule.md +35 -3
  28. package/dist/tools/introspect.tool.js +3 -3
  29. package/dist/tools/schedule.tool.js +1 -1
  30. package/dist/types/components.js +20 -0
  31. package/dist/types/schemas.js +1 -0
  32. package/dist/types/types.js +3 -0
  33. package/package.json +1 -1
  34. package/dist/components/views/Panel.js +0 -5
package/README.md CHANGED
@@ -125,6 +125,46 @@ Skills let you teach `pls` about your project-specific workflows. Create
125
125
  markdown files in `~/.pls/skills/` to define custom operations that
126
126
  `pls` can understand and execute.
127
127
 
128
+ ### Creating Skills
129
+
130
+ The easiest way to create a new skill is with the guided walkthrough:
131
+
132
+ ```
133
+ $ pls learn
134
+
135
+ Creating a new skill...
136
+
137
+ Skill name (e.g., "Deploy Project"):
138
+ > Build Frontend
139
+
140
+ Description (min 20 characters):
141
+ > Build the frontend application using npm
142
+
143
+ Step 1 - What does this step do?
144
+ > Install dependencies
145
+
146
+ How should this step be executed?
147
+ > shell command
148
+
149
+ Enter the shell command:
150
+ > npm install
151
+
152
+ Add another step?
153
+ > yes
154
+
155
+ ...
156
+ ```
157
+
158
+ The walkthrough guides you through defining:
159
+ - **Name**: A unique name for the skill
160
+ - **Description**: What the skill does (min 20 characters)
161
+ - **Aliases**: Example commands that invoke the skill (optional)
162
+ - **Config**: Configuration properties needed (optional)
163
+ - **Steps**: Each step with either a shell command or reference to another skill
164
+
165
+ Skills are saved to `~/.pls/skills/` as markdown files. File names use
166
+ kebab-case (e.g., "Build Frontend" becomes `build-frontend.md`).
167
+
128
168
  For complete documentation, see [docs/SKILLS.md](./docs/SKILLS.md).
129
169
 
130
170
  ### Structure
package/dist/Main.js CHANGED
@@ -8,10 +8,19 @@ import { getMissingConfigKeys } from './configuration/schema.js';
8
8
  import { createConfigStepsFromSchema } from './configuration/steps.js';
9
9
  import { unflattenConfig } from './configuration/transformation.js';
10
10
  import { createAnthropicService } from './services/anthropic.js';
11
- import { createCommand, createConfig, createFeedback, createMessage, createWelcome, } from './services/components.js';
11
+ import { createCommand, createConfig, createFeedback, createHelp, createMessage, createWelcome, } from './services/components.js';
12
12
  import { registerGlobalShortcut } from './services/keyboard.js';
13
13
  import { initializeLogger, setDebugLevel } from './services/logger.js';
14
14
  import { Workflow } from './components/Workflow.js';
15
+ /**
16
+ * Check if command matches help variations (help, --help, -h)
17
+ */
18
+ function isHelpCommand(cmd) {
19
+ if (!cmd)
20
+ return false;
21
+ const normalized = cmd.toLowerCase().trim();
22
+ return (normalized === 'help' || normalized === '--help' || normalized === '-h');
23
+ }
15
24
  export const Main = ({ app, command, serviceFactory = createAnthropicService, }) => {
16
25
  const [service, setService] = useState(null);
17
26
  const [initialQueue, setInitialQueue] = useState(null);
@@ -106,6 +115,10 @@ export const Main = ({ app, command, serviceFactory = createAnthropicService, })
106
115
  }),
107
116
  ]);
108
117
  }
118
+ else if (service && isHelpCommand(command)) {
119
+ // Help command - show help screen directly (no LLM)
120
+ setInitialQueue([createHelp({ app })]);
121
+ }
109
122
  else if (service && command) {
110
123
  // Valid service exists and command provided - execute command
111
124
  setInitialQueue([createCommand({ command, service })]);
@@ -10,11 +10,13 @@ import { Debug } from './views/Debug.js';
10
10
  import { Execute, ExecuteView, mapStateToViewProps, } from './controllers/Execute.js';
11
11
  import { Feedback } from './views/Feedback.js';
12
12
  import { Introspect, IntrospectView } from './controllers/Introspect.js';
13
+ import { Learn, LearnView } from './controllers/Learn.js';
13
14
  import { Message } from './views/Message.js';
14
15
  import { Refinement, RefinementView } from './controllers/Refinement.js';
15
16
  import { Report } from './views/Report.js';
16
17
  import { Schedule, ScheduleView } from './controllers/Schedule.js';
17
18
  import { Validate, ValidateView } from './controllers/Validate.js';
19
+ import { Help } from './views/Help.js';
18
20
  import { Welcome } from './views/Welcome.js';
19
21
  /**
20
22
  * Render a simple component (no lifecycle management)
@@ -25,6 +27,10 @@ export const SimpleComponent = memo(function SimpleComponent({ def, }) {
25
27
  const { props, status } = def;
26
28
  return _jsx(Welcome, { ...props, status: status });
27
29
  }
30
+ case ComponentName.Help: {
31
+ const { props, status } = def;
32
+ return _jsx(Help, { ...props, status: status });
33
+ }
28
34
  case ComponentName.Feedback: {
29
35
  const { props, status } = def;
30
36
  return _jsx(Feedback, { ...props, status: status });
@@ -86,6 +92,10 @@ export const ControllerComponent = memo(function ControllerComponent({ def, debu
86
92
  const { props: { tasks, service, upcoming, label }, status, } = def;
87
93
  return (_jsx(Execute, { tasks: tasks, service: service, upcoming: upcoming, label: label, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status }));
88
94
  }
95
+ case ComponentName.Learn: {
96
+ const { props: { suggestedName, onFinished, onAborted }, status, } = def;
97
+ return (_jsx(Learn, { suggestedName: suggestedName, onFinished: onFinished, onAborted: onAborted, requestHandlers: requestHandlers, lifecycleHandlers: lifecycleHandlers, workflowHandlers: workflowHandlers, status: status }));
98
+ }
89
99
  default:
90
100
  throw new Error(`Unknown managed component: ${def.name}`);
91
101
  }
@@ -137,6 +147,10 @@ export const ViewComponent = memo(function ViewComponent({ def, }) {
137
147
  const { props: { text }, status, } = def;
138
148
  return _jsx(RefinementView, { text: text, status: status });
139
149
  }
150
+ case ComponentName.Learn: {
151
+ const { state, status } = def;
152
+ return (_jsx(LearnView, { state: state, status: status, onInputChange: () => { }, onInputSubmit: () => { } }));
153
+ }
140
154
  default:
141
155
  throw new Error(`Unknown managed component: ${def.name}`);
142
156
  }
@@ -148,6 +162,7 @@ export const TimelineComponent = ({ def, }) => {
148
162
  switch (def.name) {
149
163
  // Simple components render as-is
150
164
  case ComponentName.Welcome:
165
+ case ComponentName.Help:
151
166
  case ComponentName.Feedback:
152
167
  case ComponentName.Message:
153
168
  case ComponentName.Debug:
@@ -163,6 +178,7 @@ export const TimelineComponent = ({ def, }) => {
163
178
  case ComponentName.Execute:
164
179
  case ComponentName.Answer:
165
180
  case ComponentName.Introspect:
181
+ case ComponentName.Learn:
166
182
  return _jsx(ViewComponent, { def: def });
167
183
  default:
168
184
  throw new Error('Unknown component type');
@@ -8,9 +8,31 @@ import { createConfigStepsFromSchema } from '../../configuration/steps.js';
8
8
  import { saveConfigLabels } from '../../configuration/labels.js';
9
9
  import { DebugLevel } from '../../configuration/types.js';
10
10
  import { useInput } from '../../services/keyboard.js';
11
- import { ConfigView, StepType } from '../views/Config.js';
11
+ import { StepType } from '../views/Config.js';
12
+ import { Setting } from './Setting.js';
12
13
  import { Spinner } from '../views/Spinner.js';
13
14
  export { ConfigView, StepType, } from '../views/Config.js';
15
+ function isTextStep(step) {
16
+ return step.type === StepType.Text;
17
+ }
18
+ function isSelectionStep(step) {
19
+ return step.type === StepType.Selection;
20
+ }
21
+ function initializeStepValues(steps) {
22
+ const values = {};
23
+ for (const step of steps) {
24
+ const configKey = step.path || step.key;
25
+ if (isTextStep(step)) {
26
+ if (step.value !== null) {
27
+ values[configKey] = step.value;
28
+ }
29
+ }
30
+ else if (isSelectionStep(step)) {
31
+ values[configKey] = step.options[step.defaultIndex].value;
32
+ }
33
+ }
34
+ return values;
35
+ }
14
36
  /**
15
37
  * Resolve query to config steps via CONFIGURE tool
16
38
  */
@@ -48,56 +70,19 @@ export function Config(props) {
48
70
  const isActive = status === ComponentStatus.Active;
49
71
  const [steps, setSteps] = useState(initialSteps || []);
50
72
  const [resolving, setResolving] = useState(!initialSteps?.length && !!query);
51
- const [step, setStep] = useState(0);
52
- const [values, setValues] = useState(() => {
53
- const initial = {};
54
- (initialSteps || []).forEach((stepConfig) => {
55
- const configKey = stepConfig.path || stepConfig.key;
56
- switch (stepConfig.type) {
57
- case StepType.Text:
58
- if (stepConfig.value !== null) {
59
- initial[configKey] = stepConfig.value;
60
- }
61
- break;
62
- case StepType.Selection:
63
- initial[configKey] =
64
- stepConfig.options[stepConfig.defaultIndex].value;
65
- break;
66
- }
67
- });
68
- return initial;
69
- });
70
- const [inputValue, setInputValue] = useState('');
71
- const [selectedIndex, setSelectedIndex] = useState(0);
72
- // Resolve query to steps
73
+ const [currentStep, setCurrentStep] = useState(0);
74
+ const [values, setValues] = useState(() => initializeStepValues(initialSteps || []));
73
75
  useEffect(() => {
74
76
  if (!isActive || !query || !service || initialSteps?.length)
75
77
  return;
76
78
  resolveQueryToSteps(query, service)
77
79
  .then((result) => {
78
- // Add debug components to timeline if present
79
80
  if (result.debug.length) {
80
81
  workflowHandlers.addToTimeline(...result.debug);
81
82
  }
82
83
  setSteps(result.steps);
83
84
  setResolving(false);
84
- // Initialize values for resolved steps
85
- const initial = {};
86
- result.steps.forEach((stepConfig) => {
87
- const configKey = stepConfig.path || stepConfig.key;
88
- switch (stepConfig.type) {
89
- case StepType.Text:
90
- if (stepConfig.value !== null) {
91
- initial[configKey] = stepConfig.value;
92
- }
93
- break;
94
- case StepType.Selection:
95
- initial[configKey] =
96
- stepConfig.options[stepConfig.defaultIndex].value;
97
- break;
98
- }
99
- });
100
- setValues(initial);
85
+ setValues(initializeStepValues(result.steps));
101
86
  })
102
87
  .catch((err) => {
103
88
  setResolving(false);
@@ -114,93 +99,39 @@ export function Config(props) {
114
99
  lifecycleHandlers,
115
100
  workflowHandlers,
116
101
  ]);
117
- // Update inputValue when step changes
118
- useEffect(() => {
119
- if (isActive && step < steps.length) {
120
- const stepConfig = steps[step];
121
- const configKey = stepConfig.path || stepConfig.key;
122
- setInputValue(values[configKey] || '');
102
+ const handleEscape = () => {
103
+ requestHandlers.onCompleted({
104
+ values,
105
+ completedStep: currentStep,
106
+ selectedIndex: 0,
107
+ steps,
108
+ });
109
+ if (onAborted) {
110
+ onAborted('configuration');
111
+ }
112
+ else {
113
+ lifecycleHandlers.completeActive(createFeedback({
114
+ type: FeedbackType.Aborted,
115
+ message: 'Configuration cancelled.',
116
+ }));
123
117
  }
124
- }, [step, isActive, steps, values]);
125
- const normalizeValue = (value) => {
126
- if (value === null || value === undefined)
127
- return '';
128
- return value.replace(/\n/g, '').trim();
129
118
  };
130
119
  useInput((_, key) => {
131
- if (!isActive || step >= steps.length)
120
+ if (!isActive || currentStep >= steps.length)
132
121
  return;
133
- const currentStepConfig = steps[step];
134
122
  if (key.escape) {
135
- const configKey = currentStepConfig.path || currentStepConfig.key;
136
- let currentValue = '';
137
- switch (currentStepConfig.type) {
138
- case StepType.Text:
139
- currentValue = inputValue || values[configKey] || '';
140
- break;
141
- case StepType.Selection:
142
- currentValue = values[configKey] || '';
143
- break;
144
- }
145
- const finalValues = currentValue
146
- ? { ...values, [configKey]: currentValue }
147
- : values;
148
- requestHandlers.onCompleted({
149
- values: finalValues,
150
- completedStep: step,
151
- selectedIndex,
152
- steps,
153
- });
154
- if (onAborted) {
155
- onAborted('configuration');
156
- }
157
- else {
158
- lifecycleHandlers.completeActive(createFeedback({
159
- type: FeedbackType.Aborted,
160
- message: 'Configuration cancelled.',
161
- }));
162
- }
163
- return;
164
- }
165
- if (currentStepConfig.type === StepType.Selection) {
166
- if (key.tab) {
167
- setSelectedIndex((prev) => (prev + 1) % currentStepConfig.options.length);
168
- }
169
- else if (key.return) {
170
- handleSubmit(currentStepConfig.options[selectedIndex].value);
171
- }
123
+ handleEscape();
172
124
  }
173
125
  }, { isActive });
174
- const handleSubmit = (value) => {
175
- const currentStepConfig = steps[step];
176
- let finalValue = '';
177
- switch (currentStepConfig.type) {
178
- case StepType.Selection:
179
- finalValue = value;
180
- break;
181
- case StepType.Text: {
182
- const normalizedInput = normalizeValue(value);
183
- if (normalizedInput && currentStepConfig.validate(normalizedInput)) {
184
- finalValue = normalizedInput;
185
- }
186
- else if (currentStepConfig.value &&
187
- currentStepConfig.validate(currentStepConfig.value)) {
188
- finalValue = currentStepConfig.value;
189
- }
190
- break;
191
- }
192
- }
193
- if (!finalValue)
194
- return;
195
- const configKey = currentStepConfig.path || currentStepConfig.key;
196
- const newValues = { ...values, [configKey]: finalValue };
126
+ const handleEntrySubmit = (stepConfig, value) => {
127
+ const configKey = stepConfig.path || stepConfig.key;
128
+ const newValues = { ...values, [configKey]: value };
197
129
  setValues(newValues);
198
- setInputValue('');
199
- if (step === steps.length - 1) {
130
+ if (currentStep === steps.length - 1) {
200
131
  requestHandlers.onCompleted({
201
132
  values: newValues,
202
133
  completedStep: steps.length,
203
- selectedIndex,
134
+ selectedIndex: 0,
204
135
  steps,
205
136
  });
206
137
  try {
@@ -216,19 +147,25 @@ export function Config(props) {
216
147
  message: error instanceof Error ? error.message : 'Configuration failed',
217
148
  }));
218
149
  }
219
- setStep(steps.length);
150
+ setCurrentStep(steps.length);
220
151
  }
221
152
  else {
222
- const nextStep = step + 1;
223
- setStep(nextStep);
224
- if (nextStep < steps.length &&
225
- steps[nextStep].type === StepType.Selection) {
226
- setSelectedIndex(steps[nextStep].defaultIndex);
227
- }
153
+ setCurrentStep(currentStep + 1);
228
154
  }
229
155
  };
230
156
  if (resolving) {
231
157
  return (_jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { children: "Resolving configuration... " }), _jsx(Spinner, {})] }));
232
158
  }
233
- return (_jsx(ConfigView, { steps: steps, state: { values, completedStep: step, selectedIndex }, status: status, debug: debug, onInputChange: setInputValue, onInputSubmit: handleSubmit }));
159
+ return (_jsx(Box, { flexDirection: "column", marginLeft: 1, children: steps.map((stepConfig, index) => {
160
+ const isStepActive = index === currentStep && isActive;
161
+ const isCompleted = index < currentStep;
162
+ const wasAborted = index === currentStep && !isActive;
163
+ const shouldShow = isCompleted || isStepActive || wasAborted;
164
+ if (!shouldShow)
165
+ return null;
166
+ const configKey = stepConfig.path || stepConfig.key;
167
+ return (_jsx(Box, { marginTop: index === 0 ? 0 : 1, children: _jsx(Setting, { step: stepConfig, initialValue: values[configKey] || '', isActive: isStepActive, debug: debug, onSubmit: (value) => {
168
+ handleEntrySubmit(stepConfig, value);
169
+ } }) }, configKey));
170
+ }) }));
234
171
  }
@@ -19,7 +19,7 @@ export function Confirm({ message, status, requestHandlers, onConfirmed, onCance
19
19
  requestHandlers.onCompleted(finalState);
20
20
  onCancelled();
21
21
  }
22
- else if (key.tab) {
22
+ else if (key.tab || key.leftArrow || key.rightArrow) {
23
23
  // Toggle between Yes (0) and No (1)
24
24
  setSelectedIndex((prev) => (prev === 0 ? 1 : 0));
25
25
  }
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useReducer, useRef } from 'react';
3
- import { ComponentStatus, } from '../../types/components.js';
2
+ import { useCallback, useEffect, useReducer, useRef, useState } from 'react';
3
+ import { ComponentStatus, OutputSource, } from '../../types/components.js';
4
4
  import { useInput } from '../../services/keyboard.js';
5
5
  import { formatErrorMessage, getExecutionErrorMessage, } from '../../services/messages.js';
6
- import { ExecutionStatus } from '../../services/shell.js';
6
+ import { ExecutionStatus, killCurrentProcess, writeStdin, } from '../../services/shell.js';
7
7
  import { ELAPSED_UPDATE_INTERVAL, ensureMinimumTime, } from '../../services/timing.js';
8
8
  import { handleTaskCompletion, handleTaskFailure, } from '../../execution/handlers.js';
9
9
  import { processTasks } from '../../execution/processing.js';
@@ -43,6 +43,9 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
43
43
  });
44
44
  // Ref to track if current task execution is cancelled
45
45
  const cancelledRef = useRef(false);
46
+ // Toggle stdin input visibility with / key
47
+ const [showStdinInput, setShowStdinInput] = useState(false);
48
+ const showStdinInputRef = useRef(false);
46
49
  const { error, tasks, message, hasProcessed, completionMessage, summary } = localState;
47
50
  // Derive current task index from tasks
48
51
  const currentTaskIndex = getCurrentTaskIndex(tasks);
@@ -75,9 +78,17 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
75
78
  clearInterval(interval);
76
79
  };
77
80
  }, [runningTask?.startTime, isExecuting, currentTaskIndex]);
78
- // Handle cancel - state already in reducer, just need to update final output
81
+ // Hide stdin input when not executing
82
+ useEffect(() => {
83
+ if (!isExecuting) {
84
+ showStdinInputRef.current = false;
85
+ setShowStdinInput(false);
86
+ }
87
+ }, [isExecuting]);
88
+ // Handle cancel - kill the running process and update final output
79
89
  const handleCancel = useCallback(() => {
80
90
  cancelledRef.current = true;
91
+ killCurrentProcess();
81
92
  dispatch({ type: ExecuteActionType.CancelExecution });
82
93
  // Build final state with current output for the running task
83
94
  const updatedTasks = tasks.map((task) => {
@@ -103,9 +114,30 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
103
114
  requestHandlers.onCompleted(finalState);
104
115
  requestHandlers.onAborted('execution');
105
116
  }, [message, summary, tasks, requestHandlers]);
106
- useInput((_, key) => {
117
+ // Handle stdin submission: write the line to the running process
118
+ const handleStdinSubmit = useCallback((value) => {
119
+ writeStdin(value + '\n');
120
+ outputRef.current.chunks.push({
121
+ text: `\n> ${value}\n`,
122
+ timestamp: Date.now(),
123
+ source: OutputSource.Stdin,
124
+ });
125
+ showStdinInputRef.current = false;
126
+ setShowStdinInput(false);
127
+ }, []);
128
+ useInput((input, key) => {
107
129
  if (key.escape && (isLoading || isExecuting)) {
108
- handleCancel();
130
+ if (showStdinInputRef.current) {
131
+ showStdinInputRef.current = false;
132
+ setShowStdinInput(false);
133
+ }
134
+ else {
135
+ handleCancel();
136
+ }
137
+ }
138
+ if (input === '/' && isExecuting && !showStdinInputRef.current) {
139
+ showStdinInputRef.current = true;
140
+ setShowStdinInput(true);
109
141
  }
110
142
  }, { isActive: (isLoading || isExecuting) && isActive });
111
143
  // Process tasks to get commands from AI
@@ -210,6 +242,8 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
210
242
  });
211
243
  // Reset output ref for new task
212
244
  outputRef.current = { chunks: [], currentMemory: undefined };
245
+ showStdinInputRef.current = false;
246
+ setShowStdinInput(false);
213
247
  // Merge workdir into command
214
248
  const command = workdirRef.current
215
249
  ? { ...currentTask.command, workdir: workdirRef.current }
@@ -217,8 +251,10 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
217
251
  void executeTask(command, currentTaskIndex, {
218
252
  onUpdate: (output) => {
219
253
  if (!cancelledRef.current) {
254
+ // Preserve stdin chunks that were added locally
255
+ const stdinChunks = outputRef.current.chunks.filter((c) => c.source === OutputSource.Stdin);
220
256
  outputRef.current = {
221
- chunks: output.chunks,
257
+ chunks: [...output.chunks, ...stdinChunks],
222
258
  currentMemory: output.currentMemory,
223
259
  };
224
260
  }
@@ -230,11 +266,13 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
230
266
  if (execOutput.workdir) {
231
267
  workdirRef.current = execOutput.workdir;
232
268
  }
269
+ // Preserve stdin chunks in final output
270
+ const stdinChunks = outputRef.current.chunks.filter((c) => c.source === OutputSource.Stdin);
233
271
  const tasksWithOutput = tasks.map((task, i) => i === currentTaskIndex
234
272
  ? {
235
273
  ...task,
236
274
  output: {
237
- chunks: execOutput.chunks,
275
+ chunks: [...execOutput.chunks, ...stdinChunks],
238
276
  },
239
277
  }
240
278
  : task);
@@ -256,11 +294,13 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
256
294
  if (execOutput.workdir) {
257
295
  workdirRef.current = execOutput.workdir;
258
296
  }
297
+ // Preserve stdin chunks in final output
298
+ const stdinChunks = outputRef.current.chunks.filter((c) => c.source === OutputSource.Stdin);
259
299
  const tasksWithOutput = tasks.map((task, i) => i === currentTaskIndex
260
300
  ? {
261
301
  ...task,
262
302
  output: {
263
- chunks: execOutput.chunks,
303
+ chunks: [...execOutput.chunks, ...stdinChunks],
264
304
  },
265
305
  error: execOutput.error || undefined,
266
306
  }
@@ -287,5 +327,5 @@ export function Execute({ tasks: inputTasks, status, service, upcoming, label, r
287
327
  lifecycleHandlers,
288
328
  workflowHandlers,
289
329
  ]);
290
- return (_jsx(ExecuteView, { isLoading: isLoading, isExecuting: isExecuting, isActive: isActive, error: error, message: message, tasks: tasks, completionMessage: completionMessage, showTasks: showTasks, upcoming: upcoming, label: label }));
330
+ return (_jsx(ExecuteView, { isLoading: isLoading, isExecuting: isExecuting, isActive: isActive, error: error, message: message, tasks: tasks, completionMessage: completionMessage, showTasks: showTasks, upcoming: upcoming, label: label, onStdinSubmit: showStdinInput ? handleStdinSubmit : undefined }));
291
331
  }