@hatchway/cli 0.50.68 → 0.50.70

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.
@@ -0,0 +1,10 @@
1
+ // Hatchway CLI - Built with Rollup
2
+ import { r as reactExports, A as AppContext } from './theme-CzLXk_6s.js';
3
+
4
+ /**
5
+ `useApp` is a React hook that exposes a method to manually exit the app (unmount).
6
+ */
7
+ const useApp = () => reactExports.useContext(AppContext);
8
+
9
+ export { useApp as u };
10
+ //# sourceMappingURL=use-app-Dw8M2HNg.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-app-Dw8M2HNg.js","sources":["../../../../node_modules/.pnpm/ink@6.5.1_@types+react@19.2.7_react@19.2.7/node_modules/ink/build/hooks/use-app.js"],"sourcesContent":["import { useContext } from 'react';\nimport AppContext from '../components/AppContext.js';\n/**\n`useApp` is a React hook that exposes a method to manually exit the app (unmount).\n*/\nconst useApp = () => useContext(AppContext);\nexport default useApp;\n//# sourceMappingURL=use-app.js.map"],"names":["useContext"],"mappings":";;;AAEA;AACA;AACA;AACK,MAAC,MAAM,GAAG,MAAMA,uBAAU,CAAC,UAAU;;;;","x_google_ignoreList":[0]}
@@ -0,0 +1,330 @@
1
+ // Hatchway CLI - Built with Rollup
2
+ import { r as reactExports, j as jsxRuntimeExports, B as Box, c as colors, T as Text, s as symbols } from './theme-CzLXk_6s.js';
3
+ import 'node:stream';
4
+ import 'node:process';
5
+ import 'chalk';
6
+ import 'node:events';
7
+ import { b as getLogger, g as getLogBuffer } from './runner-logger-instance-Dj_JMznn.js';
8
+
9
+ function BuildPanel({ build, width, height }) {
10
+ const [elapsedTime, setElapsedTime] = reactExports.useState(0);
11
+ // Update elapsed time every second
12
+ reactExports.useEffect(() => {
13
+ if (!build || build.status !== 'running') {
14
+ setElapsedTime(0);
15
+ return;
16
+ }
17
+ const startTime = build.startTime;
18
+ const updateElapsed = () => {
19
+ const now = Date.now();
20
+ setElapsedTime(Math.floor((now - startTime) / 1000));
21
+ };
22
+ updateElapsed();
23
+ const interval = setInterval(updateElapsed, 1000);
24
+ return () => clearInterval(interval);
25
+ }, [build?.id, build?.status, build?.startTime]);
26
+ // Don't render if no build
27
+ if (!build) {
28
+ return null;
29
+ }
30
+ const truncate = (str, maxLen) => {
31
+ if (str.length <= maxLen)
32
+ return str;
33
+ return str.substring(0, maxLen - 3) + '...';
34
+ };
35
+ const formatDuration = (seconds) => {
36
+ if (seconds < 60)
37
+ return `${seconds}s`;
38
+ const mins = Math.floor(seconds / 60);
39
+ const secs = seconds % 60;
40
+ return `${mins}m ${secs}s`;
41
+ };
42
+ // Determine elapsed time to show
43
+ const displayElapsed = build.status === 'running'
44
+ ? elapsedTime
45
+ : build.endTime
46
+ ? Math.floor((build.endTime - build.startTime) / 1000)
47
+ : 0;
48
+ return (jsxRuntimeExports.jsxs(Box, { flexDirection: "column", width: width, height: height, borderStyle: "single", borderColor: colors.darkGray, paddingX: 1, children: [jsxRuntimeExports.jsx(Box, { marginBottom: 1, children: jsxRuntimeExports.jsx(Text, { color: colors.cyan, bold: true, children: "BUILD" }) }), jsxRuntimeExports.jsx(Box, { children: jsxRuntimeExports.jsx(Text, { color: colors.white, children: truncate(build.projectSlug, width - 4) }) }), build.template && (jsxRuntimeExports.jsx(Box, { children: jsxRuntimeExports.jsx(Text, { color: colors.gray, children: truncate(build.template, width - 4) }) })), jsxRuntimeExports.jsx(Box, { children: jsxRuntimeExports.jsx(Text, { color: colors.gray, children: build.agent === 'claude-code' ? build.model : build.agent }) }), jsxRuntimeExports.jsxs(Box, { marginTop: 1, children: [build.status === 'running' && (jsxRuntimeExports.jsxs(Text, { color: colors.cyan, children: [symbols.spinnerFrames[Math.floor(Date.now() / 120) % symbols.spinnerFrames.length], " ", formatDuration(displayElapsed)] })), build.status === 'completed' && (jsxRuntimeExports.jsxs(Text, { color: colors.success, children: [symbols.check, " ", formatDuration(displayElapsed)] })), build.status === 'failed' && (jsxRuntimeExports.jsxs(Text, { color: colors.error, children: [symbols.cross, " ", formatDuration(displayElapsed)] })), build.status === 'pending' && (jsxRuntimeExports.jsx(Text, { color: colors.gray, children: "Pending..." }))] }), build.todos.length > 0 && (jsxRuntimeExports.jsxs(Box, { flexDirection: "column", marginTop: 1, children: [jsxRuntimeExports.jsx(Text, { color: colors.dimGray, bold: true, children: "TASKS" }), jsxRuntimeExports.jsx(TodoList, { todos: build.todos, maxWidth: width - 6, maxVisible: 10 })] })), build.status === 'failed' && build.error && (jsxRuntimeExports.jsx(Box, { marginTop: 1, children: jsxRuntimeExports.jsx(Text, { color: colors.error, wrap: "truncate", children: truncate(build.error, width - 4) }) }))] }));
49
+ }
50
+ /**
51
+ * TodoList - Smart display of todo items
52
+ * - Shows up to maxVisible items (default 10)
53
+ * - Prioritizes in_progress and pending tasks
54
+ * - Hides completed tasks when space is needed
55
+ * - Shows count of hidden completed tasks
56
+ */
57
+ function TodoList({ todos, maxWidth, maxVisible = 10 }) {
58
+ // Separate todos by status
59
+ const inProgress = todos.filter(t => t.status === 'in_progress');
60
+ const pending = todos.filter(t => t.status === 'pending');
61
+ const completed = todos.filter(t => t.status === 'completed');
62
+ const cancelled = todos.filter(t => t.status === 'cancelled');
63
+ // Calculate how many slots we have for each category
64
+ // Priority: in_progress > pending > completed > cancelled
65
+ inProgress.length + pending.length;
66
+ let visibleTodos = [];
67
+ let hiddenCompletedCount = 0;
68
+ if (todos.length <= maxVisible) {
69
+ // All todos fit - show them in order
70
+ visibleTodos = todos;
71
+ }
72
+ else {
73
+ // Need to prioritize - always show in_progress and pending first
74
+ visibleTodos = [...inProgress, ...pending];
75
+ // Calculate remaining slots for completed tasks
76
+ const remainingSlots = maxVisible - visibleTodos.length;
77
+ if (remainingSlots > 0) {
78
+ // Show as many completed as we can fit
79
+ visibleTodos = [...visibleTodos, ...completed.slice(0, remainingSlots)];
80
+ hiddenCompletedCount = Math.max(0, completed.length - remainingSlots);
81
+ }
82
+ else {
83
+ hiddenCompletedCount = completed.length;
84
+ }
85
+ // Add cancelled if there's still room
86
+ const slotsAfterCompleted = maxVisible - visibleTodos.length;
87
+ if (slotsAfterCompleted > 0) {
88
+ visibleTodos = [...visibleTodos, ...cancelled.slice(0, slotsAfterCompleted)];
89
+ }
90
+ }
91
+ // Sort visible todos to maintain logical order (by original index)
92
+ visibleTodos.sort((a, b) => {
93
+ const aIndex = todos.findIndex(t => t.id === a.id);
94
+ const bIndex = todos.findIndex(t => t.id === b.id);
95
+ return aIndex - bIndex;
96
+ });
97
+ return (jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [visibleTodos.map((todo) => (jsxRuntimeExports.jsx(TodoRow, { todo: todo, maxWidth: maxWidth }, todo.id))), hiddenCompletedCount > 0 && (jsxRuntimeExports.jsxs(Text, { color: colors.dimGray, children: ["\u2713 ", hiddenCompletedCount, " completed"] }))] }));
98
+ }
99
+ // Todo row component
100
+ function TodoRow({ todo, maxWidth }) {
101
+ const statusIcon = {
102
+ pending: jsxRuntimeExports.jsx(Text, { color: colors.dimGray, children: symbols.hollowDot }),
103
+ in_progress: jsxRuntimeExports.jsx(Text, { color: colors.cyan, children: symbols.spinnerFrames[Math.floor(Date.now() / 120) % symbols.spinnerFrames.length] }),
104
+ completed: jsxRuntimeExports.jsx(Text, { color: colors.success, children: symbols.check }),
105
+ cancelled: jsxRuntimeExports.jsx(Text, { color: colors.dimGray, children: "\u2298" }),
106
+ }[todo.status];
107
+ const textColor = {
108
+ pending: colors.dimGray,
109
+ in_progress: colors.white,
110
+ completed: colors.gray,
111
+ cancelled: colors.dimGray,
112
+ }[todo.status];
113
+ const truncatedContent = todo.content.length > maxWidth - 3
114
+ ? todo.content.substring(0, maxWidth - 6) + '...'
115
+ : todo.content;
116
+ return (jsxRuntimeExports.jsxs(Box, { children: [statusIcon, jsxRuntimeExports.jsxs(Text, { color: textColor, children: [" ", truncatedContent] })] }));
117
+ }
118
+
119
+ /**
120
+ * useBuildState - Hook to track build state and todos
121
+ *
122
+ * Manages:
123
+ * - Active builds list
124
+ * - Current build selection (for multi-build navigation)
125
+ * - Todo list updates
126
+ * - Build lifecycle events
127
+ *
128
+ * Subscribes to RunnerLogger events to automatically update state.
129
+ *
130
+ * IMPORTANT: Uses refs for event handlers to prevent race conditions
131
+ * where events could be missed during re-subscriptions caused by
132
+ * callback dependency changes.
133
+ */
134
+ function useBuildState() {
135
+ const [builds, setBuilds] = reactExports.useState([]);
136
+ const [currentBuildIndex, setCurrentBuildIndex] = reactExports.useState(0);
137
+ const [isConnected, setIsConnected] = reactExports.useState(false);
138
+ const [isVerbose, setIsVerbose] = reactExports.useState(false);
139
+ // Derive current build from index
140
+ const currentBuild = builds.length > 0 ? builds[currentBuildIndex] ?? null : null;
141
+ // Navigation between builds
142
+ const nextBuild = reactExports.useCallback(() => {
143
+ setCurrentBuildIndex(prev => {
144
+ if (builds.length === 0)
145
+ return 0;
146
+ return (prev + 1) % builds.length;
147
+ });
148
+ }, [builds.length]);
149
+ const prevBuild = reactExports.useCallback(() => {
150
+ setCurrentBuildIndex(prev => {
151
+ if (builds.length === 0)
152
+ return 0;
153
+ return prev === 0 ? builds.length - 1 : prev - 1;
154
+ });
155
+ }, [builds.length]);
156
+ // Verbose toggle - updates both UI state and logger
157
+ const toggleVerbose = reactExports.useCallback(() => {
158
+ setIsVerbose(prev => {
159
+ const newValue = !prev;
160
+ // Also update the logger's verbose flag
161
+ try {
162
+ const logger = getLogger();
163
+ logger.setVerbose(newValue);
164
+ }
165
+ catch {
166
+ // Logger not initialized yet
167
+ }
168
+ return newValue;
169
+ });
170
+ }, []);
171
+ // Track when a new build is added to auto-select it
172
+ const [pendingNewBuildId, setPendingNewBuildId] = reactExports.useState(null);
173
+ // Add a new build
174
+ const addBuild = reactExports.useCallback((build) => {
175
+ setBuilds(prev => {
176
+ // Check if build already exists
177
+ const exists = prev.some(b => b.id === build.id);
178
+ if (exists) {
179
+ // Update existing build
180
+ return prev.map(b => b.id === build.id ? { ...b, ...build } : b);
181
+ }
182
+ // Mark this as a new build to auto-select
183
+ setPendingNewBuildId(build.id);
184
+ // Add new build
185
+ return [...prev, build];
186
+ });
187
+ }, []);
188
+ // Auto-select newly added builds
189
+ reactExports.useEffect(() => {
190
+ if (pendingNewBuildId) {
191
+ const index = builds.findIndex(b => b.id === pendingNewBuildId);
192
+ if (index !== -1) {
193
+ setCurrentBuildIndex(index);
194
+ }
195
+ setPendingNewBuildId(null);
196
+ }
197
+ }, [builds, pendingNewBuildId]);
198
+ // Update an existing build
199
+ const updateBuild = reactExports.useCallback((buildId, updates) => {
200
+ setBuilds(prev => prev.map(build => build.id === buildId ? { ...build, ...updates } : build));
201
+ }, []);
202
+ // Update todos for a build
203
+ const updateTodos = reactExports.useCallback((buildId, todos) => {
204
+ setBuilds(prev => prev.map(build => build.id === buildId ? { ...build, todos } : build));
205
+ }, []);
206
+ // Connection status
207
+ const setConnected = reactExports.useCallback((connected) => {
208
+ setIsConnected(connected);
209
+ }, []);
210
+ // Use refs to store the latest callbacks so event handlers always call current versions
211
+ // This prevents race conditions where events could be missed during re-subscriptions
212
+ const addBuildRef = reactExports.useRef(addBuild);
213
+ const updateBuildRef = reactExports.useRef(updateBuild);
214
+ const updateTodosRef = reactExports.useRef(updateTodos);
215
+ // Keep refs up to date
216
+ reactExports.useEffect(() => {
217
+ addBuildRef.current = addBuild;
218
+ }, [addBuild]);
219
+ reactExports.useEffect(() => {
220
+ updateBuildRef.current = updateBuild;
221
+ }, [updateBuild]);
222
+ reactExports.useEffect(() => {
223
+ updateTodosRef.current = updateTodos;
224
+ }, [updateTodos]);
225
+ // Subscribe to RunnerLogger events - only runs ONCE on mount
226
+ // Uses refs for callbacks to avoid re-subscription race conditions
227
+ reactExports.useEffect(() => {
228
+ const logger = getLogger();
229
+ // Sync initial state from logger (in case events already fired)
230
+ setIsConnected(logger.isConnected());
231
+ setIsVerbose(logger.isVerbose());
232
+ // Load any existing builds
233
+ const existingBuilds = logger.getAllBuilds();
234
+ if (existingBuilds.length > 0) {
235
+ for (const build of existingBuilds) {
236
+ addBuildRef.current(build);
237
+ }
238
+ }
239
+ // Handle build start events - use ref to get latest callback
240
+ const handleBuildStart = (build) => {
241
+ addBuildRef.current(build);
242
+ };
243
+ // Handle build update events
244
+ const handleBuildUpdate = (build) => {
245
+ updateBuildRef.current(build.id, build);
246
+ };
247
+ // Handle build complete events
248
+ const handleBuildComplete = (build) => {
249
+ updateBuildRef.current(build.id, { ...build, status: build.status });
250
+ };
251
+ // Handle todo updates
252
+ const handleTodoUpdate = (buildId, todos) => {
253
+ updateTodosRef.current(buildId, todos);
254
+ };
255
+ // Handle connection events
256
+ const handleConnected = () => {
257
+ setIsConnected(true);
258
+ };
259
+ const handleDisconnected = () => {
260
+ setIsConnected(false);
261
+ };
262
+ // Handle verbose change
263
+ const handleVerboseChange = (verbose) => {
264
+ setIsVerbose(verbose);
265
+ };
266
+ // Subscribe to events
267
+ logger.on('buildStart', handleBuildStart);
268
+ logger.on('buildUpdate', handleBuildUpdate);
269
+ logger.on('buildComplete', handleBuildComplete);
270
+ logger.on('todoUpdate', handleTodoUpdate);
271
+ logger.on('connected', handleConnected);
272
+ logger.on('disconnected', handleDisconnected);
273
+ logger.on('verboseChange', handleVerboseChange);
274
+ // Cleanup - only runs on unmount
275
+ return () => {
276
+ logger.off('buildStart', handleBuildStart);
277
+ logger.off('buildUpdate', handleBuildUpdate);
278
+ logger.off('buildComplete', handleBuildComplete);
279
+ logger.off('todoUpdate', handleTodoUpdate);
280
+ logger.off('connected', handleConnected);
281
+ logger.off('disconnected', handleDisconnected);
282
+ logger.off('verboseChange', handleVerboseChange);
283
+ };
284
+ }, []); // Empty deps - only subscribe once on mount
285
+ const state = {
286
+ builds,
287
+ currentBuildIndex,
288
+ currentBuild,
289
+ isConnected,
290
+ isVerbose,
291
+ };
292
+ const actions = {
293
+ nextBuild,
294
+ prevBuild,
295
+ setVerbose: setIsVerbose,
296
+ toggleVerbose,
297
+ addBuild,
298
+ updateBuild,
299
+ updateTodos,
300
+ setConnected,
301
+ };
302
+ return [state, actions];
303
+ }
304
+ /**
305
+ * Hook to subscribe to log entries from the buffer
306
+ */
307
+ function useLogEntries(maxEntries = 100) {
308
+ const [entries, setEntries] = reactExports.useState([]);
309
+ reactExports.useEffect(() => {
310
+ const buffer = getLogBuffer();
311
+ // Initialize with current entries
312
+ setEntries(buffer.getRecent(maxEntries));
313
+ // Subscribe to new entries
314
+ const unsubscribe = buffer.onLog((entry) => {
315
+ setEntries(prev => {
316
+ const newEntries = [...prev, entry];
317
+ // Keep only the most recent entries
318
+ if (newEntries.length > maxEntries) {
319
+ return newEntries.slice(-maxEntries);
320
+ }
321
+ return newEntries;
322
+ });
323
+ });
324
+ return unsubscribe;
325
+ }, [maxEntries]);
326
+ return entries;
327
+ }
328
+
329
+ export { BuildPanel as B, useLogEntries as a, useBuildState as u };
330
+ //# sourceMappingURL=useBuildState-c-9oir2y.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useBuildState-c-9oir2y.js","sources":["../../src/cli/tui/components/BuildPanel.tsx","../../src/cli/tui/hooks/useBuildState.ts"],"sourcesContent":["/**\n * BuildPanel - Left panel showing current build info and todo list\n * Takes up 20% of width, shows:\n * - Project name\n * - Template name\n * - Agent/model\n * - Elapsed time\n * - Todo list with status indicators\n */\n\nimport React, { useState, useEffect } from 'react';\nimport { Box, Text } from 'ink';\nimport { colors, symbols } from '../theme.js';\nimport type { BuildInfo, TodoItem } from '../../../lib/logging/types.js';\n\ninterface BuildPanelProps {\n build: BuildInfo | null;\n width: number;\n height?: number;\n}\n\nexport function BuildPanel({ build, width, height }: BuildPanelProps) {\n const [elapsedTime, setElapsedTime] = useState(0);\n\n // Update elapsed time every second\n useEffect(() => {\n if (!build || build.status !== 'running') {\n setElapsedTime(0);\n return;\n }\n\n const startTime = build.startTime;\n const updateElapsed = () => {\n const now = Date.now();\n setElapsedTime(Math.floor((now - startTime) / 1000));\n };\n\n updateElapsed();\n const interval = setInterval(updateElapsed, 1000);\n return () => clearInterval(interval);\n }, [build?.id, build?.status, build?.startTime]);\n\n // Don't render if no build\n if (!build) {\n return null;\n }\n\n const truncate = (str: string, maxLen: number) => {\n if (str.length <= maxLen) return str;\n return str.substring(0, maxLen - 3) + '...';\n };\n\n const formatDuration = (seconds: number): string => {\n if (seconds < 60) return `${seconds}s`;\n const mins = Math.floor(seconds / 60);\n const secs = seconds % 60;\n return `${mins}m ${secs}s`;\n };\n\n // Determine elapsed time to show\n const displayElapsed = build.status === 'running' \n ? elapsedTime \n : build.endTime \n ? Math.floor((build.endTime - build.startTime) / 1000)\n : 0;\n\n return (\n <Box\n flexDirection=\"column\"\n width={width}\n height={height}\n borderStyle=\"single\"\n borderColor={colors.darkGray}\n paddingX={1}\n >\n {/* Header */}\n <Box marginBottom={1}>\n <Text color={colors.cyan} bold>BUILD</Text>\n </Box>\n\n {/* Project name */}\n <Box>\n <Text color={colors.white}>{truncate(build.projectSlug, width - 4)}</Text>\n </Box>\n\n {/* Template */}\n {build.template && (\n <Box>\n <Text color={colors.gray}>{truncate(build.template, width - 4)}</Text>\n </Box>\n )}\n\n {/* Agent/model */}\n <Box>\n <Text color={colors.gray}>\n {build.agent === 'claude-code' ? build.model : build.agent}\n </Text>\n </Box>\n\n {/* Elapsed time with status */}\n <Box marginTop={1}>\n {build.status === 'running' && (\n <Text color={colors.cyan}>\n {symbols.spinnerFrames[Math.floor(Date.now() / 120) % symbols.spinnerFrames.length]} {formatDuration(displayElapsed)}\n </Text>\n )}\n {build.status === 'completed' && (\n <Text color={colors.success}>\n {symbols.check} {formatDuration(displayElapsed)}\n </Text>\n )}\n {build.status === 'failed' && (\n <Text color={colors.error}>\n {symbols.cross} {formatDuration(displayElapsed)}\n </Text>\n )}\n {build.status === 'pending' && (\n <Text color={colors.gray}>Pending...</Text>\n )}\n </Box>\n\n {/* Todo list */}\n {build.todos.length > 0 && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text color={colors.dimGray} bold>TASKS</Text>\n <TodoList todos={build.todos} maxWidth={width - 6} maxVisible={10} />\n </Box>\n )}\n\n {/* Error message if failed */}\n {build.status === 'failed' && build.error && (\n <Box marginTop={1}>\n <Text color={colors.error} wrap=\"truncate\">\n {truncate(build.error, width - 4)}\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n\n/**\n * TodoList - Smart display of todo items\n * - Shows up to maxVisible items (default 10)\n * - Prioritizes in_progress and pending tasks\n * - Hides completed tasks when space is needed\n * - Shows count of hidden completed tasks\n */\nfunction TodoList({ todos, maxWidth, maxVisible = 10 }: { \n todos: TodoItem[]; \n maxWidth: number;\n maxVisible?: number;\n}) {\n // Separate todos by status\n const inProgress = todos.filter(t => t.status === 'in_progress');\n const pending = todos.filter(t => t.status === 'pending');\n const completed = todos.filter(t => t.status === 'completed');\n const cancelled = todos.filter(t => t.status === 'cancelled');\n \n // Calculate how many slots we have for each category\n // Priority: in_progress > pending > completed > cancelled\n const activeCount = inProgress.length + pending.length;\n \n let visibleTodos: TodoItem[] = [];\n let hiddenCompletedCount = 0;\n \n if (todos.length <= maxVisible) {\n // All todos fit - show them in order\n visibleTodos = todos;\n } else {\n // Need to prioritize - always show in_progress and pending first\n visibleTodos = [...inProgress, ...pending];\n \n // Calculate remaining slots for completed tasks\n const remainingSlots = maxVisible - visibleTodos.length;\n \n if (remainingSlots > 0) {\n // Show as many completed as we can fit\n visibleTodos = [...visibleTodos, ...completed.slice(0, remainingSlots)];\n hiddenCompletedCount = Math.max(0, completed.length - remainingSlots);\n } else {\n hiddenCompletedCount = completed.length;\n }\n \n // Add cancelled if there's still room\n const slotsAfterCompleted = maxVisible - visibleTodos.length;\n if (slotsAfterCompleted > 0) {\n visibleTodos = [...visibleTodos, ...cancelled.slice(0, slotsAfterCompleted)];\n }\n }\n \n // Sort visible todos to maintain logical order (by original index)\n visibleTodos.sort((a, b) => {\n const aIndex = todos.findIndex(t => t.id === a.id);\n const bIndex = todos.findIndex(t => t.id === b.id);\n return aIndex - bIndex;\n });\n \n return (\n <>\n {visibleTodos.map((todo) => (\n <TodoRow key={todo.id} todo={todo} maxWidth={maxWidth} />\n ))}\n {hiddenCompletedCount > 0 && (\n <Text color={colors.dimGray}>✓ {hiddenCompletedCount} completed</Text>\n )}\n </>\n );\n}\n\n// Todo row component\nfunction TodoRow({ todo, maxWidth }: { todo: TodoItem; maxWidth: number }) {\n const statusIcon = {\n pending: <Text color={colors.dimGray}>{symbols.hollowDot}</Text>,\n in_progress: <Text color={colors.cyan}>{symbols.spinnerFrames[Math.floor(Date.now() / 120) % symbols.spinnerFrames.length]}</Text>,\n completed: <Text color={colors.success}>{symbols.check}</Text>,\n cancelled: <Text color={colors.dimGray}>⊘</Text>,\n }[todo.status];\n\n const textColor = {\n pending: colors.dimGray,\n in_progress: colors.white,\n completed: colors.gray,\n cancelled: colors.dimGray,\n }[todo.status];\n\n const truncatedContent = todo.content.length > maxWidth - 3\n ? todo.content.substring(0, maxWidth - 6) + '...'\n : todo.content;\n\n return (\n <Box>\n {statusIcon}\n <Text color={textColor}> {truncatedContent}</Text>\n </Box>\n );\n}\n","/**\n * useBuildState - Hook to track build state and todos\n * \n * Manages:\n * - Active builds list\n * - Current build selection (for multi-build navigation)\n * - Todo list updates\n * - Build lifecycle events\n * \n * Subscribes to RunnerLogger events to automatically update state.\n * \n * IMPORTANT: Uses refs for event handlers to prevent race conditions\n * where events could be missed during re-subscriptions caused by\n * callback dependency changes.\n */\n\nimport { useState, useEffect, useCallback, useRef } from 'react';\nimport type { BuildInfo, TodoItem, LogEntry } from '../../../lib/logging/types.js';\nimport { getLogBuffer } from '../../../lib/logging/log-buffer.js';\nimport { getLogger } from '../../../lib/logging/index.js';\n\nexport interface BuildState {\n builds: BuildInfo[];\n currentBuildIndex: number;\n currentBuild: BuildInfo | null;\n isConnected: boolean;\n isVerbose: boolean;\n}\n\nexport interface BuildStateActions {\n nextBuild: () => void;\n prevBuild: () => void;\n setVerbose: (verbose: boolean) => void;\n toggleVerbose: () => void;\n addBuild: (build: BuildInfo) => void;\n updateBuild: (buildId: string, updates: Partial<BuildInfo>) => void;\n updateTodos: (buildId: string, todos: TodoItem[]) => void;\n setConnected: (connected: boolean) => void;\n}\n\nexport function useBuildState(): [BuildState, BuildStateActions] {\n const [builds, setBuilds] = useState<BuildInfo[]>([]);\n const [currentBuildIndex, setCurrentBuildIndex] = useState(0);\n const [isConnected, setIsConnected] = useState(false);\n const [isVerbose, setIsVerbose] = useState(false);\n\n // Derive current build from index\n const currentBuild = builds.length > 0 ? builds[currentBuildIndex] ?? null : null;\n\n // Navigation between builds\n const nextBuild = useCallback(() => {\n setCurrentBuildIndex(prev => {\n if (builds.length === 0) return 0;\n return (prev + 1) % builds.length;\n });\n }, [builds.length]);\n\n const prevBuild = useCallback(() => {\n setCurrentBuildIndex(prev => {\n if (builds.length === 0) return 0;\n return prev === 0 ? builds.length - 1 : prev - 1;\n });\n }, [builds.length]);\n\n // Verbose toggle - updates both UI state and logger\n const toggleVerbose = useCallback(() => {\n setIsVerbose(prev => {\n const newValue = !prev;\n // Also update the logger's verbose flag\n try {\n const logger = getLogger();\n logger.setVerbose(newValue);\n } catch {\n // Logger not initialized yet\n }\n return newValue;\n });\n }, []);\n\n // Track when a new build is added to auto-select it\n const [pendingNewBuildId, setPendingNewBuildId] = useState<string | null>(null);\n\n // Add a new build\n const addBuild = useCallback((build: BuildInfo) => {\n setBuilds(prev => {\n // Check if build already exists\n const exists = prev.some(b => b.id === build.id);\n if (exists) {\n // Update existing build\n return prev.map(b => b.id === build.id ? { ...b, ...build } : b);\n }\n // Mark this as a new build to auto-select\n setPendingNewBuildId(build.id);\n // Add new build\n return [...prev, build];\n });\n }, []);\n\n // Auto-select newly added builds\n useEffect(() => {\n if (pendingNewBuildId) {\n const index = builds.findIndex(b => b.id === pendingNewBuildId);\n if (index !== -1) {\n setCurrentBuildIndex(index);\n }\n setPendingNewBuildId(null);\n }\n }, [builds, pendingNewBuildId]);\n\n // Update an existing build\n const updateBuild = useCallback((buildId: string, updates: Partial<BuildInfo>) => {\n setBuilds(prev => prev.map(build => \n build.id === buildId ? { ...build, ...updates } : build\n ));\n }, []);\n\n // Update todos for a build\n const updateTodos = useCallback((buildId: string, todos: TodoItem[]) => {\n setBuilds(prev => prev.map(build =>\n build.id === buildId ? { ...build, todos } : build\n ));\n }, []);\n\n // Connection status\n const setConnected = useCallback((connected: boolean) => {\n setIsConnected(connected);\n }, []);\n\n // Use refs to store the latest callbacks so event handlers always call current versions\n // This prevents race conditions where events could be missed during re-subscriptions\n const addBuildRef = useRef(addBuild);\n const updateBuildRef = useRef(updateBuild);\n const updateTodosRef = useRef(updateTodos);\n \n // Keep refs up to date\n useEffect(() => {\n addBuildRef.current = addBuild;\n }, [addBuild]);\n \n useEffect(() => {\n updateBuildRef.current = updateBuild;\n }, [updateBuild]);\n \n useEffect(() => {\n updateTodosRef.current = updateTodos;\n }, [updateTodos]);\n\n // Subscribe to RunnerLogger events - only runs ONCE on mount\n // Uses refs for callbacks to avoid re-subscription race conditions\n useEffect(() => {\n const logger = getLogger();\n \n // Sync initial state from logger (in case events already fired)\n setIsConnected(logger.isConnected());\n setIsVerbose(logger.isVerbose());\n \n // Load any existing builds\n const existingBuilds = logger.getAllBuilds();\n if (existingBuilds.length > 0) {\n for (const build of existingBuilds) {\n addBuildRef.current(build);\n }\n }\n \n // Handle build start events - use ref to get latest callback\n const handleBuildStart = (build: BuildInfo) => {\n addBuildRef.current(build);\n };\n \n // Handle build update events\n const handleBuildUpdate = (build: BuildInfo) => {\n updateBuildRef.current(build.id, build);\n };\n \n // Handle build complete events\n const handleBuildComplete = (build: BuildInfo) => {\n updateBuildRef.current(build.id, { ...build, status: build.status });\n };\n \n // Handle todo updates\n const handleTodoUpdate = (buildId: string, todos: TodoItem[]) => {\n updateTodosRef.current(buildId, todos);\n };\n \n // Handle connection events\n const handleConnected = () => {\n setIsConnected(true);\n };\n \n const handleDisconnected = () => {\n setIsConnected(false);\n };\n \n // Handle verbose change\n const handleVerboseChange = (verbose: boolean) => {\n setIsVerbose(verbose);\n };\n \n // Subscribe to events\n logger.on('buildStart', handleBuildStart);\n logger.on('buildUpdate', handleBuildUpdate);\n logger.on('buildComplete', handleBuildComplete);\n logger.on('todoUpdate', handleTodoUpdate);\n logger.on('connected', handleConnected);\n logger.on('disconnected', handleDisconnected);\n logger.on('verboseChange', handleVerboseChange);\n \n // Cleanup - only runs on unmount\n return () => {\n logger.off('buildStart', handleBuildStart);\n logger.off('buildUpdate', handleBuildUpdate);\n logger.off('buildComplete', handleBuildComplete);\n logger.off('todoUpdate', handleTodoUpdate);\n logger.off('connected', handleConnected);\n logger.off('disconnected', handleDisconnected);\n logger.off('verboseChange', handleVerboseChange);\n };\n }, []); // Empty deps - only subscribe once on mount\n\n const state: BuildState = {\n builds,\n currentBuildIndex,\n currentBuild,\n isConnected,\n isVerbose,\n };\n\n const actions: BuildStateActions = {\n nextBuild,\n prevBuild,\n setVerbose: setIsVerbose,\n toggleVerbose,\n addBuild,\n updateBuild,\n updateTodos,\n setConnected,\n };\n\n return [state, actions];\n}\n\n/**\n * Hook to subscribe to log entries from the buffer\n */\nexport function useLogEntries(maxEntries: number = 100): LogEntry[] {\n const [entries, setEntries] = useState<LogEntry[]>([]);\n\n useEffect(() => {\n const buffer = getLogBuffer();\n \n // Initialize with current entries\n setEntries(buffer.getRecent(maxEntries));\n\n // Subscribe to new entries\n const unsubscribe = buffer.onLog((entry) => {\n setEntries(prev => {\n const newEntries = [...prev, entry];\n // Keep only the most recent entries\n if (newEntries.length > maxEntries) {\n return newEntries.slice(-maxEntries);\n }\n return newEntries;\n });\n });\n\n return unsubscribe;\n }, [maxEntries]);\n\n return entries;\n}\n"],"names":["useState","useEffect","_jsxs","_jsx","useCallback","useRef"],"mappings":"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"}
package/dist/cli/index.js CHANGED
@@ -1,9 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  // Hatchway CLI - Built with Rollup
3
- import { existsSync, readFileSync } from 'node:fs';
3
+ import { readFileSync, existsSync } from 'node:fs';
4
4
  import { dirname, join } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
