prompt-language-shell 0.9.2 → 0.9.6
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/dist/{ui/Main.js → Main.js} +12 -12
- package/dist/{ui → components}/Component.js +28 -26
- package/dist/{ui → components}/Workflow.js +14 -4
- package/dist/{ui → components/controllers}/Answer.js +18 -17
- package/dist/{ui → components/controllers}/Command.js +11 -18
- package/dist/{ui → components/controllers}/Config.js +8 -116
- package/dist/components/controllers/Confirm.js +42 -0
- package/dist/{ui → components/controllers}/Execute.js +75 -144
- package/dist/{ui → components/controllers}/Introspect.js +12 -28
- package/dist/components/controllers/Refinement.js +18 -0
- package/dist/components/controllers/Schedule.js +134 -0
- package/dist/{ui → components/controllers}/Validate.js +14 -32
- package/dist/components/views/Answer.js +28 -0
- package/dist/components/views/Command.js +11 -0
- package/dist/components/views/Config.js +115 -0
- package/dist/components/views/Confirm.js +24 -0
- package/dist/components/views/Debug.js +12 -0
- package/dist/components/views/Execute.js +60 -0
- package/dist/components/views/Feedback.js +8 -0
- package/dist/components/views/Introspect.js +17 -0
- package/dist/{ui → components/views}/Label.js +3 -3
- package/dist/{ui → components/views}/List.js +1 -1
- package/dist/{ui → components/views}/Output.js +2 -2
- package/dist/components/views/Refinement.js +9 -0
- package/dist/{ui → components/views}/Report.js +1 -1
- package/dist/components/views/Schedule.js +121 -0
- package/dist/{ui → components/views}/Separator.js +1 -1
- package/dist/{ui → components/views}/Spinner.js +1 -1
- package/dist/{ui → components/views}/Subtask.js +4 -4
- package/dist/components/views/Table.js +15 -0
- package/dist/components/views/Task.js +18 -0
- package/dist/components/views/Upcoming.js +30 -0
- package/dist/{ui → components/views}/UserQuery.js +1 -1
- package/dist/components/views/Validate.js +17 -0
- package/dist/{ui → components/views}/Welcome.js +1 -1
- package/dist/configuration/steps.js +1 -1
- package/dist/execution/handlers.js +19 -53
- package/dist/execution/reducer.js +26 -38
- package/dist/execution/runner.js +43 -25
- package/dist/execution/types.js +3 -4
- package/dist/execution/utils.js +1 -1
- package/dist/index.js +1 -1
- package/dist/services/anthropic.js +27 -31
- package/dist/services/colors.js +2 -1
- package/dist/services/logger.js +126 -13
- package/dist/services/messages.js +19 -0
- package/dist/services/parser.js +13 -5
- package/dist/services/refinement.js +8 -2
- package/dist/services/router.js +184 -89
- package/dist/services/shell.js +26 -6
- package/dist/services/skills.js +35 -7
- package/dist/services/timing.js +1 -0
- package/dist/skills/execute.md +15 -7
- package/dist/skills/schedule.md +155 -0
- package/dist/tools/execute.tool.js +0 -4
- package/dist/tools/schedule.tool.js +1 -1
- package/dist/types/schemas.js +0 -1
- package/package.json +4 -4
- package/dist/execution/hooks.js +0 -291
- package/dist/ui/Confirm.js +0 -62
- package/dist/ui/Debug.js +0 -7
- package/dist/ui/Feedback.js +0 -19
- package/dist/ui/Refinement.js +0 -23
- package/dist/ui/Schedule.js +0 -257
- package/dist/ui/Task.js +0 -11
- /package/dist/{ui → components/views}/Message.js +0 -0
- /package/dist/{ui → components/views}/Panel.js +0 -0
package/dist/skills/schedule.md
CHANGED
|
@@ -14,6 +14,15 @@ behavior must adapt accordingly:
|
|
|
14
14
|
- **Skills present**: Match user requests ONLY against listed skills
|
|
15
15
|
- **Empty or missing**: Create "ignore" tasks for ALL action verbs
|
|
16
16
|
|
|
17
|
+
**CRITICAL - Available Skills Section Takes Precedence**:
|
|
18
|
+
|
|
19
|
+
Information provided in the "Available Skills" section is AUTHORITATIVE
|
|
20
|
+
and OVERRIDES any default behavior in these instructions. When a skill's
|
|
21
|
+
description specifies required parameters, error handling, or specific
|
|
22
|
+
behaviors, those requirements MUST be followed exactly. Skill-specific
|
|
23
|
+
rules always take precedence over general examples or default patterns
|
|
24
|
+
in this document.
|
|
25
|
+
|
|
17
26
|
All examples in these instructions (e.g., "build", "deploy", "process")
|
|
18
27
|
are for illustration only. They do NOT represent actual available
|
|
19
28
|
skills unless they appear in the "Available Skills" section of the
|
|
@@ -268,6 +277,152 @@ User request with multiple config expressions
|
|
|
268
277
|
- This applies to ALL placeholders in task actions, including those
|
|
269
278
|
from skill references
|
|
270
279
|
|
|
280
|
+
## Runtime Parameter Placeholders
|
|
281
|
+
|
|
282
|
+
Skills may include runtime parameters in their Execution section using
|
|
283
|
+
angle bracket syntax. These parameters MUST be resolved by the LLM
|
|
284
|
+
during scheduling - they represent values extracted from the user's
|
|
285
|
+
command, NOT from stored configuration.
|
|
286
|
+
|
|
287
|
+
**Parameter Format:**
|
|
288
|
+
|
|
289
|
+
- `<PARAM>` - Required parameter, extract from user command
|
|
290
|
+
- `<PARAM=default>` - Parameter with default, use default if not specified
|
|
291
|
+
- `<PARAM?>` - Optional parameter, omit entirely if not mentioned
|
|
292
|
+
|
|
293
|
+
**Distinction from Config Placeholders:**
|
|
294
|
+
|
|
295
|
+
- `{x.y.z}` - Config placeholder, resolved by system at execution from
|
|
296
|
+
~/.plsrc
|
|
297
|
+
- `{x.VARIANT.z}` - Variant config, LLM matches variant at schedule time,
|
|
298
|
+
system resolves from ~/.plsrc at execution
|
|
299
|
+
- `<PARAM>` - Runtime parameter, resolved entirely by LLM at schedule time
|
|
300
|
+
from user command
|
|
301
|
+
|
|
302
|
+
**Resolution Rules:**
|
|
303
|
+
|
|
304
|
+
1. **Full resolution required**: All `<PARAM>` placeholders MUST be
|
|
305
|
+
resolved to concrete values before creating tasks. No angle-bracket
|
|
306
|
+
syntax should remain in task actions or params.
|
|
307
|
+
|
|
308
|
+
2. **Space normalization**: When optional params are omitted, collapse
|
|
309
|
+
adjacent spaces to single space (e.g., `cmd <OPT?> file` → `cmd file`)
|
|
310
|
+
|
|
311
|
+
3. **Complete descriptions**: Task actions must be human-readable with
|
|
312
|
+
all parameters filled in:
|
|
313
|
+
- CORRECT: "Process /data/report.csv in batch mode with JSON output"
|
|
314
|
+
- WRONG: "Process <SOURCE> in <MODE> mode"
|
|
315
|
+
|
|
316
|
+
**Parameter Classification:**
|
|
317
|
+
|
|
318
|
+
Runtime parameters fall into two categories:
|
|
319
|
+
|
|
320
|
+
1. **Key parameters** - Essential to the operation, define WHAT to operate on
|
|
321
|
+
- Input files, paths, URLs, target names, identifiers
|
|
322
|
+
- The primary subject of the command
|
|
323
|
+
- Cannot be guessed or listed as options
|
|
324
|
+
- Examples: `<SOURCE>`, `<FILE>`, `<URL>`, `<TARGET>`
|
|
325
|
+
|
|
326
|
+
2. **Modifier parameters** - Configure HOW the operation runs
|
|
327
|
+
- Have a finite set of valid options
|
|
328
|
+
- Affect behavior but not the primary subject
|
|
329
|
+
- Examples: `<MODE>`, `<QUALITY>`, `<FORMAT>`, `<VERBOSITY>`
|
|
330
|
+
|
|
331
|
+
**Resolution Outcomes:**
|
|
332
|
+
|
|
333
|
+
When processing runtime parameters, exactly ONE of these outcomes applies.
|
|
334
|
+
**CRITICAL: Evaluate in this EXACT order - key param check MUST happen first:**
|
|
335
|
+
|
|
336
|
+
1. **Key param missing** → Create IGNORE task (CHECK THIS FIRST!)
|
|
337
|
+
- **PREREQUISITE CHECK**: Before considering ANY other outcome, verify
|
|
338
|
+
ALL key parameters (input files, paths, URLs, targets) are present
|
|
339
|
+
- A key parameter is not specified → ALWAYS create IGNORE task
|
|
340
|
+
- **NEVER create a DEFINE task when key params are missing**, even if
|
|
341
|
+
modifier params could be listed as options
|
|
342
|
+
- NEVER offer options for key parameters - they cannot be guessed
|
|
343
|
+
- Use type `ignore` with descriptive action
|
|
344
|
+
- Action format: "Missing [param]: specify [what's needed]"
|
|
345
|
+
- Examples:
|
|
346
|
+
- "Missing input: specify which file to process"
|
|
347
|
+
- "Missing target: specify which server to deploy to"
|
|
348
|
+
- "Missing URL: specify which page to fetch"
|
|
349
|
+
|
|
350
|
+
2. **All resolved** → Create normal execute/group task
|
|
351
|
+
- All key parameters are present AND extracted successfully
|
|
352
|
+
- All modifier parameters are extracted or defaulted
|
|
353
|
+
- Task action contains fully resolved description
|
|
354
|
+
|
|
355
|
+
3. **Modifier param unclear (ALL key params present)** → Create DEFINE task
|
|
356
|
+
- **PREREQUISITE**: ALL key parameters MUST be present in user's command
|
|
357
|
+
- Only a modifier parameter is unclear but has finite options
|
|
358
|
+
- **NEVER use DEFINE when ANY key param is missing** - use IGNORE instead
|
|
359
|
+
- Use type `define` with params.skill and params.options
|
|
360
|
+
- MUST have more than one option (if only one option exists, use it
|
|
361
|
+
directly without refinement)
|
|
362
|
+
- Example: mode (batch/stream/interactive), format (json/xml/csv)
|
|
363
|
+
- Each option is an object: { name: string, command: string }
|
|
364
|
+
- name: readable display text for user selection
|
|
365
|
+
- command: user's natural language command with ALL params resolved
|
|
366
|
+
- Note: command is NOT the shell command - shell commands are generated
|
|
367
|
+
by EXECUTE in the next step
|
|
368
|
+
|
|
369
|
+
**Examples:**
|
|
370
|
+
|
|
371
|
+
Skill execution line:
|
|
372
|
+
- `process <SOURCE> --mode <MODE> --format <FORMAT=json> <VERBOSE?>`
|
|
373
|
+
|
|
374
|
+
Key param missing case (CHECK FIRST):
|
|
375
|
+
- User: "process in batch mode"
|
|
376
|
+
- Problem: SOURCE path not specified (key param, cannot be guessed)
|
|
377
|
+
- Task: type `ignore`, action: "Missing source: specify which file to process"
|
|
378
|
+
|
|
379
|
+
Key param missing with modifier specified:
|
|
380
|
+
- User: "export in JSON format"
|
|
381
|
+
- Problem: SOURCE file not specified (key param, cannot be guessed)
|
|
382
|
+
- Task: type `ignore`, action: "Missing source: specify which data to export"
|
|
383
|
+
- Note: Even though format IS specified, key param is missing → IGNORE, not
|
|
384
|
+
DEFINE. Key param check takes absolute precedence over DEFINE.
|
|
385
|
+
|
|
386
|
+
Success case (all resolved):
|
|
387
|
+
- User: "process /data/report.csv in batch mode"
|
|
388
|
+
- Resolution:
|
|
389
|
+
- `<SOURCE>` → `/data/report.csv` (extracted)
|
|
390
|
+
- `<MODE>` → `batch` (extracted from "batch mode")
|
|
391
|
+
- `<FORMAT=json>` → `json` (default used)
|
|
392
|
+
- `<VERBOSE?>` → omitted (optional, not mentioned)
|
|
393
|
+
- Task action: "Process /data/report.csv in batch mode with JSON format"
|
|
394
|
+
|
|
395
|
+
Define case (modifier unclear, ALL key params present):
|
|
396
|
+
- User: "process /data/report.csv"
|
|
397
|
+
- Key params: SOURCE is present (/data/report.csv) ✓
|
|
398
|
+
- Problem: MODE not specified but can be listed (3 options available)
|
|
399
|
+
- Task: type `define`, params:
|
|
400
|
+
- skill: "Process Data"
|
|
401
|
+
- options:
|
|
402
|
+
- { name: "Process in batch mode",
|
|
403
|
+
command: "process /data/report.csv in batch mode" }
|
|
404
|
+
- { name: "Process in stream mode",
|
|
405
|
+
command: "process /data/report.csv in stream mode" }
|
|
406
|
+
- { name: "Process interactively",
|
|
407
|
+
command: "process /data/report.csv interactively" }
|
|
408
|
+
- User selects "Process in batch mode"
|
|
409
|
+
- SCHEDULE receives: "process /data/report.csv in batch mode"
|
|
410
|
+
- EXECUTE then generates the appropriate shell command
|
|
411
|
+
|
|
412
|
+
**Critical Rules:**
|
|
413
|
+
- **KEY PARAM CHECK IS MANDATORY AND FIRST**: Before creating ANY task type,
|
|
414
|
+
verify ALL key parameters are present. This check takes absolute precedence.
|
|
415
|
+
- IGNORE when ANY key param is missing (input, file, URL, target, etc.)
|
|
416
|
+
- Key params cannot be guessed - always require IGNORE with clear error
|
|
417
|
+
- **DEFINE is ONLY valid when ALL key params are present** - if any key param
|
|
418
|
+
is missing, DEFINE is NOT an option, regardless of modifier params
|
|
419
|
+
- DEFINE tasks MUST have multiple options (2+); single option = use directly
|
|
420
|
+
- NEVER leave `<PARAM>` unresolved in task output
|
|
421
|
+
- NEVER use placeholder values like `<UNKNOWN>` or `<MISSING>`
|
|
422
|
+
- option.command is user's natural language request, NOT shell command
|
|
423
|
+
- Each option.command must include ALL user parameters (original + selected)
|
|
424
|
+
- option.command must preserve exact paths, filenames, URLs (case-sensitive)
|
|
425
|
+
|
|
271
426
|
## Grouping Strategy
|
|
272
427
|
|
|
273
428
|
Group subtasks under logical parent tasks based on:
|
|
@@ -34,10 +34,6 @@ export const executeTool = {
|
|
|
34
34
|
type: 'number',
|
|
35
35
|
description: 'Optional timeout in milliseconds. Defaults to 30000 (30 seconds).',
|
|
36
36
|
},
|
|
37
|
-
critical: {
|
|
38
|
-
type: 'boolean',
|
|
39
|
-
description: 'Whether failure should stop execution of subsequent commands. Defaults to true.',
|
|
40
|
-
},
|
|
41
37
|
},
|
|
42
38
|
required: ['description', 'command'],
|
|
43
39
|
},
|
|
@@ -31,7 +31,7 @@ export const scheduleTool = {
|
|
|
31
31
|
},
|
|
32
32
|
params: {
|
|
33
33
|
type: 'object',
|
|
34
|
-
description: 'Parameters for leaf tasks
|
|
34
|
+
description: 'Parameters for leaf tasks. For "define" type: { skill: string, options: Array<{ name: string, command: string }> }. "name" is display text, "command" is full resolved command.',
|
|
35
35
|
},
|
|
36
36
|
config: {
|
|
37
37
|
type: 'array',
|
package/dist/types/schemas.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "prompt-language-shell",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.6",
|
|
4
4
|
"description": "Your personal command-line concierge. Ask politely, and it gets things done.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
"dev": "npm run build && tsc --watch",
|
|
18
18
|
"prepare": "husky",
|
|
19
19
|
"prepublishOnly": "npm run check",
|
|
20
|
-
"test": "vitest run --exclude 'tests/
|
|
21
|
-
"test:watch": "vitest --exclude 'tests/
|
|
22
|
-
"test:llm": "vitest run tests/
|
|
20
|
+
"test": "vitest run --exclude 'tests/tools/schedule/*.test.tsx'",
|
|
21
|
+
"test:watch": "vitest --exclude 'tests/tools/schedule/*.test.tsx'",
|
|
22
|
+
"test:llm": "vitest run tests/tools/schedule/*.test.tsx",
|
|
23
23
|
"format": "prettier --write '**/*.{ts,tsx}'",
|
|
24
24
|
"format:check": "prettier --check '**/*.{ts,tsx}'",
|
|
25
25
|
"lint": "eslint .",
|
package/dist/execution/hooks.js
DELETED
|
@@ -1,291 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useRef, useState, } from 'react';
|
|
2
|
-
import { ComponentStatus, } from '../types/components.js';
|
|
3
|
-
import { FeedbackType } from '../types/types.js';
|
|
4
|
-
import { createFeedback, createMessage } from '../services/components.js';
|
|
5
|
-
import { formatErrorMessage, getExecutionErrorMessage, } from '../services/messages.js';
|
|
6
|
-
import { ExecutionStatus } from '../services/shell.js';
|
|
7
|
-
import { ensureMinimumTime } from '../services/timing.js';
|
|
8
|
-
import { handleTaskCompletion, handleTaskFailure } from './handlers.js';
|
|
9
|
-
import { processTasks } from './processing.js';
|
|
10
|
-
import { executeTask } from './runner.js';
|
|
11
|
-
import { ExecuteActionType } from './types.js';
|
|
12
|
-
import { getCurrentTaskIndex } from './utils.js';
|
|
13
|
-
const ELAPSED_UPDATE_INTERVAL = 1000;
|
|
14
|
-
/**
|
|
15
|
-
* Track elapsed time from a start timestamp.
|
|
16
|
-
* Returns 0 when not active or no start time.
|
|
17
|
-
*/
|
|
18
|
-
export function useElapsedTimer(startTime, isActive) {
|
|
19
|
-
const [elapsed, setElapsed] = useState(0);
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
if (!startTime || !isActive)
|
|
22
|
-
return;
|
|
23
|
-
const interval = setInterval(() => {
|
|
24
|
-
setElapsed(Date.now() - startTime);
|
|
25
|
-
}, ELAPSED_UPDATE_INTERVAL);
|
|
26
|
-
return () => {
|
|
27
|
-
clearInterval(interval);
|
|
28
|
-
};
|
|
29
|
-
}, [startTime, isActive]);
|
|
30
|
-
return elapsed;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Manage live output and timing for the currently executing task.
|
|
34
|
-
* Groups related state for tracking a running task's output.
|
|
35
|
-
*/
|
|
36
|
-
export function useLiveTaskOutput() {
|
|
37
|
-
const [output, setOutput] = useState({
|
|
38
|
-
stdout: '',
|
|
39
|
-
stderr: '',
|
|
40
|
-
error: '',
|
|
41
|
-
});
|
|
42
|
-
const [startTime, setStartTime] = useState(null);
|
|
43
|
-
const start = useCallback(() => {
|
|
44
|
-
setOutput({ stdout: '', stderr: '', error: '' });
|
|
45
|
-
setStartTime(Date.now());
|
|
46
|
-
}, []);
|
|
47
|
-
const stop = useCallback(() => {
|
|
48
|
-
setStartTime(null);
|
|
49
|
-
}, []);
|
|
50
|
-
return {
|
|
51
|
-
output,
|
|
52
|
-
startTime,
|
|
53
|
-
setOutput,
|
|
54
|
-
start,
|
|
55
|
-
stop,
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Handle execution cancellation with a ref-based flag.
|
|
60
|
-
* The ref is needed because callbacks check the current cancellation state.
|
|
61
|
-
*/
|
|
62
|
-
export function useCancellation() {
|
|
63
|
-
const cancelledRef = useRef(false);
|
|
64
|
-
const cancel = useCallback(() => {
|
|
65
|
-
cancelledRef.current = true;
|
|
66
|
-
}, []);
|
|
67
|
-
const reset = useCallback(() => {
|
|
68
|
-
cancelledRef.current = false;
|
|
69
|
-
}, []);
|
|
70
|
-
return {
|
|
71
|
-
cancelledRef,
|
|
72
|
-
cancel,
|
|
73
|
-
reset,
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
const MINIMUM_PROCESSING_TIME = 400;
|
|
77
|
-
/**
|
|
78
|
-
* Helper to create ExecuteState with defaults
|
|
79
|
-
*/
|
|
80
|
-
function createExecuteState(overrides = {}) {
|
|
81
|
-
return {
|
|
82
|
-
message: '',
|
|
83
|
-
summary: '',
|
|
84
|
-
tasks: [],
|
|
85
|
-
completionMessage: null,
|
|
86
|
-
error: null,
|
|
87
|
-
...overrides,
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Process input tasks through AI to generate executable commands.
|
|
92
|
-
* Handles the initial phase of task execution.
|
|
93
|
-
*/
|
|
94
|
-
export function useTaskProcessor(config) {
|
|
95
|
-
const { inputTasks, service, isActive, hasProcessed, tasksCount, dispatch, requestHandlers, lifecycleHandlers, workflowHandlers, } = config;
|
|
96
|
-
useEffect(() => {
|
|
97
|
-
if (!isActive || tasksCount > 0 || hasProcessed) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
let mounted = true;
|
|
101
|
-
async function process(svc) {
|
|
102
|
-
const startTime = Date.now();
|
|
103
|
-
try {
|
|
104
|
-
const result = await processTasks(inputTasks, svc);
|
|
105
|
-
await ensureMinimumTime(startTime, MINIMUM_PROCESSING_TIME);
|
|
106
|
-
if (!mounted)
|
|
107
|
-
return;
|
|
108
|
-
// Add debug components to timeline if present
|
|
109
|
-
if (result.debug?.length) {
|
|
110
|
-
workflowHandlers.addToTimeline(...result.debug);
|
|
111
|
-
}
|
|
112
|
-
if (result.commands.length === 0) {
|
|
113
|
-
if (result.error) {
|
|
114
|
-
const errorMessage = getExecutionErrorMessage(result.error);
|
|
115
|
-
workflowHandlers.addToTimeline(createMessage({ text: errorMessage }, ComponentStatus.Done));
|
|
116
|
-
requestHandlers.onCompleted(createExecuteState({ message: result.message }));
|
|
117
|
-
lifecycleHandlers.completeActive();
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
dispatch({
|
|
121
|
-
type: ExecuteActionType.ProcessingComplete,
|
|
122
|
-
payload: { message: result.message },
|
|
123
|
-
});
|
|
124
|
-
requestHandlers.onCompleted(createExecuteState({ message: result.message }));
|
|
125
|
-
lifecycleHandlers.completeActive();
|
|
126
|
-
return;
|
|
127
|
-
}
|
|
128
|
-
// Create task infos from commands
|
|
129
|
-
const taskInfos = result.commands.map((cmd, index) => ({
|
|
130
|
-
label: inputTasks[index]?.action ?? cmd.description,
|
|
131
|
-
command: cmd,
|
|
132
|
-
status: ExecutionStatus.Pending,
|
|
133
|
-
elapsed: 0,
|
|
134
|
-
}));
|
|
135
|
-
dispatch({
|
|
136
|
-
type: ExecuteActionType.CommandsReady,
|
|
137
|
-
payload: {
|
|
138
|
-
message: result.message,
|
|
139
|
-
summary: result.summary,
|
|
140
|
-
tasks: taskInfos,
|
|
141
|
-
},
|
|
142
|
-
});
|
|
143
|
-
requestHandlers.onCompleted(createExecuteState({
|
|
144
|
-
message: result.message,
|
|
145
|
-
summary: result.summary,
|
|
146
|
-
tasks: taskInfos,
|
|
147
|
-
}));
|
|
148
|
-
}
|
|
149
|
-
catch (err) {
|
|
150
|
-
await ensureMinimumTime(startTime, MINIMUM_PROCESSING_TIME);
|
|
151
|
-
if (mounted) {
|
|
152
|
-
const errorMessage = formatErrorMessage(err);
|
|
153
|
-
dispatch({
|
|
154
|
-
type: ExecuteActionType.ProcessingError,
|
|
155
|
-
payload: { error: errorMessage },
|
|
156
|
-
});
|
|
157
|
-
requestHandlers.onCompleted(createExecuteState({ error: errorMessage }));
|
|
158
|
-
requestHandlers.onError(errorMessage);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
void process(service);
|
|
163
|
-
return () => {
|
|
164
|
-
mounted = false;
|
|
165
|
-
};
|
|
166
|
-
}, [
|
|
167
|
-
inputTasks,
|
|
168
|
-
isActive,
|
|
169
|
-
service,
|
|
170
|
-
requestHandlers,
|
|
171
|
-
lifecycleHandlers,
|
|
172
|
-
workflowHandlers,
|
|
173
|
-
tasksCount,
|
|
174
|
-
hasProcessed,
|
|
175
|
-
dispatch,
|
|
176
|
-
]);
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Execute tasks sequentially, managing state and handling completion/errors.
|
|
180
|
-
*/
|
|
181
|
-
export function useTaskExecutor(config) {
|
|
182
|
-
const { isActive, tasks, message, summary, error, workdir, setWorkdir, cancelledRef, liveOutput, dispatch, requestHandlers, lifecycleHandlers, workflowHandlers, } = config;
|
|
183
|
-
const currentTaskIndex = getCurrentTaskIndex(tasks);
|
|
184
|
-
useEffect(() => {
|
|
185
|
-
if (!isActive ||
|
|
186
|
-
tasks.length === 0 ||
|
|
187
|
-
currentTaskIndex >= tasks.length ||
|
|
188
|
-
error) {
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
const currentTask = tasks[currentTaskIndex];
|
|
192
|
-
if (currentTask.status !== ExecutionStatus.Pending) {
|
|
193
|
-
return;
|
|
194
|
-
}
|
|
195
|
-
cancelledRef.current = false;
|
|
196
|
-
// Mark task as started (running)
|
|
197
|
-
dispatch({
|
|
198
|
-
type: ExecuteActionType.TaskStarted,
|
|
199
|
-
payload: { index: currentTaskIndex },
|
|
200
|
-
});
|
|
201
|
-
// Reset live state for new task
|
|
202
|
-
liveOutput.start();
|
|
203
|
-
// Merge workdir into command
|
|
204
|
-
const command = workdir
|
|
205
|
-
? { ...currentTask.command, workdir }
|
|
206
|
-
: currentTask.command;
|
|
207
|
-
void executeTask(command, currentTaskIndex, {
|
|
208
|
-
onOutputChange: (output) => {
|
|
209
|
-
if (!cancelledRef.current) {
|
|
210
|
-
liveOutput.setOutput(output);
|
|
211
|
-
}
|
|
212
|
-
},
|
|
213
|
-
onComplete: (elapsed, output) => {
|
|
214
|
-
if (cancelledRef.current)
|
|
215
|
-
return;
|
|
216
|
-
liveOutput.stop();
|
|
217
|
-
// Track working directory
|
|
218
|
-
if (output.workdir) {
|
|
219
|
-
setWorkdir(output.workdir);
|
|
220
|
-
}
|
|
221
|
-
const tasksWithOutput = tasks.map((task, i) => i === currentTaskIndex
|
|
222
|
-
? {
|
|
223
|
-
...task,
|
|
224
|
-
stdout: output.stdout,
|
|
225
|
-
stderr: output.stderr,
|
|
226
|
-
error: output.error,
|
|
227
|
-
}
|
|
228
|
-
: task);
|
|
229
|
-
const result = handleTaskCompletion(currentTaskIndex, elapsed, {
|
|
230
|
-
tasks: tasksWithOutput,
|
|
231
|
-
message,
|
|
232
|
-
summary,
|
|
233
|
-
});
|
|
234
|
-
dispatch(result.action);
|
|
235
|
-
requestHandlers.onCompleted(result.finalState);
|
|
236
|
-
if (result.shouldComplete) {
|
|
237
|
-
lifecycleHandlers.completeActive();
|
|
238
|
-
}
|
|
239
|
-
},
|
|
240
|
-
onError: (errorMsg, elapsed, output) => {
|
|
241
|
-
if (cancelledRef.current)
|
|
242
|
-
return;
|
|
243
|
-
liveOutput.stop();
|
|
244
|
-
// Track working directory
|
|
245
|
-
if (output.workdir) {
|
|
246
|
-
setWorkdir(output.workdir);
|
|
247
|
-
}
|
|
248
|
-
const tasksWithOutput = tasks.map((task, i) => i === currentTaskIndex
|
|
249
|
-
? {
|
|
250
|
-
...task,
|
|
251
|
-
stdout: output.stdout,
|
|
252
|
-
stderr: output.stderr,
|
|
253
|
-
error: output.error,
|
|
254
|
-
}
|
|
255
|
-
: task);
|
|
256
|
-
const result = handleTaskFailure(currentTaskIndex, errorMsg, elapsed, {
|
|
257
|
-
tasks: tasksWithOutput,
|
|
258
|
-
message,
|
|
259
|
-
summary,
|
|
260
|
-
});
|
|
261
|
-
dispatch(result.action);
|
|
262
|
-
requestHandlers.onCompleted(result.finalState);
|
|
263
|
-
if (result.action.type === ExecuteActionType.TaskErrorCritical) {
|
|
264
|
-
const criticalErrorMessage = getExecutionErrorMessage(errorMsg);
|
|
265
|
-
workflowHandlers.addToQueue(createFeedback({
|
|
266
|
-
type: FeedbackType.Failed,
|
|
267
|
-
message: criticalErrorMessage,
|
|
268
|
-
}));
|
|
269
|
-
}
|
|
270
|
-
if (result.shouldComplete) {
|
|
271
|
-
lifecycleHandlers.completeActive();
|
|
272
|
-
}
|
|
273
|
-
},
|
|
274
|
-
});
|
|
275
|
-
}, [
|
|
276
|
-
isActive,
|
|
277
|
-
tasks,
|
|
278
|
-
currentTaskIndex,
|
|
279
|
-
message,
|
|
280
|
-
summary,
|
|
281
|
-
error,
|
|
282
|
-
workdir,
|
|
283
|
-
setWorkdir,
|
|
284
|
-
cancelledRef,
|
|
285
|
-
liveOutput,
|
|
286
|
-
dispatch,
|
|
287
|
-
requestHandlers,
|
|
288
|
-
lifecycleHandlers,
|
|
289
|
-
workflowHandlers,
|
|
290
|
-
]);
|
|
291
|
-
}
|
package/dist/ui/Confirm.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
3
|
-
import { Box, Text } from 'ink';
|
|
4
|
-
import { ComponentStatus, } from '../types/components.js';
|
|
5
|
-
import { Colors, getTextColor, Palette } from '../services/colors.js';
|
|
6
|
-
import { useInput } from '../services/keyboard.js';
|
|
7
|
-
import { UserQuery } from './UserQuery.js';
|
|
8
|
-
export const ConfirmView = ({ message, state, status }) => {
|
|
9
|
-
const isActive = status === ComponentStatus.Active;
|
|
10
|
-
const { selectedIndex } = state;
|
|
11
|
-
const options = [
|
|
12
|
-
{ label: 'yes', value: 'yes', color: Palette.BrightGreen },
|
|
13
|
-
{ label: 'no', value: 'no', color: Colors.Status.Error },
|
|
14
|
-
];
|
|
15
|
-
// Timeline rendering (Done status)
|
|
16
|
-
if (status === ComponentStatus.Done) {
|
|
17
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Text, { color: undefined, children: message }) }), _jsxs(UserQuery, { children: ["> ", options[selectedIndex].label] })] }));
|
|
18
|
-
}
|
|
19
|
-
// Active/Pending rendering
|
|
20
|
-
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Text, { color: getTextColor(isActive), children: message }) }), _jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: Colors.Action.Select, children: ">" }), _jsx(Text, { children: " " }), _jsx(Box, { children: options.map((option, index) => {
|
|
21
|
-
const isSelected = index === selectedIndex;
|
|
22
|
-
return (_jsx(Box, { marginRight: 2, children: _jsx(Text, { color: isSelected ? option.color : undefined, dimColor: !isSelected, children: option.label }) }, option.value));
|
|
23
|
-
}) })] })] }));
|
|
24
|
-
};
|
|
25
|
-
/**
|
|
26
|
-
* Confirm controller: Manages yes/no selection
|
|
27
|
-
*/
|
|
28
|
-
export function Confirm({ message, status, requestHandlers, onConfirmed, onCancelled, }) {
|
|
29
|
-
const isActive = status === ComponentStatus.Active;
|
|
30
|
-
const [selectedIndex, setSelectedIndex] = useState(0); // 0 = Yes, 1 = No
|
|
31
|
-
useInput((input, key) => {
|
|
32
|
-
if (!isActive)
|
|
33
|
-
return;
|
|
34
|
-
if (key.escape) {
|
|
35
|
-
// Escape: highlight "No" and cancel
|
|
36
|
-
const finalState = { selectedIndex: 1, confirmed: false };
|
|
37
|
-
requestHandlers.onCompleted(finalState);
|
|
38
|
-
onCancelled();
|
|
39
|
-
}
|
|
40
|
-
else if (key.tab) {
|
|
41
|
-
// Toggle between Yes (0) and No (1)
|
|
42
|
-
setSelectedIndex((prev) => (prev === 0 ? 1 : 0));
|
|
43
|
-
}
|
|
44
|
-
else if (key.return) {
|
|
45
|
-
// Confirm selection
|
|
46
|
-
const finalState = {
|
|
47
|
-
selectedIndex,
|
|
48
|
-
confirmed: true,
|
|
49
|
-
};
|
|
50
|
-
requestHandlers.onCompleted(finalState);
|
|
51
|
-
if (selectedIndex === 0) {
|
|
52
|
-
onConfirmed();
|
|
53
|
-
}
|
|
54
|
-
else {
|
|
55
|
-
onCancelled();
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}, { isActive });
|
|
59
|
-
// Controller always renders View, passing current state
|
|
60
|
-
const state = { selectedIndex, confirmed: false };
|
|
61
|
-
return _jsx(ConfirmView, { message: message, state: state, status: status });
|
|
62
|
-
}
|
package/dist/ui/Debug.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box, Text } from 'ink';
|
|
3
|
-
const CONTENT_WIDTH = 80;
|
|
4
|
-
const HORIZONTAL_PADDING = 2;
|
|
5
|
-
export const Debug = ({ title, content, color }) => {
|
|
6
|
-
return (_jsxs(Box, { flexDirection: "column", paddingX: HORIZONTAL_PADDING, paddingY: 1, borderStyle: "single", borderColor: color, width: CONTENT_WIDTH, children: [_jsx(Text, { color: color, wrap: "wrap", children: title }), _jsx(Text, { color: color, wrap: "wrap", children: content })] }));
|
|
7
|
-
};
|
package/dist/ui/Feedback.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { Box, Text } from 'ink';
|
|
3
|
-
import { ComponentStatus } from '../types/components.js';
|
|
4
|
-
import { FeedbackType } from '../types/types.js';
|
|
5
|
-
import { getFeedbackColor } from '../services/colors.js';
|
|
6
|
-
function getSymbol(type) {
|
|
7
|
-
return {
|
|
8
|
-
[FeedbackType.Info]: 'ℹ',
|
|
9
|
-
[FeedbackType.Warning]: '⚠',
|
|
10
|
-
[FeedbackType.Succeeded]: '✓',
|
|
11
|
-
[FeedbackType.Aborted]: '⊘',
|
|
12
|
-
[FeedbackType.Failed]: '✗',
|
|
13
|
-
}[type];
|
|
14
|
-
}
|
|
15
|
-
export function Feedback({ type, message }) {
|
|
16
|
-
const color = getFeedbackColor(type, ComponentStatus.Done);
|
|
17
|
-
const symbol = getSymbol(type);
|
|
18
|
-
return (_jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: color, children: [symbol, " ", message] }) }));
|
|
19
|
-
}
|
package/dist/ui/Refinement.js
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box } from 'ink';
|
|
3
|
-
import { ComponentStatus } from '../types/components.js';
|
|
4
|
-
import { useInput } from '../services/keyboard.js';
|
|
5
|
-
import { Message } from './Message.js';
|
|
6
|
-
import { Spinner } from './Spinner.js';
|
|
7
|
-
export const RefinementView = ({ text, status }) => {
|
|
8
|
-
const isActive = status === ComponentStatus.Active;
|
|
9
|
-
return (_jsxs(Box, { gap: 1, children: [_jsx(Message, { text: text, status: status }), isActive && _jsx(Spinner, {})] }));
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* Refinement controller: Handles abort input
|
|
13
|
-
*/
|
|
14
|
-
export const Refinement = ({ text, status, onAborted }) => {
|
|
15
|
-
const isActive = status === ComponentStatus.Active;
|
|
16
|
-
useInput((_, key) => {
|
|
17
|
-
if (key.escape && isActive) {
|
|
18
|
-
onAborted('plan refinement');
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
}, { isActive });
|
|
22
|
-
return _jsx(RefinementView, { text: text, status: status });
|
|
23
|
-
};
|