@@ -13,11 +13,14 @@ import { createEmbeddingProvider, checkServerHealth, isServerRunning, killEmbedd
1313import { createMemoryService } from './services/memory'
1414import { createEmbeddingSyncService } from './services/embedding-sync'
1515import { createKvService } from './services/kv'
16- import { createRalphService , type RalphState } from './services/ralph'
16+ import { createRalphService , type RalphState , fetchSessionOutput , type RalphSessionOutput } from './services/ralph'
17+ import { findPartialMatch } from './utils/partial-match'
1718import { loadPluginConfig } from './setup'
1819import { resolveLogPath } from './storage'
1920import { createLogger , slugify } from './utils/logger'
2021import { stripPromiseTags } from './utils/strip-promise-tags'
22+ import { truncate } from './cli/utils'
23+ import { formatSessionOutput , formatAuditResult } from './utils/ralph-format'
2124import type { Database } from 'bun:sqlite'
2225import type { PluginConfig , CompactionConfig , HealthStatus , Logger } from './types'
2326import type { EmbeddingProvider } from './embedding'
@@ -420,20 +423,19 @@ export function createMemoryPlugin(config: PluginConfig): Plugin {
420423 interface LoopContext {
421424 sessionId : string
422425 directory : string
423- branch : string
426+ branch ? : string
424427 workspaceId ?: string
425428 inPlace : boolean
426429 }
427430
428431 let loopContext : LoopContext
429432
430433 if ( options . inPlace ) {
431- let currentBranch : string
434+ let currentBranch : string | undefined
432435 try {
433436 currentBranch = execSync ( 'git rev-parse --abbrev-ref HEAD' , { cwd : projectDir , encoding : 'utf-8' } ) . trim ( )
434437 } catch ( err ) {
435- logger . error ( `ralph: failed to get current branch` , err )
436- return 'Failed to determine current git branch.'
438+ logger . log ( `ralph: no git branch detected, running without branch info` )
437439 }
438440
439441 const createResult = await v2 . session . create ( {
@@ -564,7 +566,9 @@ export function createMemoryPlugin(config: PluginConfig): Plugin {
564566
565567 if ( options . inPlace ) {
566568 lines . push ( `Directory: ${ loopContext . directory } ` )
567- lines . push ( `Branch: ${ loopContext . branch } (in-place)` )
569+ if ( loopContext . branch ) {
570+ lines . push ( `Branch: ${ loopContext . branch } (in-place)` )
571+ }
568572 } else {
569573 lines . push ( `Workspace: ${ loopContext . workspaceId } ` )
570574 lines . push ( `Worktree name: ${ autoWorktreeName } ` )
@@ -942,9 +946,22 @@ Do NOT output text without also making this tool call.
942946 let state : RalphState | null = null
943947
944948 if ( args . name ) {
945- state = ralphService . findByWorktreeName ( args . name )
949+ const name = args . name
950+ state = ralphService . findByWorktreeName ( name )
946951 if ( ! state ) {
947- return `No active Ralph loop found for worktree "${ args . name } ".`
952+ const candidates = ralphService . findCandidatesByPartialName ( name )
953+ if ( candidates . length > 0 ) {
954+ return `Multiple loops match "${ name } ":\n${ candidates . map ( ( s ) => `- ${ s . worktreeName } ` ) . join ( '\n' ) } \n\nBe more specific.`
955+ }
956+ const recent = ralphService . listRecent ( )
957+ const foundRecent = recent . find ( ( s ) => s . worktreeName === name || ( s . worktreeBranch && s . worktreeBranch . toLowerCase ( ) . includes ( name . toLowerCase ( ) ) ) )
958+ if ( foundRecent ) {
959+ return `Ralph loop "${ foundRecent . worktreeName } " has already completed.`
960+ }
961+ return `No active Ralph loop found for worktree "${ name } ".`
962+ }
963+ if ( ! state . active ) {
964+ return `Ralph loop "${ state . worktreeName } " has already completed.`
948965 }
949966 } else {
950967 const active = ralphService . listActive ( )
@@ -974,7 +991,8 @@ Do NOT output text without also making this tool call.
974991 }
975992
976993 const modeInfo = state . inPlace ? ' (in-place)' : ''
977- return `Cancelled Ralph loop "${ state . worktreeName } "${ modeInfo } (was at iteration ${ state . iteration } ).\nDirectory: ${ state . worktreeDir } \nBranch: ${ state . worktreeBranch } `
994+ const branchInfo = state . worktreeBranch ? `\nBranch: ${ state . worktreeBranch } ` : ''
995+ return `Cancelled Ralph loop "${ state . worktreeName } "${ modeInfo } (was at iteration ${ state . iteration } ).\nDirectory: ${ state . worktreeDir } ${ branchInfo } `
978996 } ,
979997 } ) ,
980998 'ralph-status' : tool ( {
@@ -992,7 +1010,11 @@ Do NOT output text without also making this tool call.
9921010 }
9931011
9941012 const recent = ralphService . listRecent ( )
995- const stoppedState = [ ...active , ...recent ] . find ( ( s ) => s . worktreeName === args . name )
1013+ const allStates = [ ...active , ...recent ]
1014+ const { match : stoppedState , candidates } = findPartialMatch ( args . name , allStates , ( s ) => [ s . worktreeName , s . worktreeBranch ] )
1015+ if ( ! stoppedState && candidates . length > 0 ) {
1016+ return `Multiple loops match "${ args . name } ":\n${ candidates . map ( ( s ) => `- ${ s . worktreeName } ` ) . join ( '\n' ) } \n\nBe more specific.`
1017+ }
9961018 if ( ! stoppedState ) {
9971019 const available = [ ...active , ...recent ] . map ( ( s ) => `- ${ s . worktreeName } ` ) . join ( '\n' )
9981020 return `No Ralph loop found for "${ args . name } ".\n\nAvailable loops:\n${ available } `
@@ -1081,14 +1103,14 @@ Do NOT output text without also making this tool call.
10811103 ralphHandler . startWatchdog ( newSessionId )
10821104
10831105 const modeInfo = stoppedState . inPlace ? ' (in-place)' : ''
1106+ const branchInfo = stoppedState . worktreeBranch ? `\nBranch: ${ stoppedState . worktreeBranch } ` : ''
10841107 return [
10851108 `Restarted Ralph loop "${ stoppedState . worktreeName } "${ modeInfo } ` ,
10861109 '' ,
10871110 `New session: ${ newSessionId } ` ,
10881111 `Continuing from iteration: ${ stoppedState . iteration } ` ,
10891112 `Previous termination: ${ stoppedState . terminationReason } ` ,
1090- `Directory: ${ stoppedState . worktreeDir } ` ,
1091- `Branch: ${ stoppedState . worktreeBranch } ` ,
1113+ `Directory: ${ stoppedState . worktreeDir } ${ branchInfo } ` ,
10921114 `Audit: ${ stoppedState . audit ? 'enabled' : 'disabled' } ` ,
10931115 ] . join ( '\n' )
10941116 }
@@ -1161,42 +1183,61 @@ Do NOT output text without also making this tool call.
11611183
11621184 const state = ralphService . findByWorktreeName ( args . name )
11631185 if ( ! state ) {
1164- const recent = ralphService . listRecent ( )
1165- const foundRecent = recent . find ( ( s ) => s . worktreeName === args . name )
1166- if ( foundRecent ) {
1167- const maxInfo = foundRecent . maxIterations > 0 ? `${ foundRecent . iteration } / ${ foundRecent . maxIterations } ` : `${ foundRecent . iteration } (unlimited)`
1168- const duration = foundRecent . completedAt
1169- ? Math . round ( ( new Date ( foundRecent . completedAt ) . getTime ( ) - new Date ( foundRecent . startedAt ) . getTime ( ) ) / 1000 )
1170- : 0
1171- const minutes = Math . floor ( duration / 60 )
1172- const seconds = duration % 60
1173- const durationStr = minutes > 0 ? `${ minutes } m ${ seconds } s` : `${ seconds } s`
1174-
1175- const completedLines : string [ ] = [
1176- 'Ralph Loop Status (Completed)' ,
1177- '' ,
1178- `Name: ${ foundRecent . worktreeName } ` ,
1179- `Session: ${ foundRecent . sessionId } ` ,
1180- ]
1181- if ( foundRecent . inPlace ) {
1182- completedLines . push ( `Mode: in-place (completed) | Directory: ${ foundRecent . worktreeDir } ` )
1183- } else {
1184- completedLines . push ( `Workspace: ${ foundRecent . workspaceId } ` )
1185- completedLines . push ( `Worktree: ${ foundRecent . worktreeDir } ` )
1186- }
1187- completedLines . push (
1188- `Iteration: ${ maxInfo } ` ,
1189- `Duration: ${ durationStr } ` ,
1190- `Reason: ${ foundRecent . terminationReason ?? 'unknown' } ` ,
1191- `Branch: ${ foundRecent . worktreeBranch } ` ,
1192- `Started: ${ foundRecent . startedAt } ` ,
1193- `Completed: ${ foundRecent . completedAt } ` ,
1194- )
1195- return completedLines . join ( '\n' )
1186+ const candidates = ralphService . findCandidatesByPartialName ( args . name )
1187+ if ( candidates . length > 0 ) {
1188+ return `Multiple loops match "${ args . name } ":\n${ candidates . map ( ( s ) => `- ${ s . worktreeName } ` ) . join ( '\n' ) } \n\nBe more specific.`
11961189 }
11971190 return `No Ralph loop found for worktree "${ args . name } ".`
11981191 }
11991192
1193+ if ( ! state . active ) {
1194+ const maxInfo = state . maxIterations > 0 ? `${ state . iteration } / ${ state . maxIterations } ` : `${ state . iteration } (unlimited)`
1195+ const duration = state . completedAt
1196+ ? Math . round ( ( new Date ( state . completedAt ) . getTime ( ) - new Date ( state . startedAt ) . getTime ( ) ) / 1000 )
1197+ : 0
1198+ const minutes = Math . floor ( duration / 60 )
1199+ const seconds = duration % 60
1200+ const durationStr = minutes > 0 ? `${ minutes } m ${ seconds } s` : `${ seconds } s`
1201+
1202+ const statusLines : string [ ] = [
1203+ 'Ralph Loop Status (Inactive)' ,
1204+ '' ,
1205+ `Name: ${ state . worktreeName } ` ,
1206+ `Session: ${ state . sessionId } ` ,
1207+ ]
1208+ if ( state . inPlace ) {
1209+ statusLines . push ( `Mode: in-place | Directory: ${ state . worktreeDir } ` )
1210+ } else {
1211+ statusLines . push ( `Workspace: ${ state . workspaceId } ` )
1212+ statusLines . push ( `Worktree: ${ state . worktreeDir } ` )
1213+ }
1214+ statusLines . push (
1215+ `Iteration: ${ maxInfo } ` ,
1216+ `Duration: ${ durationStr } ` ,
1217+ `Reason: ${ state . terminationReason ?? 'unknown' } ` ,
1218+ )
1219+ if ( state . worktreeBranch ) {
1220+ statusLines . push ( `Branch: ${ state . worktreeBranch } ` )
1221+ }
1222+ statusLines . push (
1223+ `Started: ${ state . startedAt } ` ,
1224+ ...( state . completedAt ? [ `Completed: ${ state . completedAt } ` ] : [ ] ) ,
1225+ )
1226+
1227+ if ( state . lastAuditResult ) {
1228+ statusLines . push ( ...formatAuditResult ( state . lastAuditResult ) )
1229+ }
1230+
1231+ const sessionOutput = await fetchSessionOutput ( v2 , state . sessionId , state . worktreeDir , logger )
1232+ if ( sessionOutput ) {
1233+ statusLines . push ( '' )
1234+ statusLines . push ( 'Session Output:' )
1235+ statusLines . push ( ...formatSessionOutput ( sessionOutput ) )
1236+ }
1237+
1238+ return statusLines . join ( '\n' )
1239+ }
1240+
12001241 const maxInfo = state . maxIterations > 0 ? `${ state . iteration } / ${ state . maxIterations } ` : `${ state . iteration } (unlimited)`
12011242 const promptPreview = state . prompt . length > 100 ? `${ state . prompt . substring ( 0 , 97 ) } ...` : state . prompt
12021243
@@ -1243,7 +1284,11 @@ Do NOT output text without also making this tool call.
12431284 `Iteration: ${ maxInfo } ` ,
12441285 `Duration: ${ duration } ` ,
12451286 `Audit: ${ state . audit ? 'enabled' : 'disabled' } ` ,
1246- `Branch: ${ state . worktreeBranch } ` ,
1287+ )
1288+ if ( state . worktreeBranch ) {
1289+ statusLines . push ( `Branch: ${ state . worktreeBranch } ` )
1290+ }
1291+ statusLines . push (
12471292 `Completion promise: ${ state . completionPromise ?? 'none' } ` ,
12481293 `Started: ${ state . startedAt } ` ,
12491294 ...( state . errorCount > 0 ? [ `Error count: ${ state . errorCount } (retries before termination: ${ MAX_RETRIES } )` ] : [ ] ) ,
@@ -1255,6 +1300,18 @@ Do NOT output text without also making this tool call.
12551300 '' ,
12561301 `Prompt: ${ promptPreview } ` ,
12571302 )
1303+
1304+ if ( state . lastAuditResult ) {
1305+ statusLines . push ( ...formatAuditResult ( state . lastAuditResult ) )
1306+ }
1307+
1308+ const sessionOutput = await fetchSessionOutput ( v2 , state . sessionId , state . worktreeDir , logger )
1309+ if ( sessionOutput ) {
1310+ statusLines . push ( '' )
1311+ statusLines . push ( 'Session Output:' )
1312+ statusLines . push ( ...formatSessionOutput ( sessionOutput ) )
1313+ }
1314+
12581315 return statusLines . join ( '\n' )
12591316 } ,
12601317 } ) ,
0 commit comments