fix: resolve publish blockers for v3.7.4→v3.8.0 release
- Fix #1991 crash: optional chaining for task-history sessionID access - Fix #1992 think-mode: add antigravity entries to HIGH_VARIANT_MAP - Fix #1949 Copilot premium misattribution: use createInternalAgentTextPart - Fix #1982 load_skills: pass directory to discoverSkills for project-level skills - Fix command priority: sort scopePriority before .find(), project-first return - Fix Google provider transform: apply in userFallbackModels path - Fix ralph-loop TUI: optional chaining for event handler - Fix runtime-fallback: unify dual fallback engines, remove HTTP 400 from retry, fix pendingFallbackModel stuck state, add priority gate to skip model-fallback when runtime-fallback is active - Fix Prometheus task system: exempt from todowrite/todoread deny - Fix background_output: default full_session to true - Remove orphan hooks: hashline-edit-diff-enhancer (redundant with hashline_edit built-in diff), task-reminder (dead code) - Remove orphan config entries: 3 stale hook names from Zod schema - Fix disabled_hooks schema: accept arbitrary strings for forward compatibility - Register json-error-recovery hook in tool-guard pipeline - Add disabled_hooks gating for question-label-truncator, task-resume-info, claude-code-hooks - Update test expectations to match new behavior
This commit is contained in:
@@ -1,106 +0,0 @@
|
||||
import { log } from "../../shared"
|
||||
import { generateUnifiedDiff, countLineDiffs } from "../../tools/hashline-edit/diff-utils"
|
||||
|
||||
interface HashlineEditDiffEnhancerConfig {
|
||||
hashline_edit?: { enabled: boolean }
|
||||
}
|
||||
|
||||
type BeforeInput = { tool: string; sessionID: string; callID: string }
|
||||
type BeforeOutput = { args: Record<string, unknown> }
|
||||
type AfterInput = { tool: string; sessionID: string; callID: string }
|
||||
type AfterOutput = { title: string; output: string; metadata: Record<string, unknown> }
|
||||
|
||||
const STALE_TIMEOUT_MS = 5 * 60 * 1000
|
||||
|
||||
const pendingCaptures = new Map<string, { content: string; filePath: string; storedAt: number }>()
|
||||
|
||||
function makeKey(sessionID: string, callID: string): string {
|
||||
return `${sessionID}:${callID}`
|
||||
}
|
||||
|
||||
function cleanupStaleEntries(): void {
|
||||
const now = Date.now()
|
||||
for (const [key, entry] of pendingCaptures) {
|
||||
if (now - entry.storedAt > STALE_TIMEOUT_MS) {
|
||||
pendingCaptures.delete(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isWriteTool(toolName: string): boolean {
|
||||
return toolName.toLowerCase() === "write"
|
||||
}
|
||||
|
||||
function extractFilePath(args: Record<string, unknown>): string | undefined {
|
||||
const path = args.path ?? args.filePath ?? args.file_path
|
||||
return typeof path === "string" ? path : undefined
|
||||
}
|
||||
|
||||
async function captureOldContent(filePath: string): Promise<string> {
|
||||
try {
|
||||
const file = Bun.file(filePath)
|
||||
if (await file.exists()) {
|
||||
return await file.text()
|
||||
}
|
||||
} catch {
|
||||
log("[hashline-edit-diff-enhancer] failed to read old content", { filePath })
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
export function createHashlineEditDiffEnhancerHook(config: HashlineEditDiffEnhancerConfig) {
|
||||
const enabled = config.hashline_edit?.enabled ?? false
|
||||
|
||||
return {
|
||||
"tool.execute.before": async (input: BeforeInput, output: BeforeOutput) => {
|
||||
if (!enabled || !isWriteTool(input.tool)) return
|
||||
|
||||
const filePath = extractFilePath(output.args)
|
||||
if (!filePath) return
|
||||
|
||||
cleanupStaleEntries()
|
||||
const oldContent = await captureOldContent(filePath)
|
||||
pendingCaptures.set(makeKey(input.sessionID, input.callID), {
|
||||
content: oldContent,
|
||||
filePath,
|
||||
storedAt: Date.now(),
|
||||
})
|
||||
},
|
||||
|
||||
"tool.execute.after": async (input: AfterInput, output: AfterOutput) => {
|
||||
if (!enabled || !isWriteTool(input.tool)) return
|
||||
|
||||
const key = makeKey(input.sessionID, input.callID)
|
||||
const captured = pendingCaptures.get(key)
|
||||
if (!captured) return
|
||||
pendingCaptures.delete(key)
|
||||
|
||||
const { content: oldContent, filePath } = captured
|
||||
|
||||
let newContent: string
|
||||
try {
|
||||
newContent = await Bun.file(filePath).text()
|
||||
} catch {
|
||||
log("[hashline-edit-diff-enhancer] failed to read new content", { filePath })
|
||||
return
|
||||
}
|
||||
|
||||
const { additions, deletions } = countLineDiffs(oldContent, newContent)
|
||||
const unifiedDiff = generateUnifiedDiff(oldContent, newContent, filePath)
|
||||
|
||||
output.metadata.filediff = {
|
||||
file: filePath,
|
||||
path: filePath,
|
||||
before: oldContent,
|
||||
after: newContent,
|
||||
additions,
|
||||
deletions,
|
||||
}
|
||||
|
||||
// TUI reads metadata.diff (unified diff string), not filediff object
|
||||
output.metadata.diff = unifiedDiff
|
||||
|
||||
output.title = filePath
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user