6
- import { execFileSync } from 'node:child_process';
7
6
  import { Command } from 'commander';
8
7
  import chalk from 'chalk';
9
8
  import { C as CLIError } from '../chunks/cli-error-1drkrXNn.js';
@@ -417,30 +416,6 @@ function findPackageRoot(startDir) {
417
416
  return startDir; // Fallback to start dir
418
417
  }
419
418
  const packageRoot = findPackageRoot(__dirname$1);
420
- // Check if running in development mode (linked via pnpm/npm link)
421
- // Skip vendor install if we're in the monorepo - dependencies are handled by pnpm
422
- const isLinkedDevelopment = packageRoot.includes('/hatchway/apps/runner');
423
- // Only run vendor install for production global installs
424
- if (!isLinkedDevelopment) {
425
- // Check if Sentry packages are missing and extract from vendor if needed
426
- // (agent-core is bundled by tsup, but Sentry packages come from vendor tarballs)
427
- const nodeModulesDir = dirname(packageRoot); // Go up from package to node_modules/@hatchway
428
- const sentryNodePath = join(nodeModulesDir, "..", "@sentry", "node");
429
- if (!existsSync(sentryNodePath)) {
430
- // Silently initialize vendor packages in background
431
- try {
432
- const installScript = join(packageRoot, "scripts/install-vendor.js");
433
- execFileSync("node", [installScript], {
434
- cwd: packageRoot,
435
- stdio: "pipe" // Silent mode - output only shown if VERBOSE=1
436
- });
437
- }
438
- catch (error) {
439
- console.error("Failed to initialize vendor packages:", error);
440
- process.exit(1);
441
- }
442
- }
443
- }
444
419
  // Get package.json for version info
