mirror of
https://github.com/AgentSeal/codeburn.git
synced 2026-05-16 19:44:14 +00:00
Add workspace extraction for Cline-family providers
Extract project name from workspace directory in api_conversation_history.json so sessions show actual folder names instead of the provider display name. Thread projectPath through ParsedProviderCall to avoid unsanitizePath mangling hyphenated folder names.
This commit is contained in:
parent
7e0e3af29f
commit
166424970a
3 changed files with 47 additions and 19 deletions
|
|
@ -550,7 +550,7 @@ async function parseProviderSources(
|
|||
const provider = await getProvider(providerName)
|
||||
if (!provider) return []
|
||||
|
||||
const sessionMap = new Map<string, { project: string; turns: ClassifiedTurn[] }>()
|
||||
const sessionMap = new Map<string, { project: string; projectPath?: string; turns: ClassifiedTurn[] }>()
|
||||
|
||||
try {
|
||||
for (const source of sources) {
|
||||
|
|
@ -574,13 +574,15 @@ async function parseProviderSources(
|
|||
|
||||
const turn = providerCallToTurn(call)
|
||||
const classified = classifyTurn(turn)
|
||||
const key = `${providerName}:${call.sessionId}:${source.project}`
|
||||
const project = call.project ?? source.project
|
||||
const key = `${providerName}:${call.sessionId}:${project}`
|
||||
|
||||
const existing = sessionMap.get(key)
|
||||
if (existing) {
|
||||
existing.turns.push(classified)
|
||||
if (!existing.projectPath && call.projectPath) existing.projectPath = call.projectPath
|
||||
} else {
|
||||
sessionMap.set(key, { project: source.project, turns: [classified] })
|
||||
sessionMap.set(key, { project, projectPath: call.projectPath, turns: [classified] })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -592,22 +594,26 @@ async function parseProviderSources(
|
|||
}
|
||||
}
|
||||
|
||||
const projectMap = new Map<string, SessionSummary[]>()
|
||||
for (const [key, { project, turns }] of sessionMap) {
|
||||
const projectMap = new Map<string, { projectPath?: string; sessions: SessionSummary[] }>()
|
||||
for (const [key, { project, projectPath, turns }] of sessionMap) {
|
||||
const sessionId = key.split(':')[1] ?? key
|
||||
const session = buildSessionSummary(sessionId, project, turns)
|
||||
if (session.apiCalls > 0) {
|
||||
const existing = projectMap.get(project) ?? []
|
||||
existing.push(session)
|
||||
projectMap.set(project, existing)
|
||||
const existing = projectMap.get(project)
|
||||
if (existing) {
|
||||
existing.sessions.push(session)
|
||||
if (!existing.projectPath && projectPath) existing.projectPath = projectPath
|
||||
} else {
|
||||
projectMap.set(project, { projectPath, sessions: [session] })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const projects: ProjectSummary[] = []
|
||||
for (const [dirName, sessions] of projectMap) {
|
||||
for (const [dirName, { projectPath, sessions }] of projectMap) {
|
||||
projects.push({
|
||||
project: dirName,
|
||||
projectPath: unsanitizePath(dirName),
|
||||
projectPath: projectPath ?? unsanitizePath(dirName),
|
||||
sessions,
|
||||
totalCostUSD: sessions.reduce((s, sess) => s + sess.totalCostUSD, 0),
|
||||
totalApiCalls: sessions.reduce((s, sess) => s + sess.apiCalls, 0),
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ export type ParsedProviderCall = {
|
|||
deduplicationKey: string
|
||||
userMessage: string
|
||||
sessionId: string
|
||||
project?: string
|
||||
projectPath?: string
|
||||
}
|
||||
|
||||
export type Provider = {
|
||||
|
|
|
|||
|
|
@ -67,25 +67,40 @@ async function discoverClineTasksInBaseDir(baseDir: string, providerName: string
|
|||
}
|
||||
|
||||
const MODEL_TAG_RE = /<model>([^<]+)<\/model>/
|
||||
const WORKSPACE_DIR_RE = /Current Workspace Directory \(([^)]+)\)/
|
||||
|
||||
function extractModelFromHistory(taskDir: string, fallbackModel: string): Promise<string> {
|
||||
type HistoryMeta = { model: string; workspace: string | null }
|
||||
|
||||
function extractHistoryMeta(taskDir: string, fallbackModel: string): Promise<HistoryMeta> {
|
||||
return readFile(join(taskDir, 'api_conversation_history.json'), 'utf-8')
|
||||
.then(raw => {
|
||||
const msgs = JSON.parse(raw) as Array<{ role?: string; content?: Array<{ text?: string }> }>
|
||||
if (!Array.isArray(msgs)) return fallbackModel
|
||||
if (!Array.isArray(msgs)) return { model: fallbackModel, workspace: null }
|
||||
let model: string | null = null
|
||||
let workspace: string | null = null
|
||||
for (const msg of msgs) {
|
||||
if (msg.role !== 'user' || !Array.isArray(msg.content)) continue
|
||||
for (const block of msg.content) {
|
||||
const match = typeof block.text === 'string' && MODEL_TAG_RE.exec(block.text)
|
||||
if (match) {
|
||||
const raw = match[1]
|
||||
return raw.includes('/') ? raw.split('/').pop()! : raw
|
||||
if (typeof block.text !== 'string') continue
|
||||
if (!model) {
|
||||
const mm = MODEL_TAG_RE.exec(block.text)
|
||||
if (mm) model = mm[1].includes('/') ? mm[1].split('/').pop()! : mm[1]
|
||||
}
|
||||
if (!workspace) {
|
||||
const wm = WORKSPACE_DIR_RE.exec(block.text)
|
||||
if (wm) workspace = wm[1]
|
||||
}
|
||||
if (model && workspace) break
|
||||
}
|
||||
if (model && workspace) break
|
||||
}
|
||||
return fallbackModel
|
||||
return { model: model ?? fallbackModel, workspace }
|
||||
})
|
||||
.catch(() => fallbackModel)
|
||||
.catch(() => ({ model: fallbackModel, workspace: null }))
|
||||
}
|
||||
|
||||
function workspaceToProject(workspace: string): string {
|
||||
return basename(workspace) || workspace
|
||||
}
|
||||
|
||||
export function createClineParser(source: SessionSource, seenKeys: Set<string>, providerName: string, fallbackModel = 'cline-auto'): SessionParser {
|
||||
|
|
@ -110,7 +125,10 @@ export function createClineParser(source: SessionSource, seenKeys: Set<string>,
|
|||
|
||||
if (!Array.isArray(uiMessages)) return
|
||||
|
||||
const model = await extractModelFromHistory(taskDir, fallbackModel)
|
||||
const meta = await extractHistoryMeta(taskDir, fallbackModel)
|
||||
const model = meta.model
|
||||
const project = meta.workspace ? workspaceToProject(meta.workspace) : undefined
|
||||
const projectPath = meta.workspace ?? undefined
|
||||
|
||||
let userMessage = ''
|
||||
for (const msg of uiMessages) {
|
||||
|
|
@ -173,6 +191,8 @@ export function createClineParser(source: SessionSource, seenKeys: Set<string>,
|
|||
deduplicationKey: dedupKey,
|
||||
userMessage: index === 0 ? userMessage : '',
|
||||
sessionId: taskId,
|
||||
project,
|
||||
projectPath,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue