wiggum-cli 0.16.0 → 0.17.1
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/bin/ralph.js +0 -0
- package/dist/agent/memory/ingest.d.ts +14 -0
- package/dist/agent/memory/ingest.js +77 -0
- package/dist/agent/memory/store.d.ts +15 -0
- package/dist/agent/memory/store.js +98 -0
- package/dist/agent/memory/types.d.ts +16 -0
- package/dist/agent/memory/types.js +14 -0
- package/dist/agent/orchestrator.d.ts +7 -0
- package/dist/agent/orchestrator.js +266 -0
- package/dist/agent/resolve-config.d.ts +26 -0
- package/dist/agent/resolve-config.js +43 -0
- package/dist/agent/tools/backlog.d.ts +27 -0
- package/dist/agent/tools/backlog.js +51 -0
- package/dist/agent/tools/dry-run.d.ts +106 -0
- package/dist/agent/tools/dry-run.js +119 -0
- package/dist/agent/tools/execution.d.ts +51 -0
- package/dist/agent/tools/execution.js +256 -0
- package/dist/agent/tools/feature-state.d.ts +43 -0
- package/dist/agent/tools/feature-state.js +184 -0
- package/dist/agent/tools/introspection.d.ts +23 -0
- package/dist/agent/tools/introspection.js +40 -0
- package/dist/agent/tools/memory.d.ts +44 -0
- package/dist/agent/tools/memory.js +99 -0
- package/dist/agent/tools/preflight.d.ts +7 -0
- package/dist/agent/tools/preflight.js +137 -0
- package/dist/agent/tools/reporting.d.ts +58 -0
- package/dist/agent/tools/reporting.js +119 -0
- package/dist/agent/tools/schemas.d.ts +2 -0
- package/dist/agent/tools/schemas.js +3 -0
- package/dist/agent/types.d.ts +45 -0
- package/dist/agent/types.js +1 -0
- package/dist/ai/conversation/conversation-manager.js +8 -0
- package/dist/ai/conversation/url-fetcher.js +27 -0
- package/dist/ai/providers.js +5 -5
- package/dist/commands/agent.d.ts +17 -0
- package/dist/commands/agent.js +114 -0
- package/dist/commands/monitor.js +50 -183
- package/dist/commands/new-auto.d.ts +15 -0
- package/dist/commands/new-auto.js +237 -0
- package/dist/commands/run.js +20 -10
- package/dist/commands/sync.d.ts +15 -0
- package/dist/commands/sync.js +68 -0
- package/dist/generator/config.d.ts +1 -41
- package/dist/generator/config.js +7 -0
- package/dist/generator/index.d.ts +2 -2
- package/dist/generator/templates.d.ts +2 -0
- package/dist/generator/templates.js +9 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +115 -4
- package/dist/repl/command-parser.d.ts +10 -0
- package/dist/repl/command-parser.js +10 -0
- package/dist/templates/prompts/PROMPT.md.tmpl +13 -10
- package/dist/templates/prompts/PROMPT_e2e.md.tmpl +13 -7
- package/dist/templates/prompts/PROMPT_feature.md.tmpl +16 -3
- package/dist/templates/prompts/PROMPT_review_auto.md.tmpl +32 -12
- package/dist/templates/prompts/PROMPT_review_manual.md.tmpl +4 -1
- package/dist/templates/prompts/PROMPT_review_merge.md.tmpl +39 -14
- package/dist/templates/prompts/PROMPT_verify.md.tmpl +5 -2
- package/dist/templates/scripts/feature-loop.sh.tmpl +441 -69
- package/dist/tui/app.d.ts +19 -2
- package/dist/tui/app.js +23 -4
- package/dist/tui/components/IssuePicker.d.ts +27 -0
- package/dist/tui/components/IssuePicker.js +73 -0
- package/dist/tui/components/RunCompletionSummary.js +6 -3
- package/dist/tui/components/StatusLine.d.ts +3 -1
- package/dist/tui/components/StatusLine.js +2 -2
- package/dist/tui/hooks/useAgentOrchestrator.d.ts +40 -0
- package/dist/tui/hooks/useAgentOrchestrator.js +491 -0
- package/dist/tui/orchestration/interview-orchestrator.d.ts +5 -1
- package/dist/tui/orchestration/interview-orchestrator.js +27 -6
- package/dist/tui/screens/AgentScreen.d.ts +24 -0
- package/dist/tui/screens/AgentScreen.js +209 -0
- package/dist/tui/screens/InitScreen.js +4 -0
- package/dist/tui/screens/InterviewScreen.d.ts +3 -1
- package/dist/tui/screens/InterviewScreen.js +146 -10
- package/dist/tui/screens/MainShell.d.ts +1 -1
- package/dist/tui/screens/MainShell.js +115 -4
- package/dist/tui/screens/RunScreen.js +72 -16
- package/dist/tui/utils/build-run-summary.d.ts +1 -1
- package/dist/tui/utils/build-run-summary.js +40 -84
- package/dist/tui/utils/clear-screen.d.ts +14 -0
- package/dist/tui/utils/clear-screen.js +16 -0
- package/dist/tui/utils/git-summary.d.ts +1 -0
- package/dist/tui/utils/git-summary.js +16 -0
- package/dist/tui/utils/loop-status.d.ts +41 -1
- package/dist/tui/utils/loop-status.js +243 -35
- package/dist/tui/utils/pr-summary.d.ts +3 -2
- package/dist/tui/utils/pr-summary.js +41 -6
- package/dist/utils/config.d.ts +8 -0
- package/dist/utils/config.js +8 -0
- package/dist/utils/github.d.ts +32 -0
- package/dist/utils/github.js +106 -0
- package/package.json +4 -1
- package/src/templates/prompts/PROMPT.md.tmpl +13 -10
- package/src/templates/prompts/PROMPT_e2e.md.tmpl +13 -7
- package/src/templates/prompts/PROMPT_feature.md.tmpl +16 -3
- package/src/templates/prompts/PROMPT_review_auto.md.tmpl +32 -12
- package/src/templates/prompts/PROMPT_review_manual.md.tmpl +4 -1
- package/src/templates/prompts/PROMPT_review_merge.md.tmpl +39 -14
- package/src/templates/prompts/PROMPT_verify.md.tmpl +5 -2
- package/src/templates/scripts/feature-loop.sh.tmpl +441 -69
package/dist/tui/app.d.ts
CHANGED
|
@@ -16,7 +16,7 @@ import type { SessionState } from '../repl/session-state.js';
|
|
|
16
16
|
/**
|
|
17
17
|
* Available screen types for the App component
|
|
18
18
|
*/
|
|
19
|
-
export type AppScreen = 'shell' | 'interview' | 'init' | 'run';
|
|
19
|
+
export type AppScreen = 'shell' | 'interview' | 'init' | 'run' | 'agent';
|
|
20
20
|
/**
|
|
21
21
|
* Props for the interview screen
|
|
22
22
|
*/
|
|
@@ -31,6 +31,19 @@ export interface InterviewAppProps {
|
|
|
31
31
|
model: string;
|
|
32
32
|
/** Optional scan result with detected tech stack */
|
|
33
33
|
scanResult?: ScanResult;
|
|
34
|
+
/** References to auto-add during context phase (from CLI --issue/--context flags) */
|
|
35
|
+
initialReferences?: string[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Props for the agent screen when launched directly from CLI
|
|
39
|
+
*/
|
|
40
|
+
export interface AgentAppProps {
|
|
41
|
+
modelOverride?: string;
|
|
42
|
+
maxItems?: number;
|
|
43
|
+
maxSteps?: number;
|
|
44
|
+
labels?: string[];
|
|
45
|
+
reviewMode?: 'manual' | 'auto' | 'merge';
|
|
46
|
+
dryRun?: boolean;
|
|
34
47
|
}
|
|
35
48
|
/**
|
|
36
49
|
* Props for the run/monitor screen when launched directly from CLI
|
|
@@ -57,6 +70,8 @@ export interface AppProps {
|
|
|
57
70
|
interviewProps?: InterviewAppProps;
|
|
58
71
|
/** Props for the run/monitor screen (required when screen is 'run') */
|
|
59
72
|
runProps?: RunAppProps;
|
|
73
|
+
/** Props for the agent screen (required when screen is 'agent') */
|
|
74
|
+
agentProps?: AgentAppProps;
|
|
60
75
|
/** Called when the screen completes successfully */
|
|
61
76
|
onComplete?: (result: string) => void;
|
|
62
77
|
/** Called when the user exits/cancels */
|
|
@@ -69,7 +84,7 @@ export interface AppProps {
|
|
|
69
84
|
* and receives a shared headerElement prop.
|
|
70
85
|
*/
|
|
71
86
|
export declare function App({ screen: initialScreen, initialSessionState, version, // Fallback if package.json read fails (keep in sync with index.ts)
|
|
72
|
-
interviewProps, runProps, onComplete, onExit, }: AppProps): React.ReactElement | null;
|
|
87
|
+
interviewProps, runProps, agentProps, onComplete, onExit, }: AppProps): React.ReactElement | null;
|
|
73
88
|
/**
|
|
74
89
|
* Render options for renderApp
|
|
75
90
|
*/
|
|
@@ -84,6 +99,8 @@ export interface RenderAppOptions {
|
|
|
84
99
|
interviewProps?: InterviewAppProps;
|
|
85
100
|
/** Props for run/monitor screen (if starting directly on run screen) */
|
|
86
101
|
runProps?: RunAppProps;
|
|
102
|
+
/** Props for agent screen (if starting directly on agent screen) */
|
|
103
|
+
agentProps?: AgentAppProps;
|
|
87
104
|
/** Called when spec generation completes */
|
|
88
105
|
onComplete?: (result: string) => void;
|
|
89
106
|
/** Called when user exits */
|
package/dist/tui/app.js
CHANGED
|
@@ -19,6 +19,7 @@ import { logger } from '../utils/logger.js';
|
|
|
19
19
|
import { InterviewScreen } from './screens/InterviewScreen.js';
|
|
20
20
|
import { InitScreen } from './screens/InitScreen.js';
|
|
21
21
|
import { RunScreen } from './screens/RunScreen.js';
|
|
22
|
+
import { AgentScreen } from './screens/AgentScreen.js';
|
|
22
23
|
import { MainShell } from './screens/MainShell.js';
|
|
23
24
|
import { HeaderContent } from './components/HeaderContent.js';
|
|
24
25
|
import { useBackgroundRuns } from './hooks/useBackgroundRuns.js';
|
|
@@ -29,7 +30,7 @@ import { useBackgroundRuns } from './hooks/useBackgroundRuns.js';
|
|
|
29
30
|
* and receives a shared headerElement prop.
|
|
30
31
|
*/
|
|
31
32
|
export function App({ screen: initialScreen, initialSessionState, version = '0.12.1', // Fallback if package.json read fails (keep in sync with index.ts)
|
|
32
|
-
interviewProps, runProps, onComplete, onExit, }) {
|
|
33
|
+
interviewProps, runProps, agentProps, onComplete, onExit, }) {
|
|
33
34
|
const [currentScreen, setCurrentScreen] = useState(initialScreen);
|
|
34
35
|
const [screenProps, setScreenProps] = useState(() => {
|
|
35
36
|
if (initialScreen === 'run' && runProps) {
|
|
@@ -184,7 +185,7 @@ interviewProps, runProps, onComplete, onExit, }) {
|
|
|
184
185
|
navigate('shell', { message: 'Feature name is required for the run screen.' });
|
|
185
186
|
}
|
|
186
187
|
}
|
|
187
|
-
else if (currentScreen !== 'shell' && currentScreen !== 'init') {
|
|
188
|
+
else if (currentScreen !== 'shell' && currentScreen !== 'init' && currentScreen !== 'agent') {
|
|
188
189
|
// Unknown screen — redirect to shell on next tick
|
|
189
190
|
navigate('shell', { message: `Internal error: unknown screen "${currentScreen}". Returned to shell.` });
|
|
190
191
|
}
|
|
@@ -198,7 +199,8 @@ interviewProps, runProps, onComplete, onExit, }) {
|
|
|
198
199
|
if (!featureName || typeof featureName !== 'string' || !sessionState.provider) {
|
|
199
200
|
return null; // useEffect will redirect to shell
|
|
200
201
|
}
|
|
201
|
-
return (_jsx(InterviewScreen, { header: headerElement, featureName: featureName, projectRoot: sessionState.projectRoot, provider: sessionState.provider, model: sessionState.model, scanResult: sessionState.scanResult, specsPath: sessionState.config?.paths.specs,
|
|
202
|
+
return (_jsx(InterviewScreen, { header: headerElement, featureName: featureName, projectRoot: sessionState.projectRoot, provider: sessionState.provider, model: sessionState.model, scanResult: sessionState.scanResult, specsPath: sessionState.config?.paths.specs, initialReferences: screenProps?.initialReferences
|
|
203
|
+
?? interviewProps?.initialReferences, onComplete: handleInterviewComplete, onCancel: handleInterviewCancel }));
|
|
202
204
|
}
|
|
203
205
|
case 'init':
|
|
204
206
|
return (_jsx(InitScreen, { header: headerElement, projectRoot: sessionState.projectRoot, sessionState: sessionState, onComplete: handleInitComplete, onCancel: () => navigate('shell') }));
|
|
@@ -211,6 +213,23 @@ interviewProps, runProps, onComplete, onExit, }) {
|
|
|
211
213
|
const reviewMode = screenProps?.reviewMode;
|
|
212
214
|
return (_jsx(RunScreen, { header: headerElement, featureName: featureName, projectRoot: sessionState.projectRoot, sessionState: sessionState, monitorOnly: monitorOnly, reviewMode: reviewMode, onComplete: handleRunComplete, onBackground: handleRunBackground, onCancel: () => navigate('shell') }));
|
|
213
215
|
}
|
|
216
|
+
case 'agent': {
|
|
217
|
+
// Merge CLI-provided agentProps with navigation screenProps (from /agent command)
|
|
218
|
+
const resolvedAgentOptions = {
|
|
219
|
+
...agentProps,
|
|
220
|
+
...(screenProps?.dryRun != null ? { dryRun: screenProps.dryRun } : {}),
|
|
221
|
+
...(screenProps?.maxItems != null ? { maxItems: screenProps.maxItems } : {}),
|
|
222
|
+
...(screenProps?.reviewMode != null ? { reviewMode: screenProps.reviewMode } : {}),
|
|
223
|
+
};
|
|
224
|
+
return (_jsx(AgentScreen, { header: headerElement, projectRoot: sessionState.projectRoot, agentOptions: resolvedAgentOptions, onExit: () => {
|
|
225
|
+
if (initialScreen === 'agent') {
|
|
226
|
+
onExit?.();
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
navigate('shell');
|
|
230
|
+
}
|
|
231
|
+
} }));
|
|
232
|
+
}
|
|
214
233
|
default: {
|
|
215
234
|
// Return fallback UI instead of calling navigate() during render (which would be setState during render).
|
|
216
235
|
// The useEffect guard above will redirect to shell on next tick.
|
|
@@ -224,5 +243,5 @@ interviewProps, runProps, onComplete, onExit, }) {
|
|
|
224
243
|
* Render the App component to the terminal
|
|
225
244
|
*/
|
|
226
245
|
export function renderApp(options) {
|
|
227
|
-
return render(_jsx(App, { screen: options.screen, initialSessionState: options.initialSessionState, version: options.version, interviewProps: options.interviewProps, runProps: options.runProps, onComplete: options.onComplete, onExit: options.onExit }));
|
|
246
|
+
return render(_jsx(App, { screen: options.screen, initialSessionState: options.initialSessionState, version: options.version, interviewProps: options.interviewProps, runProps: options.runProps, agentProps: options.agentProps, onComplete: options.onComplete, onExit: options.onExit }));
|
|
228
247
|
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IssuePicker - GitHub issue selection dropdown
|
|
3
|
+
*
|
|
4
|
+
* Displays a bordered list of GitHub issues for the user to navigate
|
|
5
|
+
* and select from. Follows the visual style of CommandDropdown.
|
|
6
|
+
* Supports arrow keys and j/k for navigation, Enter to select, Esc to cancel.
|
|
7
|
+
*/
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import type { GitHubIssueListItem } from '../../utils/github.js';
|
|
10
|
+
/**
|
|
11
|
+
* Props for the IssuePicker component
|
|
12
|
+
*/
|
|
13
|
+
export interface IssuePickerProps {
|
|
14
|
+
/** List of issues to display */
|
|
15
|
+
issues: GitHubIssueListItem[];
|
|
16
|
+
/** Repository slug (e.g. "owner/repo") shown in header */
|
|
17
|
+
repoSlug: string;
|
|
18
|
+
/** Called when an issue is selected */
|
|
19
|
+
onSelect: (issue: GitHubIssueListItem) => void;
|
|
20
|
+
/** Called when the picker is dismissed (Escape) */
|
|
21
|
+
onCancel: () => void;
|
|
22
|
+
/** Whether issues are currently loading */
|
|
23
|
+
isLoading: boolean;
|
|
24
|
+
/** Optional error message to display */
|
|
25
|
+
error?: string;
|
|
26
|
+
}
|
|
27
|
+
export declare function IssuePicker({ issues, repoSlug, onSelect, onCancel, isLoading, error, }: IssuePickerProps): React.ReactElement;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* IssuePicker - GitHub issue selection dropdown
|
|
4
|
+
*
|
|
5
|
+
* Displays a bordered list of GitHub issues for the user to navigate
|
|
6
|
+
* and select from. Follows the visual style of CommandDropdown.
|
|
7
|
+
* Supports arrow keys and j/k for navigation, Enter to select, Esc to cancel.
|
|
8
|
+
*/
|
|
9
|
+
import React, { useState } from 'react';
|
|
10
|
+
import { Box, Text, useInput, useStdout } from 'ink';
|
|
11
|
+
import Spinner from 'ink-spinner';
|
|
12
|
+
import { colors, box } from '../theme.js';
|
|
13
|
+
const MAX_LABELS = 2;
|
|
14
|
+
function truncate(text, maxLen) {
|
|
15
|
+
if (text.length <= maxLen)
|
|
16
|
+
return text;
|
|
17
|
+
return text.slice(0, maxLen - 1) + '\u2026';
|
|
18
|
+
}
|
|
19
|
+
function pad(text, width) {
|
|
20
|
+
if (text.length >= width)
|
|
21
|
+
return text.slice(0, width);
|
|
22
|
+
return text + ' '.repeat(width - text.length);
|
|
23
|
+
}
|
|
24
|
+
// Fixed column widths
|
|
25
|
+
const COL_NUMBER = 6; // " #104 "
|
|
26
|
+
const COL_STATE = 8; // " closed " or " open "
|
|
27
|
+
const COL_LABELS = 14; // " enhancement "
|
|
28
|
+
const COL_CHROME = 2; // left + right border
|
|
29
|
+
export function IssuePicker({ issues, repoSlug, onSelect, onCancel, isLoading, error, }) {
|
|
30
|
+
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
31
|
+
const { stdout } = useStdout();
|
|
32
|
+
const termColumns = stdout?.columns ?? 80;
|
|
33
|
+
React.useEffect(() => {
|
|
34
|
+
setSelectedIndex(0);
|
|
35
|
+
}, [issues]);
|
|
36
|
+
useInput((input, key) => {
|
|
37
|
+
if (key.escape) {
|
|
38
|
+
onCancel();
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (key.return && issues.length > 0) {
|
|
42
|
+
onSelect(issues[selectedIndex]);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (key.upArrow || input === 'k') {
|
|
46
|
+
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (key.downArrow || input === 'j') {
|
|
50
|
+
setSelectedIndex((prev) => Math.min(issues.length - 1, prev + 1));
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
const countSuffix = !isLoading && !error ? ` (${issues.length})` : '';
|
|
55
|
+
const headerLabel = ` GitHub Issues ${box.horizontal} ${repoSlug}${countSuffix} `;
|
|
56
|
+
const contentWidth = Math.min(Math.max(60, headerLabel.length + 10), termColumns - 6);
|
|
57
|
+
const topBorderFill = contentWidth - headerLabel.length - 1;
|
|
58
|
+
const topBorder = box.topLeft + box.horizontal + headerLabel + box.horizontal.repeat(Math.max(0, topBorderFill)) + box.topRight;
|
|
59
|
+
const bottomBorder = box.bottomLeft + box.horizontal.repeat(contentWidth) + box.bottomRight;
|
|
60
|
+
// Title column gets all remaining space
|
|
61
|
+
const colTitle = Math.max(20, contentWidth - COL_NUMBER - COL_STATE - COL_LABELS - COL_CHROME);
|
|
62
|
+
return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: [_jsx(Text, { dimColor: true, children: topBorder }), isLoading && (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { dimColor: true, children: box.vertical }), _jsx(Text, { children: " " }), _jsx(Spinner, { type: "dots" }), _jsx(Text, { color: colors.yellow, children: " Searching..." }), _jsx(Text, { children: ' '.repeat(Math.max(0, contentWidth - 16)) }), _jsx(Text, { dimColor: true, children: box.vertical })] })), !isLoading && error && (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { dimColor: true, children: box.vertical }), _jsx(Text, { color: colors.pink, children: pad(` ${error}`, contentWidth) }), _jsx(Text, { dimColor: true, children: box.vertical })] })), !isLoading && !error && issues.length === 0 && (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { dimColor: true, children: box.vertical }), _jsx(Text, { dimColor: true, children: pad(' No open issues found', contentWidth) }), _jsx(Text, { dimColor: true, children: box.vertical })] })), !isLoading && !error && issues.map((issue, index) => {
|
|
63
|
+
const isSelected = index === selectedIndex;
|
|
64
|
+
const bg = isSelected ? colors.yellow : undefined;
|
|
65
|
+
const fg = isSelected ? colors.brown : undefined;
|
|
66
|
+
const numberCell = pad(` #${issue.number}`, COL_NUMBER);
|
|
67
|
+
const titleCell = pad(` ${truncate(issue.title, colTitle - 1)}`, colTitle);
|
|
68
|
+
const stateCell = pad(` ${issue.state}`, COL_STATE);
|
|
69
|
+
const labelText = issue.labels.slice(0, MAX_LABELS).join(' ');
|
|
70
|
+
const labelCell = pad(` ${truncate(labelText, COL_LABELS - 1)}`, COL_LABELS);
|
|
71
|
+
return (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { dimColor: true, children: box.vertical }), _jsx(Text, { backgroundColor: bg, color: isSelected ? colors.brown : colors.yellow, children: numberCell }), _jsx(Text, { backgroundColor: bg, color: fg, children: titleCell }), _jsx(Text, { backgroundColor: bg, color: isSelected ? colors.brown : issue.state === 'open' ? colors.green : colors.gray, children: stateCell }), _jsx(Text, { backgroundColor: bg, color: isSelected ? colors.brown : colors.gray, children: labelCell }), _jsx(Text, { dimColor: true, children: box.vertical })] }, issue.number));
|
|
72
|
+
}), _jsx(Text, { dimColor: true, children: bottomBorder }), _jsx(Box, { marginLeft: 1, children: _jsxs(Text, { color: colors.gray, children: ["(", '\u2191\u2193 navigate, Enter select, Esc cancel', ")"] }) })] }));
|
|
73
|
+
}
|
|
@@ -116,10 +116,12 @@ export function RunCompletionSummary({ summary, }) {
|
|
|
116
116
|
return (_jsxs(SummaryBox, { minWidth: 60, children: [_jsx(Text, { bold: true, children: summary.feature }), _jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsxs(Text, { bold: true, color: exitStatus.color, children: [statusIcon, " ", exitStatus.label] }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { dimColor: true, children: subtitleParts.join(' · ') })] }), _jsxs(SummaryBoxSection, { children: [_jsx(Text, { bold: true, children: "Phases" }), summary.phases && summary.phases.length > 0 ? (summary.phases.map((phaseInfo) => {
|
|
117
117
|
const phaseIcon = phaseInfo.status === 'success' ? phase.complete :
|
|
118
118
|
phaseInfo.status === 'failed' ? phase.error :
|
|
119
|
-
phase.
|
|
119
|
+
phaseInfo.status === 'started' ? phase.active :
|
|
120
|
+
phase.pending;
|
|
120
121
|
const phaseColor = phaseInfo.status === 'success' ? colors.green :
|
|
121
122
|
phaseInfo.status === 'failed' ? colors.pink :
|
|
122
|
-
colors.
|
|
123
|
+
phaseInfo.status === 'started' ? colors.yellow :
|
|
124
|
+
colors.gray;
|
|
123
125
|
const durationText = phaseInfo.durationMs !== undefined
|
|
124
126
|
? formatDurationMs(phaseInfo.durationMs)
|
|
125
127
|
: 'Not available';
|
|
@@ -127,7 +129,8 @@ export function RunCompletionSummary({ summary, }) {
|
|
|
127
129
|
? ` (${phaseInfo.iterations} iterations)`
|
|
128
130
|
: '';
|
|
129
131
|
const statusText = phaseInfo.status === 'skipped' ? ' skipped' :
|
|
130
|
-
phaseInfo.status === 'failed' ? ' failed' :
|
|
132
|
+
phaseInfo.status === 'failed' ? ' failed' :
|
|
133
|
+
phaseInfo.status === 'started' ? ' in progress' : '';
|
|
131
134
|
return (_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: phaseColor, children: [phaseIcon, " "] }), _jsxs(Text, { children: [phaseInfo.label, " ", durationText, iterationsText, statusText] })] }, phaseInfo.id));
|
|
132
135
|
})) : (_jsx(Text, { children: "No phase information available" }))] }), _jsxs(SummaryBoxSection, { children: [_jsx(Text, { bold: true, children: "Changes" }), !summary.changes || !summary.changes.available ? (_jsx(Text, { children: "Changes: Not available" })) : summary.changes.totalFilesChanged === 0 || (summary.changes.files && summary.changes.files.length === 0) ? (_jsx(Text, { children: "No changes" })) : summary.changes.totalFilesChanged !== undefined ? (_jsxs(Text, { children: [summary.changes.totalFilesChanged, " file", summary.changes.totalFilesChanged !== 1 ? 's' : '', " changed"] })) : !summary.changes.files ? (_jsx(Text, { children: "Changes: Could not compute diff" })) : null, (() => {
|
|
133
136
|
const files = summary.changes?.available ? summary.changes.files : undefined;
|
|
@@ -15,6 +15,8 @@ export interface StatusLineProps {
|
|
|
15
15
|
phase?: string;
|
|
16
16
|
/** Path or context info (e.g., working directory or feature name) */
|
|
17
17
|
path?: string;
|
|
18
|
+
/** Extra trailing segment (e.g., "review: auto") */
|
|
19
|
+
extra?: string;
|
|
18
20
|
}
|
|
19
21
|
/**
|
|
20
22
|
* StatusLine component
|
|
@@ -32,4 +34,4 @@ export interface StatusLineProps {
|
|
|
32
34
|
* // Renders: Initialize Project │ Analysis (4/5) │ /Users/name/project
|
|
33
35
|
* ```
|
|
34
36
|
*/
|
|
35
|
-
export declare function StatusLine({ action, phase, path, }: StatusLineProps): React.ReactElement;
|
|
37
|
+
export declare function StatusLine({ action, phase, path, extra, }: StatusLineProps): React.ReactElement;
|
|
@@ -17,11 +17,11 @@ import { theme, colors } from '../theme.js';
|
|
|
17
17
|
* // Renders: Initialize Project │ Analysis (4/5) │ /Users/name/project
|
|
18
18
|
* ```
|
|
19
19
|
*/
|
|
20
|
-
export function StatusLine({ action, phase, path, }) {
|
|
20
|
+
export function StatusLine({ action, phase, path, extra, }) {
|
|
21
21
|
const separator = theme.statusLine.separator;
|
|
22
22
|
// Truncate path if too long (keep last 40 chars)
|
|
23
23
|
const displayPath = path && path.length > 40
|
|
24
24
|
? '...' + path.slice(-37)
|
|
25
25
|
: path;
|
|
26
|
-
return (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: colors.yellow, bold: true, children: action }), phase && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: separator }), _jsx(Text, { children: phase })] })), displayPath && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: separator }), _jsx(Text, { dimColor: true, children: displayPath })] }))] }));
|
|
26
|
+
return (_jsxs(Box, { flexDirection: "row", children: [_jsx(Text, { color: colors.yellow, bold: true, children: action }), phase && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: separator }), _jsx(Text, { children: phase })] })), displayPath && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: separator }), _jsx(Text, { dimColor: true, children: displayPath })] })), extra && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: separator }), _jsx(Text, { dimColor: true, children: extra })] }))] }));
|
|
27
27
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useAgentOrchestrator — React hook that bridges the agent orchestrator
|
|
3
|
+
* lifecycle to TUI state via callbacks.
|
|
4
|
+
*
|
|
5
|
+
* Creates the orchestrator, runs it via stream(), and interprets tool
|
|
6
|
+
* calls into structured state (active issue, queue, completed, log).
|
|
7
|
+
* Exposes an abort() function for clean shutdown on q/Esc.
|
|
8
|
+
*/
|
|
9
|
+
import type { AgentIssueState, AgentLogEntry, ReviewMode } from '../../agent/types.js';
|
|
10
|
+
import { type LoopStatus, type TaskCounts, type ActivityEvent } from '../utils/loop-status.js';
|
|
11
|
+
import { type CommitLogEntry } from '../utils/git-summary.js';
|
|
12
|
+
export type AgentStatus = 'idle' | 'running' | 'complete' | 'error';
|
|
13
|
+
export interface UseAgentOrchestratorOptions {
|
|
14
|
+
projectRoot: string;
|
|
15
|
+
modelOverride?: string;
|
|
16
|
+
maxItems?: number;
|
|
17
|
+
maxSteps?: number;
|
|
18
|
+
labels?: string[];
|
|
19
|
+
reviewMode?: ReviewMode;
|
|
20
|
+
dryRun?: boolean;
|
|
21
|
+
}
|
|
22
|
+
export interface LoopMonitorData {
|
|
23
|
+
loopStatus: LoopStatus;
|
|
24
|
+
tasks: TaskCounts;
|
|
25
|
+
branch: string;
|
|
26
|
+
recentCommits: CommitLogEntry[];
|
|
27
|
+
activityEvents: ActivityEvent[];
|
|
28
|
+
startTime: number;
|
|
29
|
+
}
|
|
30
|
+
export interface UseAgentOrchestratorResult {
|
|
31
|
+
status: AgentStatus;
|
|
32
|
+
activeIssue: AgentIssueState | null;
|
|
33
|
+
queue: AgentIssueState[];
|
|
34
|
+
completed: AgentIssueState[];
|
|
35
|
+
logEntries: AgentLogEntry[];
|
|
36
|
+
loopMonitor: LoopMonitorData | null;
|
|
37
|
+
error: string | null;
|
|
38
|
+
abort: () => void;
|
|
39
|
+
}
|
|
40
|
+
export declare function useAgentOrchestrator(options: UseAgentOrchestratorOptions): UseAgentOrchestratorResult;
|