445
420
  const packageJson = JSON.parse(readFileSync(join(packageRoot, 'package.json'), 'utf-8'));
446
421
  // Setup global error handlers for uncaught errors
@@ -510,12 +485,12 @@ program
510
485
  // Default action when no subcommand is provided
511
486
  if (options.runner) {
512
487
  // Start runner only (legacy flag)
513
- const { runCommand } = await import('../chunks/run-BbxtXsLx.js');
488
+ const { runCommand } = await import('../chunks/run-jC7sIavB.js');
514
489
  await runCommand({});
515
490
  }
516
491
  else {
517
492
  // Show TUI main menu
518
- const { mainTUICommand } = await import('../chunks/main-tui-qySRaa-s.js');
493
+ const { mainTUICommand } = await import('../chunks/main-tui-ZMX-H8Pf.js');
519
494
  await mainTUICommand();
520
495
  }
521
496
  }
@@ -536,7 +511,7 @@ program
536
511
  .option('--non-interactive', 'Use defaults without prompts (alias for -y)')
537
512
  .action(async (options) => {
538
513
  try {
539
- const { initCommand } = await import('../chunks/init-CBODHYDw.js');
514
+ const { initCommand } = await import('../chunks/init-DO24F24y.js');
540
515
  await initCommand(options);
541
516
  }
542
517
  catch (error) {
@@ -554,7 +529,7 @@ program
554
529
  .option('-v, --verbose', 'Enable verbose logging (show debug info)')
555
530
  .action(async (options) => {
556
531
  try {
557
- const { startCommand } = await import('../chunks/start-Dkuro1jp.js');
532
+ const { startCommand } = await import('../chunks/start-B5Vi66EE.js');
558
533
  await startCommand(options);
559
534
  }
560
535
  catch (error) {
@@ -587,7 +562,7 @@ program
587
562
  .option('--no-tui', 'Disable TUI dashboard, use plain text logs')
588
563
  .action(async (options) => {
589
564
  try {
590
- const { runCommand } = await import('../chunks/run-BbxtXsLx.js');
565
+ const { runCommand } = await import('../chunks/run-jC7sIavB.js');
591
566
  await runCommand(options);
592
567
  }
593
568
  catch (error) {