@@ -207,128 +207,176 @@ async function main() {
207207 const prompt = positionals . join ( " " ) . trim ( ) ;
208208 const harness = new Harness ( cfg ) ;
209209
210- // Fallback to non-TUI mode if terminal is not a TTY or if requested
211- const isTTY = ! ! ( process . stdout . isTTY && process . stdin . isTTY ) ;
212- const useNoTui = ! ! ( values [ "no-tui" ] || ! isTTY ) ;
213-
214- if ( useNoTui || prompt !== "" ) {
215- harness . events . on ( ( e ) => {
216- if ( e . type === "phase" )
210+ harness . events . on ( ( e ) => {
211+ if ( e . type === "phase" ) {
212+ if ( harness . phase !== "idle" ) {
217213 console . log ( `\n[phase] ${ e . phase . toUpperCase ( ) } ${ e . detail || "" } ` ) ;
218- else if ( e . type === "agent_log" && e . line . kind !== "say" )
214+ }
215+ } else if ( e . type === "agent_log" && e . line . kind !== "say" ) {
216+ if ( harness . phase !== "idle" ) {
219217 console . log ( `[${ e . agent } ] ${ e . line . kind } : ${ e . line . text } ` ) ;
220- else if ( e . type === "agent_log" && e . line . kind === "say" )
221- process . stdout . write ( e . line . text ) ;
222- else if ( e . type === "bus" )
218+ }
219+ } else if ( e . type === "agent_log" && e . line . kind === "say" ) {
220+ process . stdout . write ( e . line . text ) ;
221+ } else if ( e . type === "bus" ) {
222+ if ( harness . phase !== "idle" ) {
223223 console . log (
224224 `[bus] ${ e . message . from } -> ${ e . message . to } [${ e . message . kind } ] ${ e . message . title } ` ,
225225 ) ;
226- else if ( e . type === "system" ) console . log ( `[system] ${ e . text } ` ) ;
227- else if ( e . type === "plan" ) {
228- console . log ( `\n=================== PLAN ===================` ) ;
229- console . log ( `Title: ${ e . plan . title } ` ) ;
230- console . log ( `Summary: ${ e . plan . summary } ` ) ;
231- console . log ( `============================================\n` ) ;
232- } else if ( e . type === "questions" ) {
233- console . log ( `\n================= QUESTIONS =================` ) ;
234- console . log ( e . questions . map ( ( q , i ) => `${ i + 1 } . ${ q . question } ` ) . join ( "\n" ) ) ;
235- console . log ( `=============================================\n` ) ;
236- } else if ( e . type === "final" ) console . log ( "\n=== READY ===\n" + e . text ) ;
237- else if ( e . type === "swarm" )
226+ }
227+ } else if ( e . type === "system" ) {
228+ console . log ( `[system] ${ e . text } ` ) ;
229+ } else if ( e . type === "plan" ) {
230+ console . log ( `\n=================== PLAN ===================` ) ;
231+ console . log ( `Title: ${ e . plan . title } ` ) ;
232+ console . log ( `Summary: ${ e . plan . summary } ` ) ;
233+ console . log ( `============================================\n` ) ;
234+ } else if ( e . type === "questions" ) {
235+ console . log ( `\n================= QUESTIONS =================` ) ;
236+ console . log ( e . questions . map ( ( q , i ) => `${ i + 1 } . ${ q . question } ` ) . join ( "\n" ) ) ;
237+ console . log ( `=============================================\n` ) ;
238+ } else if ( e . type === "final" ) {
239+ console . log ( "\n=== READY ===\n" + e . text ) ;
240+ } else if ( e . type === "swarm" ) {
241+ if ( harness . phase !== "idle" ) {
238242 console . log ( `[swarm] ${ e . action } ${ e . workerId } active=${ e . active } ` ) ;
239- else if ( e . type === "approval_request" ) {
240- if ( cfg . autoApprove ) harness . resolveApproval ( e . id , true ) ;
241- else {
242- console . log ( `[approval] denied (use -y/--yolo or autoApprove): ${ e . agent } ${ e . tool } ` ) ;
243- harness . resolveApproval ( e . id , false ) ;
244- }
245243 }
244+ } else if ( e . type === "approval_request" ) {
245+ if ( cfg . autoApprove ) harness . resolveApproval ( e . id , true ) ;
246+ else {
247+ console . log ( `[approval] denied (use -y/--yolo or autoApprove): ${ e . agent } ${ e . tool } ` ) ;
248+ harness . resolveApproval ( e . id , false ) ;
249+ }
250+ }
251+ } ) ;
252+
253+ if ( prompt ) {
254+ await harness . startPlan ( prompt ) ;
255+ if ( harness . plan ) {
256+ console . log ( "\n[headless] auto /confirm" ) ;
257+ await harness . confirmAndExecute ( ) ;
258+ }
259+ console . log ( "\n[metrics]" , harness . metricsLine ( ) ) ;
260+ process . exit ( 0 ) ;
261+ } else {
262+ // CLI Interactive Session (like Claude Code)
263+ const readline = await import ( "node:readline" ) ;
264+ const rl = readline . createInterface ( {
265+ input : process . stdin ,
266+ output : process . stdout ,
246267 } ) ;
247268
248- if ( prompt ) {
249- await harness . startPlan ( prompt ) ;
250- if ( harness . plan ) {
251- console . log ( "\n[headless] auto /confirm" ) ;
252- await harness . confirmAndExecute ( ) ;
253- }
254- console . log ( "\n[metrics]" , harness . metricsLine ( ) ) ;
255- process . exit ( 0 ) ;
256- } else {
257- // CLI Interactive Session (like Claude Code)
258- const readline = await import ( "node:readline" ) ;
259- const rl = readline . createInterface ( {
260- input : process . stdin ,
261- output : process . stdout ,
262- } ) ;
269+ console . log ( "\n================================================================" ) ;
270+ console . log ( " /\\" ) ;
271+ console . log ( " / \\ ARROWCODE Interactive CLI" ) ;
272+ console . log ( " / /\\ \\ swarm coding harness" ) ;
273+ console . log ( " /______\\ ORCH · FE · BE · QA" ) ;
274+ console . log ( "================================================================\n" ) ;
275+ console . log ( " Welcome! You are in the interactive CLI mode (similar to Claude Code)." ) ;
276+ console . log ( " - Type standard prompt to chat with the agents." ) ;
277+ console . log ( " - Use /plan <goal> to propose a new plan." ) ;
278+ console . log ( " - Use /add <filepath> to attach a file to the context." ) ;
279+ console . log ( " - Use /swarm to launch the fullscreen dashboard TUI." ) ;
280+ console . log ( " - Use /confirm to approve and execute the current plan." ) ;
281+ console . log ( " - Use /accept to finish and save changes." ) ;
282+ console . log ( " - Use /help to see all available commands." ) ;
283+ console . log ( " - Type /exit or /quit to quit.\n" ) ;
284+
285+ const attachedFiles : { path : string ; content : string } [ ] = [ ] ;
286+
287+ const promptUser = ( ) => {
288+ rl . question ( "arrowcode> " , async ( answer ) => {
289+ const line = answer . trim ( ) ;
290+ if ( ! line ) {
291+ promptUser ( ) ;
292+ return ;
293+ }
294+ if ( line === "/exit" || line === "/quit" ) {
295+ rl . close ( ) ;
296+ process . exit ( 0 ) ;
297+ }
298+
299+ let cmdLine = line ;
300+ if ( cmdLine . startsWith ( "." ) ) {
301+ cmdLine = "/" + cmdLine . slice ( 1 ) ;
302+ }
303+
304+ if ( cmdLine === "/swarm" ) {
305+ rl . close ( ) ;
306+ console . log ( "\nLaunching Multi-Agent Swarm TUI Dashboard..." ) ;
307+ const { render } = await import ( "ink" ) ;
308+ const { App } = await import ( "./tui/App" ) ;
309+ const { waitUntilExit } = render (
310+ React . createElement ( App , {
311+ harness,
312+ config : cfg ,
313+ initialPrompt : undefined ,
314+ } ) ,
315+ { exitOnCtrlC : true } ,
316+ ) ;
317+ await waitUntilExit ( ) ;
318+ process . exit ( 0 ) ;
319+ }
320+
321+ if ( cmdLine . startsWith ( "/add " ) ) {
322+ const filePath = cmdLine . slice ( 5 ) . trim ( ) ;
323+ try {
324+ const absolutePath = harness . workspace . resolve ( filePath , { mustExist : true } ) ;
325+ const fs = await import ( "node:fs" ) ;
326+ const content = fs . readFileSync ( absolutePath , "utf8" ) ;
327+ const lineCount = content . split ( "\n" ) . length ;
328+ attachedFiles . push ( { path : filePath , content } ) ;
329+ console . log ( `[system] Attached file ${ filePath } (${ lineCount } lines)` ) ;
330+ } catch ( err : any ) {
331+ console . log ( `[system] Error: Failed to attach file: ${ err . message } ` ) ;
332+ }
333+ promptUser ( ) ;
334+ return ;
335+ }
263336
264- console . log ( "\n================================================================" ) ;
265- console . log ( " /\\" ) ;
266- console . log ( " / \\ ARROWCODE Interactive CLI" ) ;
267- console . log ( " / /\\ \\ swarm coding harness" ) ;
268- console . log ( " /______\\ ORCH · FE · BE · QA" ) ;
269- console . log ( "================================================================\n" ) ;
270- console . log ( " Welcome! You are in the interactive CLI mode (similar to Claude Code)." ) ;
271- console . log ( " - Type standard prompt to chat with the agents." ) ;
272- console . log ( " - Use /plan <goal> to propose a new plan." ) ;
273- console . log ( " - Use /confirm to approve and execute the current plan." ) ;
274- console . log ( " - Use /accept to finish and save changes." ) ;
275- console . log ( " - Use /help to see all available commands." ) ;
276- console . log ( " - Type /exit or /quit to quit.\n" ) ;
277-
278- const promptUser = ( ) => {
279- rl . question ( "arrowcode> " , async ( answer ) => {
280- const line = answer . trim ( ) ;
281- if ( ! line ) {
282- promptUser ( ) ;
283- return ;
337+ if ( cmdLine . startsWith ( "/" ) ) {
338+ // Dispatch command
339+ const [ cmd , ..._rest ] = cmdLine . slice ( 1 ) . split ( / \s + / ) ;
340+ const { dispatchCommand } = await import ( "./commands/registry" ) ;
341+
342+ let modifiedCmdLine = cmdLine ;
343+ if ( cmd === "plan" && attachedFiles . length > 0 ) {
344+ const attachmentBlock = attachedFiles
345+ . map ( ( f ) => `=== ATTACHED FILE: ${ f . path } ===\n${ f . content } \n===============================` )
346+ . join ( "\n\n" ) ;
347+ modifiedCmdLine = `/plan ${ attachmentBlock } \n\nUser Request:\n${ cmdLine . slice ( 5 ) . trim ( ) } ` ;
348+ attachedFiles . length = 0 ;
284349 }
285- if ( line === "/exit" || line === "/quit" ) {
350+
351+ const res = await dispatchCommand ( modifiedCmdLine , {
352+ harness,
353+ setYolo : ( ) => { } ,
354+ getYolo : ( ) => false ,
355+ } ) ;
356+ if ( res . type === "exit" ) {
286357 rl . close ( ) ;
287358 process . exit ( 0 ) ;
288359 }
289-
290- let cmdLine = line ;
291- if ( cmdLine . startsWith ( "." ) ) {
292- cmdLine = "/" + cmdLine . slice ( 1 ) ;
360+ if ( res && "message" in res && res . message ) {
361+ console . log ( `[system] ${ res . message } ` ) ;
293362 }
294-
295- if ( cmdLine . startsWith ( "/" ) ) {
296- // Dispatch command
297- const [ cmd , ..._rest ] = cmdLine . slice ( 1 ) . split ( / \s + / ) ;
298- const { dispatchCommand } = await import ( "./commands/registry" ) ;
299- const res = await dispatchCommand ( cmdLine , {
300- harness,
301- setYolo : ( ) => { } ,
302- getYolo : ( ) => false ,
303- } ) ;
304- if ( res . type === "exit" ) {
305- rl . close ( ) ;
306- process . exit ( 0 ) ;
307- }
308- if ( res && "message" in res && res . message ) {
309- console . log ( `[system] ${ res . message } ` ) ;
310- }
311- } else {
312- await harness . chat ( line ) ;
363+ } else {
364+ let payload = line ;
365+ if ( attachedFiles . length > 0 ) {
366+ const attachmentBlock = attachedFiles
367+ . map ( ( f ) => `=== ATTACHED FILE: ${ f . path } ===\n${ f . content } \n===============================` )
368+ . join ( "\n\n" ) ;
369+ payload = `${ attachmentBlock } \n\nUser Prompt:\n${ line } ` ;
370+ attachedFiles . length = 0 ;
313371 }
314- promptUser ( ) ;
315- } ) ;
316- } ;
317- promptUser ( ) ;
318- return ;
319- }
372+ await harness . chat ( payload ) ;
373+ }
374+ promptUser ( ) ;
375+ } ) ;
376+ } ;
377+ promptUser ( ) ;
378+ return ;
320379 }
321-
322- const { waitUntilExit } = render (
323- React . createElement ( App , {
324- harness,
325- config : cfg ,
326- initialPrompt : prompt || undefined ,
327- } ) ,
328- { exitOnCtrlC : true } ,
329- ) ;
330-
331- await waitUntilExit ( ) ;
332380}
333381
334382main ( ) . catch ( ( e ) => {
0 commit comments