prompt-language-shell 0.9.2 → 0.9.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 (56) hide show
  1. package/dist/{ui/Main.js → Main.js} +12 -12
  2. package/dist/{ui → components}/Component.js +28 -26
  3. package/dist/{ui → components}/Workflow.js +2 -3
  4. package/dist/{ui → components/controllers}/Answer.js +18 -17
  5. package/dist/{ui → components/controllers}/Command.js +11 -18
  6. package/dist/{ui → components/controllers}/Config.js +8 -116
  7. package/dist/components/controllers/Confirm.js +42 -0
  8. package/dist/{ui → components/controllers}/Execute.js +75 -144
  9. package/dist/{ui → components/controllers}/Introspect.js +12 -28
  10. package/dist/components/controllers/Refinement.js +18 -0
  11. package/dist/components/controllers/Schedule.js +139 -0
  12. package/dist/{ui → components/controllers}/Validate.js +14 -32
  13. package/dist/components/views/Answer.js +28 -0
  14. package/dist/components/views/Command.js +11 -0
  15. package/dist/components/views/Config.js +115 -0
  16. package/dist/components/views/Confirm.js +24 -0
  17. package/dist/components/views/Execute.js +60 -0
  18. package/dist/{ui → components/views}/Feedback.js +3 -3
  19. package/dist/components/views/Introspect.js +17 -0
  20. package/dist/{ui → components/views}/Label.js +3 -3
  21. package/dist/{ui → components/views}/List.js +1 -1
  22. package/dist/{ui → components/views}/Output.js +2 -2
  23. package/dist/components/views/Refinement.js +9 -0
  24. package/dist/{ui → components/views}/Report.js +1 -1
  25. package/dist/components/views/Schedule.js +120 -0
  26. package/dist/{ui → components/views}/Separator.js +1 -1
  27. package/dist/{ui → components/views}/Spinner.js +1 -1
  28. package/dist/{ui → components/views}/Subtask.js +4 -4
  29. package/dist/components/views/Task.js +18 -0
  30. package/dist/components/views/Upcoming.js +30 -0
  31. package/dist/{ui → components/views}/UserQuery.js +1 -1
  32. package/dist/components/views/Validate.js +17 -0
  33. package/dist/{ui → components/views}/Welcome.js +1 -1
  34. package/dist/configuration/steps.js +1 -1
  35. package/dist/execution/handlers.js +19 -53
  36. package/dist/execution/reducer.js +26 -38
  37. package/dist/execution/runner.js +43 -25
  38. package/dist/execution/types.js +3 -4
  39. package/dist/execution/utils.js +1 -1
  40. package/dist/index.js +1 -1
  41. package/dist/services/messages.js +19 -0
  42. package/dist/services/router.js +69 -11
  43. package/dist/services/shell.js +26 -6
  44. package/dist/services/timing.js +1 -0
  45. package/dist/skills/execute.md +15 -7
  46. package/dist/tools/execute.tool.js +0 -4
  47. package/dist/types/schemas.js +0 -1
  48. package/package.json +1 -1
  49. package/dist/execution/hooks.js +0 -291
  50. package/dist/ui/Confirm.js +0 -62
  51. package/dist/ui/Refinement.js +0 -23
  52. package/dist/ui/Schedule.js +0 -257
  53. package/dist/ui/Task.js +0 -11
  54. /package/dist/{ui → components/views}/Debug.js +0 -0
  55. /package/dist/{ui → components/views}/Message.js +0 -0
  56. /package/dist/{ui → components/views}/Panel.js +0 -0
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import { Box, Text } from 'ink';
4
+ import { ComponentStatus } from '../../types/components.js';
5
+ import { Colors, getTextColor } from '../../services/colors.js';
6
+ import { getAnswerLoadingMessage } from '../../services/messages.js';
7
+ import { ExecutionStatus } from '../../services/shell.js';
8
+ import { Spinner } from './Spinner.js';
9
+ import { Upcoming } from './Upcoming.js';
10
+ /**
11
+ * Answer view: Displays question and answer
12
+ */
13
+ export const AnswerView = ({ status, question, lines, error, upcoming, cancelled = false, }) => {
14
+ const isActive = status === ComponentStatus.Active;
15
+ const isLoading = isActive && !lines && !error;
16
+ const loadingMessage = useMemo(() => getAnswerLoadingMessage(), []);
17
+ // Determine upcoming status: cancelled, error, or pending
18
+ const isTerminated = cancelled || error !== null;
19
+ const upcomingStatus = cancelled
20
+ ? ExecutionStatus.Aborted
21
+ : error
22
+ ? ExecutionStatus.Failed
23
+ : ExecutionStatus.Pending;
24
+ // Build full list of items to show - include current question when terminated
25
+ const upcomingItems = isTerminated && upcoming ? [question, ...upcoming] : (upcoming ?? []);
26
+ const showUpcoming = upcomingItems.length > 0 && (isActive || isTerminated);
27
+ return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isLoading && (_jsxs(_Fragment, { children: [_jsx(Box, { marginLeft: 1, marginBottom: 1, children: _jsx(Text, { color: getTextColor(isActive), children: question }) }), _jsxs(Box, { paddingLeft: 3, marginBottom: 1, children: [_jsxs(Text, { color: getTextColor(isActive), children: [loadingMessage, " "] }), _jsx(Spinner, {})] })] })), lines && lines.length > 0 && (_jsxs(_Fragment, { children: [_jsx(Box, { marginLeft: 1, marginBottom: 1, children: _jsx(Text, { color: getTextColor(isActive), children: question }) }), _jsx(Box, { flexDirection: "column", paddingLeft: 3, children: lines.map((line, index) => (_jsx(Text, { color: getTextColor(isActive), children: line }, index))) })] })), showUpcoming && (_jsx(Box, { marginTop: lines && lines.length > 0 ? 1 : 0, children: _jsx(Upcoming, { items: upcomingItems, status: upcomingStatus }) })), error && (_jsx(Box, { marginTop: 1, marginLeft: 1, children: _jsxs(Text, { color: Colors.Status.Error, children: ["Error: ", error] }) }))] }));
28
+ };
@@ -0,0 +1,11 @@
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 { Colors } from '../../services/colors.js';
5
+ import { Spinner } from './Spinner.js';
6
+ import { UserQuery } from './UserQuery.js';
7
+ export const CommandView = ({ command, state, status }) => {
8
+ const isActive = status === ComponentStatus.Active;
9
+ const { error } = state;
10
+ return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [!isActive ? (_jsxs(UserQuery, { children: ["> pls ", command] })) : (_jsxs(Box, { marginLeft: 1, children: [_jsxs(Text, { color: Colors.Text.Active, children: ["> pls ", command] }), _jsx(Text, { children: " " }), _jsx(Spinner, {})] })), error && (_jsx(Box, { marginTop: 1, marginLeft: 1, children: _jsxs(Text, { color: Colors.Status.Error, children: ["Error: ", error] }) }))] }));
11
+ };
@@ -0,0 +1,115 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from 'react';
3
+ import { Box, Text, useFocus } from 'ink';
4
+ import TextInput from 'ink-text-input';
5
+ import { ComponentStatus } from '../../types/components.js';
6
+ import { Colors } from '../../services/colors.js';
7
+ import { DebugLevel } from '../../configuration/types.js';
8
+ import { useInput } from '../../services/keyboard.js';
9
+ /**
10
+ * Get postfix with debug brackets if debug is enabled
11
+ * Info: {key} | Verbose: {key} entry
12
+ */
13
+ function getPostfix(text, debugLevel) {
14
+ if (debugLevel === DebugLevel.None || !text) {
15
+ return '';
16
+ }
17
+ if (debugLevel === DebugLevel.Info) {
18
+ return `{${text}}`;
19
+ }
20
+ return `{${text}} entry`;
21
+ }
22
+ export var StepType;
23
+ (function (StepType) {
24
+ StepType["Text"] = "text";
25
+ StepType["Selection"] = "selection";
26
+ })(StepType || (StepType = {}));
27
+ function TextStep({ value, placeholder, validate, onChange, onSubmit, }) {
28
+ const [inputValue, setInputValue] = useState(value);
29
+ const [validationFailed, setValidationFailed] = useState(false);
30
+ const { isFocused } = useFocus({ autoFocus: true });
31
+ // Sync internal state with prop changes
32
+ useEffect(() => {
33
+ setInputValue(value);
34
+ }, [value]);
35
+ const handleChange = (newValue) => {
36
+ setInputValue(newValue);
37
+ onChange(newValue);
38
+ if (validationFailed) {
39
+ setValidationFailed(false);
40
+ }
41
+ };
42
+ const handleSubmit = (value) => {
43
+ // Use placeholder if input is empty
44
+ const finalValue = value || placeholder || '';
45
+ if (!validate(finalValue)) {
46
+ setValidationFailed(true);
47
+ return;
48
+ }
49
+ onSubmit(finalValue);
50
+ };
51
+ // Handle input manually when validation fails
52
+ useInput((input, key) => {
53
+ if (!validationFailed)
54
+ return;
55
+ if (key.return) {
56
+ handleSubmit(inputValue);
57
+ }
58
+ else if (key.backspace || key.delete) {
59
+ const newValue = inputValue.slice(0, -1);
60
+ handleChange(newValue);
61
+ }
62
+ else if (!key.ctrl && !key.meta && input) {
63
+ const newValue = inputValue + input;
64
+ handleChange(newValue);
65
+ }
66
+ }, { isActive: validationFailed });
67
+ // When validation fails, show colored text
68
+ if (validationFailed) {
69
+ return (_jsxs(Text, { color: Colors.Status.Error, children: [inputValue || placeholder, isFocused && _jsx(Text, { inverse: true, children: " " })] }));
70
+ }
71
+ return (_jsx(TextInput, { value: inputValue, onChange: handleChange, onSubmit: handleSubmit, placeholder: placeholder }));
72
+ }
73
+ function SelectionStep({ options, selectedIndex, isCurrentStep, }) {
74
+ return (_jsx(Box, { children: options.map((option, optIndex) => {
75
+ const isSelected = optIndex === selectedIndex;
76
+ return (_jsx(Box, { marginRight: 2, children: _jsx(Text, { dimColor: !isSelected || !isCurrentStep, bold: isSelected, children: option.label }) }, option.value));
77
+ }) }));
78
+ }
79
+ export const ConfigView = ({ steps, state, status, debug = DebugLevel.None, onInputChange, onInputSubmit, }) => {
80
+ const isActive = status === ComponentStatus.Active;
81
+ const { values, completedStep, selectedIndex } = state;
82
+ const renderStepInput = (stepConfig, isCurrentStep) => {
83
+ const configKey = stepConfig.path || stepConfig.key;
84
+ const displayValue = values[configKey];
85
+ switch (stepConfig.type) {
86
+ case StepType.Text:
87
+ if (isCurrentStep && onInputChange && onInputSubmit) {
88
+ return (_jsx(TextStep, { value: values[configKey] || '', placeholder: stepConfig.value || undefined, validate: stepConfig.validate, onChange: onInputChange, onSubmit: onInputSubmit }));
89
+ }
90
+ return (_jsx(Text, { dimColor: true, wrap: "truncate-end", children: displayValue || '' }));
91
+ case StepType.Selection: {
92
+ if (!isCurrentStep) {
93
+ const option = stepConfig.options.find((opt) => opt.value === displayValue);
94
+ return _jsx(Text, { dimColor: true, children: option?.label || '' });
95
+ }
96
+ return (_jsx(SelectionStep, { options: stepConfig.options, selectedIndex: selectedIndex, isCurrentStep: true }));
97
+ }
98
+ default: {
99
+ const _exhaustiveCheck = stepConfig;
100
+ throw new Error('Unsupported step type');
101
+ }
102
+ }
103
+ };
104
+ return (_jsx(Box, { flexDirection: "column", marginLeft: 1, children: steps.map((stepConfig, index) => {
105
+ const isCurrentStep = index === completedStep && isActive;
106
+ const isCompleted = index < completedStep;
107
+ const wasAborted = index === completedStep && !isActive;
108
+ const shouldShow = isCompleted || isCurrentStep || wasAborted;
109
+ if (!shouldShow) {
110
+ return null;
111
+ }
112
+ const postfix = getPostfix(stepConfig.path, debug);
113
+ return (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { children: [_jsx(Text, { children: stepConfig.description }), _jsx(Text, { children: ": " }), postfix && _jsx(Text, { color: Colors.Type.Config, children: postfix })] }), _jsxs(Box, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: Colors.Action.Select, dimColor: !isCurrentStep, children: ">" }), _jsx(Text, { children: " " }), renderStepInput(stepConfig, isCurrentStep)] })] }, stepConfig.path || stepConfig.key));
114
+ }) }));
115
+ };
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import { ComponentStatus } from '../../types/components.js';
4
+ import { Colors, getTextColor, Palette } from '../../services/colors.js';
5
+ import { UserQuery } from './UserQuery.js';
6
+ const OPTIONS = [
7
+ { label: 'yes', value: 'yes', color: Palette.BrightGreen },
8
+ { label: 'no', value: 'no', color: Colors.Status.Error },
9
+ ];
10
+ /**
11
+ * Confirm view: Displays yes/no confirmation prompt
12
+ */
13
+ export const ConfirmView = ({ status, message, selectedIndex, }) => {
14
+ const isActive = status === ComponentStatus.Active;
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
+ };
@@ -0,0 +1,60 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import { getTextColor, Palette } from '../../services/colors.js';
4
+ import { ExecutionStatus } from '../../services/shell.js';
5
+ import { Spinner } from './Spinner.js';
6
+ import { TaskView } from './Task.js';
7
+ import { Upcoming } from './Upcoming.js';
8
+ /**
9
+ * Check if a task is finished (success, failed, or aborted)
10
+ */
11
+ function isTaskFinished(task) {
12
+ return (task.status === ExecutionStatus.Success ||
13
+ task.status === ExecutionStatus.Failed ||
14
+ task.status === ExecutionStatus.Aborted);
15
+ }
16
+ /**
17
+ * Determine the upcoming status based on task states
18
+ */
19
+ function getUpcomingStatus(tasks) {
20
+ const hasFailed = tasks.some((task) => task.status === ExecutionStatus.Failed);
21
+ const hasCancelled = tasks.some((task) => task.status === ExecutionStatus.Aborted ||
22
+ task.status === ExecutionStatus.Cancelled);
23
+ if (hasFailed)
24
+ return ExecutionStatus.Failed;
25
+ if (hasCancelled)
26
+ return ExecutionStatus.Aborted;
27
+ return ExecutionStatus.Pending;
28
+ }
29
+ /**
30
+ * Convert ExecuteState to view props for timeline rendering
31
+ */
32
+ export function mapStateToViewProps(state, isActive, upcoming, label) {
33
+ return {
34
+ isLoading: false,
35
+ isExecuting: false,
36
+ isActive,
37
+ error: state.error,
38
+ message: state.message,
39
+ tasks: state.tasks,
40
+ completionMessage: state.completionMessage,
41
+ showTasks: state.tasks.length > 0,
42
+ upcoming,
43
+ label,
44
+ };
45
+ }
46
+ /**
47
+ * Execute view: Pure display component for task execution progress
48
+ */
49
+ export const ExecuteView = ({ isLoading, isExecuting, isActive, error, message, tasks, completionMessage, showTasks, upcoming, label, }) => {
50
+ // Return null only when loading completes with no commands
51
+ if (!isActive && tasks.length === 0 && !error) {
52
+ return null;
53
+ }
54
+ // Determine upcoming status based on task states
55
+ const upcomingStatus = getUpcomingStatus(tasks);
56
+ const isTerminated = upcomingStatus !== ExecutionStatus.Pending;
57
+ // Show upcoming during active execution or when terminated (to show skipped tasks)
58
+ const showUpcoming = upcoming && upcoming.length > 0 && (isActive || isTerminated);
59
+ return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isLoading && (_jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [label && (_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: getTextColor(isActive), children: label }) })), _jsxs(Box, { marginLeft: label ? 2 : 0, children: [_jsx(Text, { color: Palette.Gray, children: "Preparing commands. " }), _jsx(Spinner, {})] })] })), (isExecuting || showTasks) && (_jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [message && (_jsxs(Box, { marginBottom: 1, gap: 1, children: [_jsx(Text, { color: getTextColor(isActive), children: message }), isExecuting && _jsx(Spinner, {})] })), tasks.map((task, index) => (_jsx(Box, { marginBottom: index < tasks.length - 1 ? 1 : 0, children: _jsx(TaskView, { label: task.label, command: task.command, status: task.status, elapsed: task.elapsed, output: task.output, isFinished: isTaskFinished(task), isActive: isActive }) }, index)))] })), showUpcoming && (_jsx(Box, { marginTop: 1, children: _jsx(Upcoming, { items: upcoming, status: upcomingStatus }) })), completionMessage && !isActive && (_jsx(Box, { marginTop: 1, marginLeft: 1, children: _jsx(Text, { color: getTextColor(false), children: completionMessage }) })), error && (_jsx(Box, { marginLeft: 1, children: _jsx(Text, { children: error }) }))] }));
60
+ };
@@ -1,8 +1,8 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
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';
3
+ import { ComponentStatus } from '../../types/components.js';
4
+ import { FeedbackType } from '../../types/types.js';
5
+ import { getFeedbackColor } from '../../services/colors.js';
6
6
  function getSymbol(type) {
7
7
  return {
8
8
  [FeedbackType.Info]: 'ℹ',
@@ -0,0 +1,17 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import { ComponentStatus } from '../../types/components.js';
4
+ import { Colors, getTextColor } from '../../services/colors.js';
5
+ import { Spinner } from './Spinner.js';
6
+ /**
7
+ * Introspect view: Displays capabilities list
8
+ */
9
+ export const IntrospectView = ({ status, hasCapabilities, error, children, }) => {
10
+ const isActive = status === ComponentStatus.Active;
11
+ const isLoading = isActive && !hasCapabilities && !error;
12
+ // Don't render wrapper when done and nothing to show
13
+ if (!isActive && !error && !children) {
14
+ return null;
15
+ }
16
+ return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isLoading && (_jsxs(Box, { marginLeft: 1, children: [_jsx(Text, { color: getTextColor(isActive), children: "Listing capabilities. " }), _jsx(Spinner, {})] })), error && (_jsx(Box, { marginTop: 1, marginLeft: 1, children: _jsxs(Text, { color: Colors.Status.Error, children: ["Error: ", error] }) })), children] }));
17
+ };
@@ -1,9 +1,9 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
- import { getTaskColors, getTaskTypeLabel } from '../services/colors.js';
4
- import { DebugLevel } from '../configuration/types.js';
3
+ import { getTaskColors, getTaskTypeLabel } from '../../services/colors.js';
4
+ import { DebugLevel } from '../../configuration/types.js';
5
5
  import { Separator } from './Separator.js';
