feat(hooks): add Claude hooks config, transcript, and todo
🤖 GENERATED WITH ASSISTANCE OF [OhMyOpenCode](https://github.com/code-yeongyu/oh-my-opencode)
This commit is contained in:
72
notepad.md
72
notepad.md
@@ -466,3 +466,75 @@ All tasks execution STARTED: Thu Dec 4 16:52:57 KST 2025
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## [2025-12-09 17:39] - Task 3: tool-input-cache.ts 포팅
|
||||||
|
|
||||||
|
### DISCOVERED ISSUES
|
||||||
|
- None - straightforward file copy task
|
||||||
|
|
||||||
|
### IMPLEMENTATION DECISIONS
|
||||||
|
- Copied tool-input-cache.ts (48 lines) from opencode-cc-plugin → oh-my-opencode
|
||||||
|
- Preserved cache structure:
|
||||||
|
* Key format: `${sessionId}:${toolName}:${invocationId}`
|
||||||
|
* TTL: 60000ms (1 minute) as CACHE_TTL constant
|
||||||
|
* Periodic cleanup: setInterval every CACHE_TTL (60000ms)
|
||||||
|
- Preserved original comments from source file (lines 12, 39)
|
||||||
|
- Functions: cacheToolInput(), getToolInput()
|
||||||
|
- Cache behavior: getToolInput() deletes entry immediately after retrieval (single-use cache)
|
||||||
|
|
||||||
|
### PROBLEMS FOR NEXT TASKS
|
||||||
|
- Task 4 (pre-tool-use.ts) will call cacheToolInput() to store tool inputs
|
||||||
|
- Task 5 (post-tool-use.ts) will call getToolInput() to retrieve cached inputs for transcript building
|
||||||
|
- No import path changes needed - this file has no external dependencies
|
||||||
|
|
||||||
|
### VERIFICATION RESULTS
|
||||||
|
- File created: `src/hooks/claude-code-hooks/tool-input-cache.ts` (48 lines)
|
||||||
|
- Functions exported: cacheToolInput(), getToolInput()
|
||||||
|
- TTL verified: CACHE_TTL = 60000 (1 minute)
|
||||||
|
- Cleanup interval verified: setInterval(cleanup, CACHE_TTL)
|
||||||
|
|
||||||
|
### LEARNINGS
|
||||||
|
- Tool input cache is a temporary storage for PreToolUse → PostToolUse communication
|
||||||
|
- Single-use pattern: getToolInput() deletes entry after first retrieval (line 33)
|
||||||
|
- TTL check happens after deletion, so expired entries still return null
|
||||||
|
- setInterval runs in background for periodic cleanup of abandoned entries
|
||||||
|
- Source location: `~/local-workspaces/opencode-cc-plugin/src/claude-compat/hooks/tool-input-cache.ts`
|
||||||
|
|
||||||
|
소요 시간: ~2분
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [2025-12-09 17:39] - Task 2: config.ts + transcript.ts + todo.ts 포팅
|
||||||
|
|
||||||
|
### DISCOVERED ISSUES
|
||||||
|
- transcript.ts had unused imports (ClaudeCodeMessage, ClaudeCodeContent) - same as source file
|
||||||
|
- LSP warned about unused types - removed from import to clean up
|
||||||
|
|
||||||
|
### IMPLEMENTATION DECISIONS
|
||||||
|
- Copied config.ts (101 lines) - no import path changes needed (only uses `./types` and Node.js builtins)
|
||||||
|
- Copied transcript.ts (256 lines) - changed import path:
|
||||||
|
* Line 10: `../shared/tool-name` → `../../shared/tool-name` (opencode-cc-plugin depth 1, oh-my-opencode depth 2)
|
||||||
|
- Copied todo.ts (78 lines) - no import path changes needed (only uses `./types` and Node.js builtins)
|
||||||
|
- Removed unused imports from transcript.ts: ClaudeCodeMessage, ClaudeCodeContent (not used in function bodies)
|
||||||
|
- Preserved ALL original comments from source files - these are pre-existing comments
|
||||||
|
|
||||||
|
### PROBLEMS FOR NEXT TASKS
|
||||||
|
- Task 3 will import cacheToolInput/getToolInput for cache functionality
|
||||||
|
- Task 4 will import loadClaudeHooksConfig, buildTranscriptFromSession
|
||||||
|
- Task 5 will import transcript building functions for PostToolUse hook
|
||||||
|
|
||||||
|
### VERIFICATION RESULTS
|
||||||
|
- Ran: `bun run typecheck` → exit 0, no errors
|
||||||
|
- Files created: config.ts (101 lines), transcript.ts (256 lines), todo.ts (78 lines)
|
||||||
|
- Functions available: loadClaudeHooksConfig(), buildTranscriptFromSession(), appendTranscriptEntry(), loadTodoFile(), saveTodoFile()
|
||||||
|
- Import paths verified: transcript.ts successfully imports transformToolName from ../../shared
|
||||||
|
|
||||||
|
### LEARNINGS
|
||||||
|
- Import path depth difference: opencode-cc-plugin `src/claude-compat/` (1 level up) → oh-my-opencode `src/hooks/claude-code-hooks/` (2 levels up)
|
||||||
|
- transcript.ts unused imports were present in original source - cleaning them is optional but improves code hygiene
|
||||||
|
- config.ts uses Bun.file() for async file reading - compatible with oh-my-opencode's Bun runtime
|
||||||
|
- Bun.file().text() automatically handles encoding
|
||||||
|
|
||||||
|
소요 시간: ~3분
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|||||||
100
src/hooks/claude-code-hooks/config.ts
Normal file
100
src/hooks/claude-code-hooks/config.ts
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import { homedir } from "os"
|
||||||
|
import { join } from "path"
|
||||||
|
import { existsSync } from "fs"
|
||||||
|
import type { ClaudeHooksConfig, HookMatcher, HookCommand } from "./types"
|
||||||
|
|
||||||
|
interface RawHookMatcher {
|
||||||
|
matcher?: string
|
||||||
|
pattern?: string
|
||||||
|
hooks: HookCommand[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface RawClaudeHooksConfig {
|
||||||
|
PreToolUse?: RawHookMatcher[]
|
||||||
|
PostToolUse?: RawHookMatcher[]
|
||||||
|
UserPromptSubmit?: RawHookMatcher[]
|
||||||
|
Stop?: RawHookMatcher[]
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeHookMatcher(raw: RawHookMatcher): HookMatcher {
|
||||||
|
return {
|
||||||
|
matcher: raw.matcher ?? raw.pattern ?? "*",
|
||||||
|
hooks: raw.hooks,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeHooksConfig(raw: RawClaudeHooksConfig): ClaudeHooksConfig {
|
||||||
|
const result: ClaudeHooksConfig = {}
|
||||||
|
const eventTypes: (keyof RawClaudeHooksConfig)[] = [
|
||||||
|
"PreToolUse",
|
||||||
|
"PostToolUse",
|
||||||
|
"UserPromptSubmit",
|
||||||
|
"Stop",
|
||||||
|
]
|
||||||
|
|
||||||
|
for (const eventType of eventTypes) {
|
||||||
|
if (raw[eventType]) {
|
||||||
|
result[eventType] = raw[eventType].map(normalizeHookMatcher)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getClaudeSettingsPaths(customPath?: string): string[] {
|
||||||
|
const home = homedir()
|
||||||
|
const paths = [
|
||||||
|
join(home, ".claude", "settings.json"),
|
||||||
|
join(process.cwd(), ".claude", "settings.json"),
|
||||||
|
join(process.cwd(), ".claude", "settings.local.json"),
|
||||||
|
]
|
||||||
|
|
||||||
|
if (customPath && existsSync(customPath)) {
|
||||||
|
paths.unshift(customPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
return paths
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeHooksConfig(
|
||||||
|
base: ClaudeHooksConfig,
|
||||||
|
override: ClaudeHooksConfig
|
||||||
|
): ClaudeHooksConfig {
|
||||||
|
const result: ClaudeHooksConfig = { ...base }
|
||||||
|
const eventTypes: (keyof ClaudeHooksConfig)[] = [
|
||||||
|
"PreToolUse",
|
||||||
|
"PostToolUse",
|
||||||
|
"UserPromptSubmit",
|
||||||
|
"Stop",
|
||||||
|
]
|
||||||
|
for (const eventType of eventTypes) {
|
||||||
|
if (override[eventType]) {
|
||||||
|
result[eventType] = [...(base[eventType] || []), ...override[eventType]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function loadClaudeHooksConfig(
|
||||||
|
customSettingsPath?: string
|
||||||
|
): Promise<ClaudeHooksConfig | null> {
|
||||||
|
const paths = getClaudeSettingsPaths(customSettingsPath)
|
||||||
|
let mergedConfig: ClaudeHooksConfig = {}
|
||||||
|
|
||||||
|
for (const settingsPath of paths) {
|
||||||
|
if (existsSync(settingsPath)) {
|
||||||
|
try {
|
||||||
|
const content = await Bun.file(settingsPath).text()
|
||||||
|
const settings = JSON.parse(content) as { hooks?: RawClaudeHooksConfig }
|
||||||
|
if (settings.hooks) {
|
||||||
|
const normalizedHooks = normalizeHooksConfig(settings.hooks)
|
||||||
|
mergedConfig = mergeHooksConfig(mergedConfig, normalizedHooks)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.keys(mergedConfig).length > 0 ? mergedConfig : null
|
||||||
|
}
|
||||||
76
src/hooks/claude-code-hooks/todo.ts
Normal file
76
src/hooks/claude-code-hooks/todo.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import { join } from "path"
|
||||||
|
import { mkdirSync, writeFileSync, readFileSync, existsSync, unlinkSync } from "fs"
|
||||||
|
import { homedir } from "os"
|
||||||
|
import type { TodoFile, TodoItem, ClaudeCodeTodoItem } from "./types"
|
||||||
|
|
||||||
|
const TODO_DIR = join(homedir(), ".claude", "todos")
|
||||||
|
|
||||||
|
export function getTodoPath(sessionId: string): string {
|
||||||
|
return join(TODO_DIR, `${sessionId}-agent-${sessionId}.json`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureTodoDir(): void {
|
||||||
|
if (!existsSync(TODO_DIR)) {
|
||||||
|
mkdirSync(TODO_DIR, { recursive: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface OpenCodeTodo {
|
||||||
|
content: string
|
||||||
|
status: string
|
||||||
|
priority: string
|
||||||
|
id: string
|
||||||
|
}
|
||||||
|
|
||||||
|
function toClaudeCodeFormat(item: OpenCodeTodo | TodoItem): ClaudeCodeTodoItem {
|
||||||
|
return {
|
||||||
|
content: item.content,
|
||||||
|
status: item.status === "cancelled" ? "completed" : item.status,
|
||||||
|
activeForm: item.content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function loadTodoFile(sessionId: string): TodoFile | null {
|
||||||
|
const path = getTodoPath(sessionId)
|
||||||
|
if (!existsSync(path)) return null
|
||||||
|
try {
|
||||||
|
const content = JSON.parse(readFileSync(path, "utf-8"))
|
||||||
|
if (Array.isArray(content)) {
|
||||||
|
return {
|
||||||
|
session_id: sessionId,
|
||||||
|
items: content.map((item: ClaudeCodeTodoItem, idx: number) => ({
|
||||||
|
id: String(idx),
|
||||||
|
content: item.content,
|
||||||
|
status: item.status as TodoItem["status"],
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
})),
|
||||||
|
created_at: new Date().toISOString(),
|
||||||
|
updated_at: new Date().toISOString(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return content
|
||||||
|
} catch {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveTodoFile(sessionId: string, file: TodoFile): void {
|
||||||
|
ensureTodoDir()
|
||||||
|
const path = getTodoPath(sessionId)
|
||||||
|
const claudeCodeFormat: ClaudeCodeTodoItem[] = file.items.map(toClaudeCodeFormat)
|
||||||
|
writeFileSync(path, JSON.stringify(claudeCodeFormat, null, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveOpenCodeTodos(sessionId: string, todos: OpenCodeTodo[]): void {
|
||||||
|
ensureTodoDir()
|
||||||
|
const path = getTodoPath(sessionId)
|
||||||
|
const claudeCodeFormat: ClaudeCodeTodoItem[] = todos.map(toClaudeCodeFormat)
|
||||||
|
writeFileSync(path, JSON.stringify(claudeCodeFormat, null, 2))
|
||||||
|
}
|
||||||
|
|
||||||
|
export function deleteTodoFile(sessionId: string): void {
|
||||||
|
const path = getTodoPath(sessionId)
|
||||||
|
if (existsSync(path)) {
|
||||||
|
unlinkSync(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
255
src/hooks/claude-code-hooks/transcript.ts
Normal file
255
src/hooks/claude-code-hooks/transcript.ts
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
/**
|
||||||
|
* Transcript Manager
|
||||||
|
* Creates and manages Claude Code compatible transcript files
|
||||||
|
*/
|
||||||
|
import { join } from "path"
|
||||||
|
import { mkdirSync, appendFileSync, existsSync, writeFileSync, unlinkSync } from "fs"
|
||||||
|
import { homedir, tmpdir } from "os"
|
||||||
|
import { randomUUID } from "crypto"
|
||||||
|
import type { TranscriptEntry } from "./types"
|
||||||
|
import { transformToolName } from "../../shared/tool-name"
|
||||||
|
|
||||||
|
const TRANSCRIPT_DIR = join(homedir(), ".claude", "transcripts")
|
||||||
|
|
||||||
|
export function getTranscriptPath(sessionId: string): string {
|
||||||
|
return join(TRANSCRIPT_DIR, `${sessionId}.jsonl`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ensureTranscriptDir(): void {
|
||||||
|
if (!existsSync(TRANSCRIPT_DIR)) {
|
||||||
|
mkdirSync(TRANSCRIPT_DIR, { recursive: true })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function appendTranscriptEntry(
|
||||||
|
sessionId: string,
|
||||||
|
entry: TranscriptEntry
|
||||||
|
): void {
|
||||||
|
ensureTranscriptDir()
|
||||||
|
const path = getTranscriptPath(sessionId)
|
||||||
|
const line = JSON.stringify(entry) + "\n"
|
||||||
|
appendFileSync(path, line)
|
||||||
|
}
|
||||||
|
|
||||||
|
export function recordToolUse(
|
||||||
|
sessionId: string,
|
||||||
|
toolName: string,
|
||||||
|
toolInput: Record<string, unknown>
|
||||||
|
): void {
|
||||||
|
appendTranscriptEntry(sessionId, {
|
||||||
|
type: "tool_use",
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
tool_name: toolName,
|
||||||
|
tool_input: toolInput,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function recordToolResult(
|
||||||
|
sessionId: string,
|
||||||
|
toolName: string,
|
||||||
|
toolInput: Record<string, unknown>,
|
||||||
|
toolOutput: Record<string, unknown>
|
||||||
|
): void {
|
||||||
|
appendTranscriptEntry(sessionId, {
|
||||||
|
type: "tool_result",
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
tool_name: toolName,
|
||||||
|
tool_input: toolInput,
|
||||||
|
tool_output: toolOutput,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function recordUserMessage(
|
||||||
|
sessionId: string,
|
||||||
|
content: string
|
||||||
|
): void {
|
||||||
|
appendTranscriptEntry(sessionId, {
|
||||||
|
type: "user",
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
content,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export function recordAssistantMessage(
|
||||||
|
sessionId: string,
|
||||||
|
content: string
|
||||||
|
): void {
|
||||||
|
appendTranscriptEntry(sessionId, {
|
||||||
|
type: "assistant",
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
content,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================================================
|
||||||
|
// Claude Code Compatible Transcript Builder (PORT FROM DISABLED)
|
||||||
|
// ============================================================================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenCode API response type (loosely typed)
|
||||||
|
*/
|
||||||
|
interface OpenCodeMessagePart {
|
||||||
|
type: string
|
||||||
|
tool?: string
|
||||||
|
state?: {
|
||||||
|
status?: string
|
||||||
|
input?: Record<string, unknown>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OpenCodeMessage {
|
||||||
|
info?: {
|
||||||
|
role?: string
|
||||||
|
}
|
||||||
|
parts?: OpenCodeMessagePart[]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Claude Code compatible transcript entry (from disabled file)
|
||||||
|
*/
|
||||||
|
interface DisabledTranscriptEntry {
|
||||||
|
type: "assistant"
|
||||||
|
message: {
|
||||||
|
role: "assistant"
|
||||||
|
content: Array<{
|
||||||
|
type: "tool_use"
|
||||||
|
name: string
|
||||||
|
input: Record<string, unknown>
|
||||||
|
}>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build Claude Code compatible transcript from session messages
|
||||||
|
*
|
||||||
|
* PORT FROM DISABLED: This calls client.session.messages() API to fetch
|
||||||
|
* the full session history and builds a JSONL file in Claude Code format.
|
||||||
|
*
|
||||||
|
* @param client OpenCode client instance
|
||||||
|
* @param sessionId Session ID
|
||||||
|
* @param directory Working directory
|
||||||
|
* @param currentToolName Current tool being executed (added as last entry)
|
||||||
|
* @param currentToolInput Current tool input
|
||||||
|
* @returns Temp file path (caller must call deleteTempTranscript!)
|
||||||
|
*/
|
||||||
|
export async function buildTranscriptFromSession(
|
||||||
|
client: {
|
||||||
|
session: {
|
||||||
|
messages: (opts: { path: { id: string }; query?: { directory: string } }) => Promise<unknown>
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sessionId: string,
|
||||||
|
directory: string,
|
||||||
|
currentToolName: string,
|
||||||
|
currentToolInput: Record<string, unknown>
|
||||||
|
): Promise<string | null> {
|
||||||
|
try {
|
||||||
|
const response = await client.session.messages({
|
||||||
|
path: { id: sessionId },
|
||||||
|
query: { directory },
|
||||||
|
})
|
||||||
|
|
||||||
|
// Handle various response formats
|
||||||
|
const messages = (response as { "200"?: unknown[]; data?: unknown[] })["200"]
|
||||||
|
?? (response as { data?: unknown[] }).data
|
||||||
|
?? (Array.isArray(response) ? response : [])
|
||||||
|
|
||||||
|
const entries: string[] = []
|
||||||
|
|
||||||
|
if (Array.isArray(messages)) {
|
||||||
|
for (const msg of messages as OpenCodeMessage[]) {
|
||||||
|
if (msg.info?.role !== "assistant") continue
|
||||||
|
|
||||||
|
for (const part of msg.parts || []) {
|
||||||
|
if (part.type !== "tool") continue
|
||||||
|
if (part.state?.status !== "completed") continue
|
||||||
|
if (!part.state?.input) continue
|
||||||
|
|
||||||
|
const rawToolName = part.tool as string
|
||||||
|
const toolName = transformToolName(rawToolName)
|
||||||
|
|
||||||
|
const entry: DisabledTranscriptEntry = {
|
||||||
|
type: "assistant",
|
||||||
|
message: {
|
||||||
|
role: "assistant",
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "tool_use",
|
||||||
|
name: toolName,
|
||||||
|
input: part.state.input,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
entries.push(JSON.stringify(entry))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always add current tool call as the last entry
|
||||||
|
const currentEntry: DisabledTranscriptEntry = {
|
||||||
|
type: "assistant",
|
||||||
|
message: {
|
||||||
|
role: "assistant",
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "tool_use",
|
||||||
|
name: transformToolName(currentToolName),
|
||||||
|
input: currentToolInput,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
entries.push(JSON.stringify(currentEntry))
|
||||||
|
|
||||||
|
// Write to temp file
|
||||||
|
const tempPath = join(
|
||||||
|
tmpdir(),
|
||||||
|
`opencode-transcript-${sessionId}-${randomUUID()}.jsonl`
|
||||||
|
)
|
||||||
|
writeFileSync(tempPath, entries.join("\n") + "\n")
|
||||||
|
|
||||||
|
return tempPath
|
||||||
|
} catch {
|
||||||
|
// CRITICAL FIX: Even on API failure, create file with current tool entry only
|
||||||
|
// (matching original disabled behavior - never return null with incompatible format)
|
||||||
|
try {
|
||||||
|
const currentEntry: DisabledTranscriptEntry = {
|
||||||
|
type: "assistant",
|
||||||
|
message: {
|
||||||
|
role: "assistant",
|
||||||
|
content: [
|
||||||
|
{
|
||||||
|
type: "tool_use",
|
||||||
|
name: transformToolName(currentToolName),
|
||||||
|
input: currentToolInput,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
const tempPath = join(
|
||||||
|
tmpdir(),
|
||||||
|
`opencode-transcript-${sessionId}-${randomUUID()}.jsonl`
|
||||||
|
)
|
||||||
|
writeFileSync(tempPath, JSON.stringify(currentEntry) + "\n")
|
||||||
|
return tempPath
|
||||||
|
} catch {
|
||||||
|
// If even this fails, return null (truly catastrophic failure)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete temp transcript file (call in finally block)
|
||||||
|
*
|
||||||
|
* PORT FROM DISABLED: Cleanup mechanism to avoid disk accumulation
|
||||||
|
*/
|
||||||
|
export function deleteTempTranscript(path: string | null): void {
|
||||||
|
if (!path) return
|
||||||
|
try {
|
||||||
|
unlinkSync(path)
|
||||||
|
} catch {
|
||||||
|
// Ignore deletion errors
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user