2026-02-13 11:25:40 +09:00
|
|
|
import { existsSync, readFileSync } from "node:fs"
|
|
|
|
|
import { homedir } from "node:os"
|
|
|
|
|
import { isAbsolute, resolve } from "node:path"
|
2026-04-02 14:55:35 +09:00
|
|
|
import { isWithinProject } from "../../shared/contains-path"
|
|
|
|
|
import { log } from "../../shared/logger"
|
2026-02-13 11:25:40 +09:00
|
|
|
|
|
|
|
|
export function resolvePromptAppend(promptAppend: string, configDir?: string): string {
|
|
|
|
|
if (!promptAppend.startsWith("file://")) return promptAppend
|
|
|
|
|
|
|
|
|
|
const encoded = promptAppend.slice(7)
|
|
|
|
|
|
|
|
|
|
let filePath: string
|
|
|
|
|
try {
|
|
|
|
|
const decoded = decodeURIComponent(encoded)
|
|
|
|
|
const expanded = decoded.startsWith("~/") ? decoded.replace(/^~\//, `${homedir()}/`) : decoded
|
|
|
|
|
filePath = isAbsolute(expanded)
|
|
|
|
|
? expanded
|
|
|
|
|
: resolve(configDir ?? process.cwd(), expanded)
|
|
|
|
|
} catch {
|
|
|
|
|
return `[WARNING: Malformed file URI (invalid percent-encoding): ${promptAppend}]`
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-02 14:55:35 +09:00
|
|
|
const projectRoot = configDir ?? process.cwd()
|
|
|
|
|
if (!isWithinProject(filePath, projectRoot)) {
|
|
|
|
|
log("[resolve-file-uri] Rejected file URI outside project root", {
|
|
|
|
|
promptAppend,
|
|
|
|
|
filePath,
|
|
|
|
|
projectRoot,
|
|
|
|
|
})
|
|
|
|
|
return `[WARNING: Path rejected: ${promptAppend}]`
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-13 11:25:40 +09:00
|
|
|
if (!existsSync(filePath)) {
|
|
|
|
|
return `[WARNING: Could not resolve file URI: ${promptAppend}]`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
return readFileSync(filePath, "utf8")
|
|
|
|
|
} catch {
|
|
|
|
|
return `[WARNING: Could not read file: ${promptAppend}]`
|
|
|
|
|
}
|
|
|
|
|
}
|