6
- import { ComponentStatus } from '../types/components.js';
6
+ import { ComponentStatus } from '../../types/components.js';
7
7
  export function Label({ description, taskType, showType = false, status = ComponentStatus.Done, debug = DebugLevel.None, }) {
8
8
  const colors = getTaskColors(taskType, status);
9
9
  return (_jsxs(Box, { children: [_jsx(Text, { color: colors.description, children: description }), showType && (_jsxs(_Fragment, { children: [_jsx(Separator, {}), _jsx(Text, { color: colors.type, children: getTaskTypeLabel(taskType, debug) })] }))] }));
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
- import { Palette } from '../services/colors.js';
3
+ import { Palette } from '../../services/colors.js';
4
4
  import { Separator } from './Separator.js';
5
5
  export const List = ({ items, level = 0, highlightedIndex = null, highlightedParentIndex = null, showType = false, compact = false, }) => {
6
6
  const marginLeft = level > 0 ? 2 : 0;
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
- import { Palette } from '../services/colors.js';
4
- import { ExecutionStatus } from '../services/shell.js';
3
+ import { Palette } from '../../services/colors.js';
4
+ import { ExecutionStatus } from '../../services/shell.js';
5
5
  const MAX_LINES = 8;
6
6
  const MAX_WIDTH = 75;
7
7
  const SHORT_OUTPUT_THRESHOLD = 4;
@@ -0,0 +1,9 @@
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 { Message } from './Message.js';
5
+ import { Spinner } from './Spinner.js';
6
+ export const RefinementView = ({ text, status }) => {
7
+ const isActive = status === ComponentStatus.Active;
8
+ return (_jsxs(Box, { gap: 1, children: [_jsx(Message, { text: text, status: status }), isActive && _jsx(Spinner, {})] }));
9
+ };
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
- import { Colors, getOriginColor } from '../services/colors.js';
3
+ import { Colors, getOriginColor } from '../../services/colors.js';
4
4
  function CapabilityItem({ name, description, origin, isIncomplete, }) {
5
5
  const color = getOriginColor(origin);
6
6
  return (_jsxs(Box, { children: [_jsx(Text, { children: "- " }), _jsx(Text, { color: color, children: name }), _jsxs(Text, { children: [" - ", description] }), isIncomplete && _jsx(Text, { color: Colors.Status.Warning, children: " (incomplete)" })] }));
@@ -0,0 +1,120 @@
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 { TaskType } from '../../types/types.js';
5
+ import { DebugLevel } from '../../configuration/types.js';
6
+ import { getTaskColors, getTaskTypeLabel, Palette, } from '../../services/colors.js';
7
+ import { Label } from './Label.js';
8
+ import { List } from './List.js';
9
+ export function taskToListItem(task, highlightedChildIndex = null, isDefineTaskWithoutSelection = false, status = ComponentStatus.Done, debug = DebugLevel.None) {
10
+ const taskColors = getTaskColors(task.type, status);
11
+ // Determine description color based on status
12
+ let descriptionColor = taskColors.description;
13
+ if (status === ComponentStatus.Pending) {
14
+ descriptionColor = Palette.SoftWhite;
15
+ }
16
+ const item = {
17
+ description: {
18
+ text: task.action,
19
+ color: descriptionColor,
20
+ },
21
+ type: { text: getTaskTypeLabel(task.type, debug), color: taskColors.type },
22
+ children: [],
23
+ };
24
+ // Mark define tasks with right arrow when no selection has been made
25
+ if (isDefineTaskWithoutSelection) {
26
+ item.marker = ' → ';
27
+ item.markerColor = getTaskColors(TaskType.Schedule, status).type;
28
+ }
29
+ // Add children for Define tasks with options
30
+ if (task.type === TaskType.Define && Array.isArray(task.params?.options)) {
31
+ item.children = task.params.options.map((option, index) => {
32
+ // Determine the type based on selection state
33
+ let childType = TaskType.Select;
34
+ if (highlightedChildIndex !== null) {
35
+ // A selection was made - mark others as discarded
36
+ childType =
37
+ index === highlightedChildIndex ? TaskType.Execute : TaskType.Discard;
38
+ }
39
+ const colors = getTaskColors(childType, status);
40
+ const planColors = getTaskColors(TaskType.Schedule, status);
41
+ return {
42
+ description: {
43
+ text: option,
44
+ color: colors.description,
45
+ highlightedColor: planColors.description,
46
+ },
47
+ type: {
48
+ text: getTaskTypeLabel(childType, debug),
49
+ color: colors.type,
50
+ highlightedColor: planColors.type,
51
+ },
52
+ };
53
+ });
54
+ }
55
+ // Add children for Group tasks with subtasks
56
+ const scheduledTask = task;
57
+ if (task.type === TaskType.Group &&
58
+ scheduledTask.subtasks &&
59
+ Array.isArray(scheduledTask.subtasks) &&
60
+ scheduledTask.subtasks.length > 0) {
61
+ item.children = scheduledTask.subtasks.map((subtask) => {
62
+ const subtaskColors = getTaskColors(subtask.type, status);
63
+ return {
64
+ description: {
65
+ text: subtask.action,
66
+ color: Palette.AshGray,
67
+ },
68
+ type: {
69
+ text: getTaskTypeLabel(subtask.type, debug),
70
+ color: subtaskColors.type,
71
+ },
72
+ };
73
+ });
74
+ }
75
+ return item;
76
+ }
77
+ /**
78
+ * Schedule view: Displays task list with navigation
79
+ */
80
+ export const ScheduleView = ({ status, message, tasks, highlightedIndex, currentDefineGroupIndex, completedSelections, debug = DebugLevel.None, }) => {
81
+ const isActive = status === ComponentStatus.Active;
82
+ // Use compact mode when all tasks are Config type
83
+ const isCompact = tasks.every((task) => task.type === TaskType.Config);
84
+ // Find all Define tasks
85
+ const defineTaskIndices = tasks
86
+ .map((t, idx) => (t.type === TaskType.Define ? idx : -1))
87
+ .filter((idx) => idx !== -1);
88
+ // Get the current active define task
89
+ const currentDefineTaskIndex = defineTaskIndices[currentDefineGroupIndex] ?? -1;
90
+ const listItems = tasks.map((task, idx) => {
91
+ // Find which define group this task belongs to (if any)
92
+ const defineGroupIndex = defineTaskIndices.indexOf(idx);
93
+ const isDefineTask = defineGroupIndex !== -1;
94
+ // Determine child selection state
95
+ let childIndex = null;
96
+ if (isDefineTask) {
97
+ if (defineGroupIndex < currentDefineGroupIndex) {
98
+ // Previously completed group - show the selection
99
+ childIndex = completedSelections[defineGroupIndex] ?? null;
100
+ }
101
+ else if (defineGroupIndex === currentDefineGroupIndex) {
102
+ // Current active group - show live navigation unless not active
103
+ if (!isActive) {
104
+ // If not active, show the completed selection for this group too
105
+ childIndex = completedSelections[defineGroupIndex] ?? null;
106
+ }
107
+ else {
108
+ childIndex = null;
109
+ }
110
+ }
111
+ }
112
+ // Show arrow on current active define task when no child is highlighted and is active
113
+ const isDefineWithoutSelection = isDefineTask &&
114
+ defineGroupIndex === currentDefineGroupIndex &&
115
+ highlightedIndex === null &&
116
+ isActive;
117
+ return taskToListItem(task, childIndex, isDefineWithoutSelection, status, debug);
118
+ });
119
+ return (_jsxs(Box, { flexDirection: "column", children: [message && (_jsx(Box, { marginBottom: 1, marginLeft: 1, children: _jsx(Label, { description: message, taskType: TaskType.Schedule, showType: debug !== DebugLevel.None, status: status, debug: debug }) })), _jsx(Box, { marginLeft: 1, children: _jsx(List, { items: listItems, highlightedIndex: currentDefineTaskIndex >= 0 ? highlightedIndex : null, highlightedParentIndex: currentDefineTaskIndex, showType: debug !== DebugLevel.None, compact: isCompact }) })] }));
120
+ };
@@ -1,6 +1,6 @@
1
1
  import { jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Text } from 'ink';
3
- import { Colors } from '../services/colors.js';
3
+ import { Colors } from '../../services/colors.js';
4
4
  export const Separator = ({ color = Colors.Label.Discarded, spaces = 1, }) => {
5
5
  const spacing = ' '.repeat(spaces);
6
6
  return (_jsxs(Text, { color: color, children: [spacing, "\u203A", spacing] }));
@@ -1,7 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useState } from 'react';
3
3
  import { Text } from 'ink';
4
- import { Palette } from '../services/colors.js';
4
+ import { Palette } from '../../services/colors.js';
5
5
  const FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
6
6
  const INTERVAL = 80;
7
7
  const CYCLE = FRAMES.length * INTERVAL;
@@ -1,8 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
- import { getStatusColors, Palette, STATUS_ICONS } from '../services/colors.js';
4
- import { ExecutionStatus } from '../services/shell.js';
5
- import { formatDuration } from '../services/utils.js';
3
+ import { getStatusColors, Palette, STATUS_ICONS, } from '../../services/colors.js';
4
+ import { ExecutionStatus } from '../../services/shell.js';
5
+ import { formatDuration } from '../../services/utils.js';
6
6
  import { Spinner } from './Spinner.js';
7
7
  /**
8
8
  * Pure display component for a single subtask.
@@ -12,11 +12,11 @@ export function SubtaskView({ label, command, status, elapsed, }) {
12
12
  const colors = getStatusColors(status);
13
13
  const isCancelled = status === ExecutionStatus.Cancelled;
14
14
  const isAborted = status === ExecutionStatus.Aborted;
15
- const shouldStrikethrough = isCancelled || isAborted;
16
15
  const isFinished = status === ExecutionStatus.Success ||
17
16
  status === ExecutionStatus.Failed ||
18
17
  status === ExecutionStatus.Aborted;
19
18
  // Apply strikethrough for cancelled and aborted tasks
19
+ const shouldStrikethrough = isCancelled || isAborted;
20
20
  const formatText = (text) => shouldStrikethrough ? text.split('').join('\u0336') + '\u0336' : text;
21
21
  return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { paddingLeft: 2, gap: 1, children: [_jsx(Text, { color: colors.icon, children: STATUS_ICONS[status] }), _jsx(Text, { color: colors.description, children: shouldStrikethrough
22
22
  ? formatText(label || command.description)
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box } from 'ink';
3
+ import { loadDebugSetting } from '../../configuration/io.js';
4
+ import { DebugLevel } from '../../configuration/types.js';
5
+ import { Output } from './Output.js';
6
+ import { SubtaskView } from './Subtask.js';
7
+ /**
8
+ * Pure display component for a task.
9
+ * Combines SubtaskView (label/command/status) with Output (stdout/stderr).
10
+ * Output is shown during active execution, or in timeline only with debug mode.
11
+ */
12
+ export function TaskView({ label, command, status, elapsed, output, isFinished, isActive = false, }) {
13
+ const stdout = output?.stdout ?? '';
14
+ const stderr = output?.stderr ?? '';
15
+ // Show output during active execution, or in timeline only with debug enabled
16
+ const showOutput = isActive || loadDebugSetting() !== DebugLevel.None;
17
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SubtaskView, { label: label, command: command, status: status, elapsed: elapsed }), showOutput && (_jsx(Output, { stdout: stdout, stderr: stderr, isFinished: isFinished, status: status }, `${stdout.length}-${stderr.length}`))] }));
18
+ }
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import { Palette } from '../../services/colors.js';
4
+ import { ExecutionStatus } from '../../services/shell.js';
5
+ /**
6
+ * Branching symbols for tree-like display
7
+ */
8
+ const BRANCH_MIDDLE = '├─';
9
+ const BRANCH_LAST = '└─';
10
+ /**
11
+ * Labels for each status
12
+ */
13
+ const STATUS_LABELS = {
14
+ [ExecutionStatus.Pending]: 'Next:',
15
+ [ExecutionStatus.Failed]: 'Skipped:',
16
+ [ExecutionStatus.Aborted]: 'Cancelled:',
17
+ };
18
+ /**
19
+ * Upcoming: Displays upcoming tasks in a tree-like structure
20
+ */
21
+ export const Upcoming = ({ items, status = ExecutionStatus.Pending, }) => {
22
+ if (items.length === 0)
23
+ return null;
24
+ const strikethrough = status !== ExecutionStatus.Pending;
25
+ return (_jsxs(Box, { flexDirection: "column", marginLeft: 1, children: [_jsx(Text, { color: Palette.Gray, children: STATUS_LABELS[status] }), items.map((name, index) => {
26
+ const isLast = index === items.length - 1;
27
+ const symbol = isLast ? BRANCH_LAST : BRANCH_MIDDLE;
28
+ return (_jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: Palette.DarkGray, strikethrough: strikethrough, children: [symbol, " ", name] }) }, index));
29
+ })] }));
30
+ };
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
- import { Colors } from '../services/colors.js';
3
+ import { Colors } from '../../services/colors.js';
4
4
  export function UserQuery({ children }) {
5
5
  return (_jsx(Box, { paddingX: 1, alignSelf: "flex-start", backgroundColor: Colors.Background.UserQuery, children: _jsx(Text, { color: Colors.Text.UserQuery, children: children }) }));
6
6
  }
