Skip to content

Commit 48670ce

Browse files
Update memory core services and bump version to 0.0.23
Enhance ralph service with partial name matching and candidate lookup. Improve loop context handling for git branch detection. Add formatSessionOutput and formatAuditResult helpers.
1 parent c2d502f commit 48670ce

5 files changed

Lines changed: 231 additions & 50 deletions

File tree

packages/memory/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@opencode-manager/memory",
3-
"version": "0.0.22",
3+
"version": "0.0.23",
44
"type": "module",
55
"main": "./dist/index.js",
66
"types": "./dist/index.d.ts",

packages/memory/src/agents/auditor.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,15 @@ If only suggestions were found or no issues at all:
109109
110110
If no issues are found, say so clearly and briefly.
111111
112+
## Verification
113+
114+
Before finalizing your review, run the project's type check to catch type errors the diff review may miss.
115+
116+
1. Determine the type check command — look at package.json scripts, Makefile, pyproject.toml, or other build config for a typecheck/type-check/check-types target. If none exists, look for a tsconfig.json and run \`tsc --noEmit\`, or skip if the project has no static type checking.
117+
2. Run the type check command.
118+
3. If there are type errors in files touched by the diff, report each as a **bug** severity finding with the file path and error message.
119+
4. If type errors exist only in files NOT touched by the diff, mention them under **Observations** but do not block the review.
120+
112121
## Constraints
113122
114123
You are read-only on source code. Do not edit files, run destructive commands, or make any changes. Only read, search, analyze, and report findings.

packages/memory/src/index.ts

Lines changed: 102 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,14 @@ import { createEmbeddingProvider, checkServerHealth, isServerRunning, killEmbedd
1313
import { createMemoryService } from './services/memory'
1414
import { createEmbeddingSyncService } from './services/embedding-sync'
1515
import { 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'
1718
import { loadPluginConfig } from './setup'
1819
import { resolveLogPath } from './storage'
1920
import { createLogger, slugify } from './utils/logger'
2021
import { stripPromiseTags } from './utils/strip-promise-tags'
22+
import { truncate } from './cli/utils'
23+
import { formatSessionOutput, formatAuditResult } from './utils/ralph-format'
2124
import type { Database } from 'bun:sqlite'
2225
import type { PluginConfig, CompactionConfig, HealthStatus, Logger } from './types'
2326
import 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

Comments
 (0)