@@ -0,0 +1,17 @@
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 { Colors, getTextColor } from '../../services/colors.js';
5
+ import { Spinner } from './Spinner.js';
6
+ /**
7
+ * Validate view: Displays validation and config prompt
8
+ */
9
+ export const ValidateView = ({ status, completionMessage, error, }) => {
10
+ const isActive = status === ComponentStatus.Active;
11
+ const isLoading = isActive && !completionMessage && !error;
12
+ // Don't render when not active and nothing to show
13
+ if (!isActive && !completionMessage && !error) {
14
+ return null;
15
+ }
16
+ return (_jsxs(Box, { alignSelf: "flex-start", flexDirection: "column", children: [isLoading && (_jsxs(Box, { marginLeft: 1, children: [_jsxs(Text, { color: getTextColor(isActive), children: ["Validating configuration requirements.", ' '] }), _jsx(Spinner, {})] })), completionMessage && (_jsx(Box, { marginLeft: 1, children: _jsx(Text, { color: getTextColor(isActive), children: completionMessage }) })), error && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: Colors.Status.Error, children: ["Error: ", error] }) }))] }));
17
+ };
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
- import { Palette } from '../services/colors.js';
3
+ import { Palette } from '../../services/colors.js';
4
4
  import { Panel } from './Panel.js';
5
5
  export function Welcome({ app }) {
6
6
  return (_jsx(Box, { alignSelf: "flex-start", children: _jsxs(Panel, { children: [_jsx(Header, { app: app }), _jsx(Description, { description: app.description }), _jsx(Usage, {})] }) }));
@@ -4,7 +4,7 @@ import { getConfigPath, loadConfig } from './io.js';
4
4
  import { getConfigSchema } from './schema.js';
5
5
  import { getConfigLabel } from './labels.js';
6
6
  import { defaultFileSystem } from '../services/filesystem.js';
7
- import { StepType } from '../ui/Config.js';
7
+ import { StepType } from '../components/controllers/Config.js';
8
8
  export function createConfigSteps() {
9
9
  // Use schema-based config step generation for required Anthropic settings
10
10
  return createConfigStepsFromSchema(['anthropic.key', 'anthropic.model']);