@narumitw/pi-files 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/LICENSE +21 -0
  3. package/README.md +102 -2
  4. package/dist/chunks/chunk-3GQ7VS22.ts +273 -0
  5. package/dist/chunks/chunk-3GQ7VS22.ts.map +7 -0
  6. package/dist/chunks/chunk-AXW73HDJ.ts +31 -0
  7. package/dist/chunks/chunk-AXW73HDJ.ts.map +7 -0
  8. package/dist/chunks/chunk-F5QFC255.ts +101 -0
  9. package/dist/chunks/chunk-F5QFC255.ts.map +7 -0
  10. package/dist/chunks/chunk-NFNGJETD.ts +77 -0
  11. package/dist/chunks/chunk-NFNGJETD.ts.map +7 -0
  12. package/dist/chunks/chunk-OPGTWNJI.ts +258 -0
  13. package/dist/chunks/chunk-OPGTWNJI.ts.map +7 -0
  14. package/dist/chunks/chunk-WOTDNRTG.ts +242 -0
  15. package/dist/chunks/chunk-WOTDNRTG.ts.map +7 -0
  16. package/dist/chunks/file-context-explorer-WR6GQZHF.ts +1793 -0
  17. package/dist/chunks/file-context-explorer-WR6GQZHF.ts.map +7 -0
  18. package/dist/chunks/file-context-menu-VPLHUUQ6.ts +302 -0
  19. package/dist/chunks/file-context-menu-VPLHUUQ6.ts.map +7 -0
  20. package/dist/chunks/files-menu-MES4MQ4F.ts +119 -0
  21. package/dist/chunks/files-menu-MES4MQ4F.ts.map +7 -0
  22. package/dist/chunks/files-settings-NYXQU2HV.ts +295 -0
  23. package/dist/chunks/files-settings-NYXQU2HV.ts.map +7 -0
  24. package/dist/chunks/menu-P6IBMROH.ts +876 -0
  25. package/dist/chunks/menu-P6IBMROH.ts.map +7 -0
  26. package/dist/index.ts +1789 -0
  27. package/dist/index.ts.map +7 -0
  28. package/docs/edit-scope-reference.md +142 -0
  29. package/docs/file-context-reference.md +91 -0
  30. package/docs/migration.md +105 -0
  31. package/docs/verification.md +99 -0
  32. package/package.json +58 -4
  33. package/src/content-search-session.ts +204 -0
  34. package/src/content-search-ui.ts +159 -0
  35. package/src/content-search.ts +170 -0
  36. package/src/context-service.ts +515 -0
  37. package/src/external-editor.ts +143 -0
  38. package/src/file-browser-ui.ts +128 -0
  39. package/src/file-browser.ts +76 -0
  40. package/src/file-context-explorer.ts +992 -0
  41. package/src/file-context-menu.ts +360 -0
  42. package/src/file-context-preview-ui.ts +354 -0
  43. package/src/file-context-settings.ts +301 -0
  44. package/src/file-context.ts +321 -0
  45. package/src/file-search.ts +246 -0
  46. package/src/files-menu.ts +125 -0
  47. package/src/files-settings.ts +314 -0
  48. package/src/files-widget.ts +65 -0
  49. package/src/files.ts +148 -0
  50. package/src/git-context.ts +485 -0
  51. package/src/index.ts +1 -0
  52. package/src/scope/contracts.ts +243 -0
  53. package/src/scope/display.ts +27 -0
  54. package/src/scope/edit-scope.ts +435 -0
  55. package/src/scope/files.ts +118 -0
  56. package/src/scope/input.ts +207 -0
  57. package/src/scope/menu.ts +277 -0
  58. package/src/scope/paths.ts +142 -0
  59. package/src/scope/picker-keys.ts +154 -0
  60. package/src/scope/picker.ts +484 -0
  61. package/src/scope/policy.ts +55 -0
  62. package/src/scope/settings.ts +138 -0
  63. package/src/scope/state.ts +91 -0
  64. package/src/scope/ui.ts +106 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/external-editor.ts", "../src/files-widget.ts", "../src/git-context.ts", "../src/context-service.ts", "../src/scope/edit-scope.ts", "../src/scope/contracts.ts", "../src/scope/policy.ts", "../src/scope/settings.ts", "../src/files.ts"],
4
+ "sourcesContent": ["import { type ChildProcess, spawn } from \"node:child_process\";\nimport { lstat, realpath } from \"node:fs/promises\";\nimport { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { getAgentDir, SettingsManager, withFileMutationQueue } from \"@earendil-works/pi-coding-agent\";\nimport type { TUI } from \"@earendil-works/pi-tui\";\n\nexport interface ExternalEditorProcess {\n readonly killed: boolean;\n kill(): boolean;\n once(event: \"close\", listener: (code: number | null) => void): this;\n once(event: \"error\", listener: (error: Error) => void): this;\n}\n\nexport type SpawnExternalEditorProcess = (command: string, args: readonly string[]) => ExternalEditorProcess;\n\nexport interface EditProjectFileInExternalEditorOptions {\n root: string;\n projectPath: string;\n tui: TUI;\n projectTrusted: boolean;\n signal?: AbortSignal;\n isCurrent?: () => boolean;\n agentDir?: string;\n command?: string;\n spawnProcess?: SpawnExternalEditorProcess;\n}\n\nexport async function editProjectFileInExternalEditor(options: EditProjectFileInExternalEditorOptions): Promise<void> {\n options.signal?.throwIfAborted();\n const target = await resolveEditableProjectFile(options.root, options.projectPath, options.signal);\n options.signal?.throwIfAborted();\n const command =\n options.command ??\n SettingsManager.create(options.root, options.agentDir ?? getAgentDir(), {\n projectTrusted: options.projectTrusted,\n }).getExternalEditorCommand();\n const [editor, ...editorArgs] = command.trim().split(/\\s+/u);\n if (!editor) throw new Error(\"External editor command is empty\");\n assertEditorTargetSafeForPlatform(target);\n\n await withFileMutationQueue(target, async () => {\n options.signal?.throwIfAborted();\n let stopped = false;\n try {\n options.tui.stop();\n stopped = true;\n process.stdout.write(\"Launching external editor. Pi will resume when the editor exits.\\n\");\n await runExternalEditorProcess(editor, [...editorArgs, target], options.signal, options.spawnProcess);\n options.signal?.throwIfAborted();\n } finally {\n if (stopped && !options.signal?.aborted && (options.isCurrent === undefined || options.isCurrent())) {\n options.tui.start();\n options.tui.requestRender(true);\n }\n }\n });\n}\n\nexport async function resolveEditableProjectFile(\n root: string,\n projectPath: string,\n signal?: AbortSignal,\n): Promise<string> {\n signal?.throwIfAborted();\n if (!projectPath || isAbsolute(projectPath) || projectPath.includes(\"\\0\")) {\n throw new Error(\"File path is outside the project\");\n }\n const canonicalRoot = await realpath(root);\n signal?.throwIfAborted();\n const candidate = resolve(canonicalRoot, projectPath);\n if (!isInside(canonicalRoot, candidate)) throw new Error(\"File path is outside the project\");\n const candidateInfo = await lstat(candidate);\n signal?.throwIfAborted();\n if (candidateInfo.isSymbolicLink()) throw new Error(`${projectPath} is a symbolic link`);\n const canonicalFile = await realpath(candidate);\n signal?.throwIfAborted();\n if (!isInside(canonicalRoot, canonicalFile)) throw new Error(\"File path is outside the project\");\n const info = await lstat(canonicalFile);\n signal?.throwIfAborted();\n if (!info.isFile()) throw new Error(`${projectPath} is not a regular file`);\n return canonicalFile;\n}\n\nasync function runExternalEditorProcess(\n command: string,\n args: readonly string[],\n signal: AbortSignal | undefined,\n spawnProcess: SpawnExternalEditorProcess = defaultSpawnExternalEditorProcess,\n): Promise<void> {\n signal?.throwIfAborted();\n const child = spawnProcess(command, args);\n await new Promise<void>((resolvePromise, reject) => {\n let settled = false;\n let aborted = signal?.aborted ?? false;\n const finish = (error?: Error) => {\n if (settled) return;\n settled = true;\n signal?.removeEventListener(\"abort\", abort);\n if (error) reject(error);\n else resolvePromise();\n };\n const abort = () => {\n aborted = true;\n if (!child.killed) child.kill();\n };\n child.once(\"error\", (error) => finish(aborted ? abortError() : error));\n child.once(\"close\", (code) => {\n if (aborted) {\n finish(abortError());\n return;\n }\n if (code === 0) {\n finish();\n return;\n }\n finish(new Error(`External editor exited with code ${code ?? \"unknown\"}`));\n });\n signal?.addEventListener(\"abort\", abort, { once: true });\n if (signal?.aborted) abort();\n });\n}\n\nexport function assertEditorTargetSafeForPlatform(target: string, platform: NodeJS.Platform = process.platform): void {\n if (platform === \"win32\" && /[\\r\\n\"&|<>^()%!]/u.test(target)) {\n throw new Error(\"File path contains characters unsafe for the Windows external-editor shell\");\n }\n}\n\nfunction defaultSpawnExternalEditorProcess(command: string, args: readonly string[]): ChildProcess {\n return spawn(command, [...args], {\n stdio: \"inherit\",\n shell: process.platform === \"win32\",\n });\n}\n\nfunction isInside(root: string, candidate: string): boolean {\n const result = relative(root, candidate);\n return result === \"\" || (!result.startsWith(`..${sep}`) && result !== \"..\" && !isAbsolute(result));\n}\n\nfunction abortError(): DOMException {\n return new DOMException(\"External editor cancelled\", \"AbortError\");\n}\n", "import type { ExtensionContext, Theme } from \"@earendil-works/pi-coding-agent\";\nimport { wrapTextWithAnsi } from \"@earendil-works/pi-tui\";\nimport { EditorStatusWidget } from \"@narumitw/pi-tui-kit/editor-status-widget\";\nimport { sanitizeTerminalText } from \"@narumitw/pi-tui-kit/terminal-text\";\nimport type { FileQuote } from \"./file-context.js\";\nimport { displayPath } from \"./scope/display.js\";\nimport type { ScopeView } from \"./scope/menu.js\";\n\n/** One owned widget; context summaries never replace an active restriction. */\nexport function publishFilesWidget(ctx: ExtensionContext, quotes: readonly FileQuote[], scope?: ScopeView): void {\n if (!ctx.hasUI) return;\n if (!quotes.length && (!scope || scope.state === \"inactive\")) {\n ctx.ui.setWidget(\"files\", undefined);\n return;\n }\n const totalBytes = quotes.reduce((total, quote) => total + Buffer.byteLength(quote.text, \"utf8\"), 0);\n const lines: Array<{ role: \"accent\" | \"muted\" | \"error\"; text: string }> = [];\n if (scope && scope.state !== \"inactive\") {\n lines.push({\n role: scope.state === \"blocked\" ? \"error\" : \"accent\",\n text: `${scope.state === \"blocked\" ? \"Edit Scope unavailable \u00B7 edits blocked\" : `Editing limited to ${scope.targets.length} file${scope.targets.length === 1 ? \"\" : \"s\"}`} \u00B7 /files off`,\n });\n lines.push({\n role: \"muted\",\n text: `${scope.targets\n .slice(0, 2)\n .map((target) => displayPath(target.path))\n .join(\" \u00B7 \")}${scope.targets.length > 2 ? \" \u00B7 \u2026\" : \"\"}`,\n });\n }\n if (quotes.length) {\n lines.push({\n role: \"accent\",\n text: `Next prompt context \u00B7 ${quotes.length} ${quotes.length === 1 ? \"snippet\" : \"snippets\"} \u00B7 ~${Math.max(1, Math.ceil(totalBytes / 4))} tokens \u00B7 /file-context to review`,\n });\n lines.push(\n ...quotes.map((quote, index) => ({\n role: \"muted\" as const,\n text: `${index + 1}. ${sanitizeTerminalText(escapeControls(quote.path))} \u00B7 lines ${quote.startLine}-${quote.endLine} \u00B7 ~${Math.max(1, Math.ceil(Buffer.byteLength(quote.text, \"utf8\") / 4))} tokens`,\n })),\n );\n }\n const render = (theme: Pick<Theme, \"fg\">) => lines.map((line) => theme.fg(line.role, line.text));\n if (ctx.mode !== \"tui\") {\n ctx.ui.setWidget(\"files\", render(ctx.ui.theme));\n return;\n }\n ctx.ui.setWidget(\n \"files\",\n (_tui, theme) =>\n new EditorStatusWidget({\n theme,\n renderBody: (width) =>\n width === 0 ? lines.map(() => \"\") : render(theme).flatMap((line) => wrapTextWithAnsi(line, width)),\n }),\n );\n}\nfunction escapeControls(text: string): string {\n return [...text]\n .map((character) => {\n const code = character.charCodeAt(0);\n return code <= 31 || (code >= 127 && code <= 159) ? `\\\\x${code.toString(16).padStart(2, \"0\")}` : character;\n })\n .join(\"\");\n}\n", "import { execFile } from \"node:child_process\";\nimport { realpath } from \"node:fs/promises\";\nimport { isAbsolute, relative, resolve, sep } from \"node:path\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\nconst GIT_TIMEOUT_MS = 5_000;\nconst GIT_MAX_BUFFER = 1_100_000;\nconst MAX_HISTORY_ENTRIES = 20;\n\nexport interface GitProjectInfo {\n repositoryRoot: string;\n projectPrefix: string;\n branch: string;\n head: string;\n dirty: boolean;\n}\n\nexport interface GitFileStatus {\n code: string;\n label: string;\n staged: boolean;\n unstaged: boolean;\n untracked: boolean;\n ignored: boolean;\n conflicted: boolean;\n}\n\nexport interface GitDiffHunk {\n header: string;\n oldStart: number;\n oldCount: number;\n newStart: number;\n newCount: number;\n lines: string[];\n changedLines: number[];\n}\n\nexport interface GitFileContext {\n status: GitFileStatus | undefined;\n blob: string | undefined;\n hunks: GitDiffHunk[];\n}\n\nexport interface GitBlameInfo {\n commit: string;\n author: string;\n authorTime: number | undefined;\n summary: string;\n committed: boolean;\n}\n\nexport interface GitHistoryEntry {\n commit: string;\n author: string;\n authorTime: number;\n summary: string;\n path: string;\n}\n\nexport interface GitRevisionFile {\n path: string;\n lines: string[];\n revision: string;\n commit: string;\n blob: string | undefined;\n}\n\nexport class GitContext {\n private readonly statusMap: Map<string, GitFileStatus>;\n private readonly work = new Set<Promise<GitResult>>();\n\n constructor(\n readonly projectRoot: string,\n readonly project: GitProjectInfo,\n statuses: ReadonlyMap<string, GitFileStatus>,\n ) {\n this.statusMap = new Map(statuses);\n }\n\n get statuses(): ReadonlyMap<string, GitFileStatus> {\n return this.statusMap;\n }\n\n async getFileContext(projectPath: string, signal?: AbortSignal): Promise<GitFileContext> {\n this.assertProjectPath(projectPath);\n const [blobResult, diffResult] = await Promise.all([\n this.run([\"rev-parse\", \"--verify\", `HEAD:${this.repositoryPath(projectPath)}`], true, signal),\n this.run([\"diff\", \"--no-ext-diff\", \"--no-textconv\", \"--unified=3\", \"HEAD\", \"--\", projectPath], true, signal),\n ]);\n signal?.throwIfAborted();\n return {\n status: this.statusMap.get(projectPath),\n blob: blobResult.ok ? blobResult.stdout.trim() || undefined : undefined,\n hunks: diffResult.ok ? parseUnifiedDiff(diffResult.stdout) : [],\n };\n }\n\n async refreshFileContext(projectPath: string, signal?: AbortSignal): Promise<GitFileContext> {\n const [fileContext, statusResult] = await Promise.all([\n this.getFileContext(projectPath, signal),\n this.run(\n [\n \"-c\",\n \"status.relativePaths=true\",\n \"status\",\n \"--porcelain=v1\",\n \"-z\",\n \"--untracked-files=all\",\n \"--ignored=traditional\",\n \"--\",\n \".\",\n ],\n true,\n signal,\n ),\n ]);\n signal?.throwIfAborted();\n if (!statusResult.ok) return fileContext;\n const statuses = parseStatuses(statusResult.stdout, this.project.projectPrefix);\n this.statusMap.clear();\n for (const [path, status] of statuses) this.statusMap.set(path, status);\n this.project.dirty = [...statuses.values()].some((status) => !status.ignored);\n return { ...fileContext, status: this.statusMap.get(projectPath) };\n }\n\n async getBlame(\n projectPath: string,\n line: number,\n revision?: string,\n signal?: AbortSignal,\n ): Promise<GitBlameInfo | undefined> {\n this.assertProjectPath(projectPath);\n if (!Number.isSafeInteger(line) || line < 1) throw new Error(\"Blame line must be positive\");\n if (revision && !/^[0-9a-f]{40}$/i.test(revision)) throw new Error(\"Invalid blame revision\");\n const result = await this.run(\n [\n \"blame\",\n \"--no-textconv\",\n \"--line-porcelain\",\n \"-L\",\n `${line},${line}`,\n ...(revision ? [revision] : []),\n \"--\",\n projectPath,\n ],\n true,\n signal,\n );\n signal?.throwIfAborted();\n if (!result.ok) return undefined;\n return parseBlame(result.stdout);\n }\n\n async getHistory(projectPath: string, signal?: AbortSignal): Promise<GitHistoryEntry[]> {\n this.assertProjectPath(projectPath);\n const result = await this.run(\n [\n \"log\",\n `--max-count=${MAX_HISTORY_ENTRIES}`,\n \"--format=%x1e%H%x1f%an%x1f%at%x1f%s\",\n \"--name-only\",\n \"-z\",\n \"--follow\",\n \"--\",\n projectPath,\n ],\n true,\n signal,\n );\n signal?.throwIfAborted();\n return result.ok ? parseHistory(result.stdout) : [];\n }\n\n async loadRevision(\n projectPath: string,\n revision: string,\n historicalPath?: string,\n signal?: AbortSignal,\n ): Promise<GitRevisionFile> {\n this.assertProjectPath(projectPath);\n const normalizedRevision = revision.trim();\n if (!normalizedRevision || /[\\0\\r\\n]/.test(normalizedRevision)) {\n throw new Error(\"Revision is required\");\n }\n const resolved = await this.run(\n [\"rev-parse\", \"--verify\", \"--end-of-options\", `${normalizedRevision}^{commit}`],\n true,\n signal,\n );\n if (!resolved.ok || !/^[0-9a-f]{40}$/i.test(resolved.stdout.trim())) {\n throw new Error(`Unknown Git revision: ${normalizedRevision}`);\n }\n signal?.throwIfAborted();\n const commit = resolved.stdout.trim().toLowerCase();\n const repositoryPath = historicalPath\n ? this.assertRepositoryPath(historicalPath)\n : this.repositoryPath(projectPath);\n const [contentsResult, blobResult] = await Promise.all([\n this.run([\"show\", `${commit}:${repositoryPath}`], true, signal),\n this.run([\"rev-parse\", \"--verify\", `${commit}:${repositoryPath}`], true, signal),\n ]);\n signal?.throwIfAborted();\n if (!contentsResult.ok) {\n throw new Error(`${projectPath} does not exist at ${normalizedRevision}`);\n }\n if (Buffer.byteLength(contentsResult.stdout, \"utf8\") > 1_000_000) {\n throw new Error(`${projectPath} exceeds 1000000 bytes at ${normalizedRevision}`);\n }\n if (contentsResult.stdout.includes(\"\\0\")) {\n throw new Error(`${projectPath} appears to be binary at ${normalizedRevision}`);\n }\n return {\n path: historicalPath ?? projectPath,\n lines: normalizeLines(contentsResult.stdout),\n revision: normalizedRevision,\n commit,\n blob: blobResult.ok ? blobResult.stdout.trim() || undefined : undefined,\n };\n }\n\n /** Drain processes after the owning explorer cancels its request controllers. */\n async whenIdle(): Promise<void> {\n await Promise.allSettled([...this.work]);\n }\n\n private run(args: string[], allowFailure = false, signal?: AbortSignal): Promise<GitResult> {\n const work = runGit(this.projectRoot, args, allowFailure, signal);\n this.work.add(work);\n void work.then(\n () => this.work.delete(work),\n () => this.work.delete(work),\n );\n return work;\n }\n\n private repositoryPath(projectPath: string): string {\n return `${this.project.projectPrefix}${projectPath}`.replaceAll(\"\\\\\", \"/\");\n }\n\n private assertRepositoryPath(repositoryPath: string): string {\n const normalized = repositoryPath.replaceAll(\"\\\\\", \"/\");\n if (!normalized || isAbsolute(normalized) || normalized.includes(\"\\0\")) {\n throw new Error(\"Invalid historical Git path\");\n }\n const candidate = resolve(this.project.repositoryRoot, normalized);\n const result = relative(this.project.repositoryRoot, candidate);\n if (result === \"..\" || result.startsWith(`..${sep}`)) {\n throw new Error(\"Historical Git path is outside the repository\");\n }\n return normalized;\n }\n\n private assertProjectPath(projectPath: string): void {\n if (!projectPath || isAbsolute(projectPath) || projectPath.includes(\"\\0\")) {\n throw new Error(\"Invalid project path\");\n }\n const candidate = resolve(this.projectRoot, projectPath);\n const result = relative(this.projectRoot, candidate);\n if (result === \"..\" || result.startsWith(`..${sep}`)) {\n throw new Error(\"Git path is outside the project\");\n }\n }\n}\n\nexport async function createGitContext(root: string, signal?: AbortSignal): Promise<GitContext | undefined> {\n signal?.throwIfAborted();\n const projectRoot = await realpath(root);\n signal?.throwIfAborted();\n const repositoryResult = await runGit(projectRoot, [\"rev-parse\", \"--show-toplevel\"], true, signal);\n if (!repositoryResult.ok) return undefined;\n const repositoryRoot = stripLineEnding(repositoryResult.stdout);\n if (!repositoryRoot) return undefined;\n const [prefixResult, headResult, branchResult, statusResult] = await settleGitReads([\n runGit(projectRoot, [\"rev-parse\", \"--show-prefix\"], true, signal),\n runGit(projectRoot, [\"rev-parse\", \"--verify\", \"HEAD\"], true, signal),\n runGit(projectRoot, [\"symbolic-ref\", \"--quiet\", \"--short\", \"HEAD\"], true, signal),\n runGit(\n projectRoot,\n [\n \"-c\",\n \"status.relativePaths=true\",\n \"status\",\n \"--porcelain=v1\",\n \"-z\",\n \"--untracked-files=all\",\n \"--ignored=traditional\",\n \"--\",\n \".\",\n ],\n true,\n signal,\n ),\n ]);\n signal?.throwIfAborted();\n const head = headResult.ok ? headResult.stdout.trim().toLowerCase() : \"unborn\";\n const branch = branchResult.ok ? branchResult.stdout.trim() : head === \"unborn\" ? \"unborn\" : head.slice(0, 12);\n const projectPrefix = prefixResult.ok ? stripLineEnding(prefixResult.stdout) : \"\";\n const statuses = statusResult.ok ? parseStatuses(statusResult.stdout, projectPrefix) : new Map();\n return new GitContext(\n projectRoot,\n {\n repositoryRoot,\n projectPrefix,\n branch,\n head,\n dirty: [...statuses.values()].some((status) => !status.ignored),\n },\n statuses,\n );\n}\n\ninterface GitResult {\n ok: boolean;\n stdout: string;\n}\n\nasync function runGit(cwd: string, args: string[], allowFailure: boolean, signal?: AbortSignal): Promise<GitResult> {\n try {\n const task = execFileAsync(\"git\", [\"--no-pager\", \"-c\", \"core.pager=cat\", \"-c\", \"core.fsmonitor=false\", ...args], {\n cwd,\n encoding: \"utf8\",\n timeout: GIT_TIMEOUT_MS,\n maxBuffer: GIT_MAX_BUFFER,\n signal,\n env: {\n ...process.env,\n GIT_OPTIONAL_LOCKS: \"0\",\n GIT_PAGER: \"cat\",\n PAGER: \"cat\",\n },\n });\n const closed = new Promise<void>((resolve) => {\n task.child.once(\"close\", () => resolve());\n });\n let stdout: string;\n try {\n ({ stdout } = await task);\n } finally {\n await closed;\n }\n return { ok: true, stdout };\n } catch (error: unknown) {\n if (signal?.aborted) throw error;\n if (allowFailure) return { ok: false, stdout: \"\" };\n throw error;\n }\n}\n\nasync function settleGitReads(reads: Promise<GitResult>[]): Promise<GitResult[]> {\n const results = await Promise.allSettled(reads);\n return results.map((result) => {\n if (result.status === \"rejected\") throw result.reason;\n return result.value;\n });\n}\n\nfunction parseStatuses(output: string, projectPrefix: string): Map<string, GitFileStatus> {\n const statuses = new Map<string, GitFileStatus>();\n const records = output.split(\"\\0\");\n for (let index = 0; index < records.length; index += 1) {\n const record = records[index];\n if (!record || record.length < 4) continue;\n const code = record.slice(0, 2);\n const repositoryPath = record.slice(3).replaceAll(\"\\\\\", \"/\");\n const projectPath = projectPrefix\n ? repositoryPath.startsWith(projectPrefix)\n ? repositoryPath.slice(projectPrefix.length)\n : undefined\n : repositoryPath;\n if (projectPath) statuses.set(projectPath, statusFromCode(code));\n if (code.includes(\"R\") || code.includes(\"C\")) index += 1;\n }\n return new Map([...statuses].sort(([left], [right]) => compareStrings(left, right)));\n}\n\nfunction statusFromCode(code: string): GitFileStatus {\n const conflicted = /^(DD|AU|UD|UA|DU|AA|UU)$/.test(code);\n const untracked = code === \"??\";\n const ignored = code === \"!!\";\n const staged = !untracked && !ignored && code[0] !== \" \";\n const unstaged = !untracked && !ignored && code[1] !== \" \";\n let label: string;\n if (conflicted) label = \"conflicted\";\n else if (untracked) label = \"untracked\";\n else if (ignored) label = \"ignored\";\n else {\n const character = unstaged ? code[1] : code[0];\n const action =\n character === \"A\"\n ? \"added\"\n : character === \"D\"\n ? \"deleted\"\n : character === \"R\"\n ? \"renamed\"\n : character === \"C\"\n ? \"copied\"\n : character === \"T\"\n ? \"type changed\"\n : \"modified\";\n const location = staged && unstaged ? \"staged + unstaged\" : staged ? \"staged\" : \"unstaged\";\n label = `${action} (${location})`;\n }\n return { code, label, staged, unstaged, untracked, ignored, conflicted };\n}\n\nfunction parseUnifiedDiff(output: string): GitDiffHunk[] {\n const hunks: GitDiffHunk[] = [];\n let current: GitDiffHunk | undefined;\n let newLine = 0;\n for (const line of output.replaceAll(\"\\r\\n\", \"\\n\").split(\"\\n\")) {\n const match = /^@@ -(\\d+)(?:,(\\d+))? \\+(\\d+)(?:,(\\d+))? @@(.*)$/.exec(line);\n if (match) {\n current = {\n header: line,\n oldStart: Number(match[1]),\n oldCount: Number(match[2] ?? 1),\n newStart: Number(match[3]),\n newCount: Number(match[4] ?? 1),\n lines: [line],\n changedLines: [],\n };\n newLine = current.newStart;\n hunks.push(current);\n continue;\n }\n if (!current) continue;\n current.lines.push(line);\n if (line.startsWith(\"+\") && !line.startsWith(\"+++\")) {\n current.changedLines.push(newLine);\n newLine += 1;\n } else if (!line.startsWith(\"-\") && !line.startsWith(\"\\\\\")) {\n newLine += 1;\n }\n }\n for (const hunk of hunks) {\n while (hunk.lines.at(-1) === \"\") hunk.lines.pop();\n }\n return hunks;\n}\n\nfunction parseBlame(output: string): GitBlameInfo | undefined {\n const lines = output.split(\"\\n\");\n const commit = lines[0]?.split(\" \")[0];\n if (!commit) return undefined;\n const field = (name: string) => lines.find((line) => line.startsWith(`${name} `))?.slice(name.length + 1);\n const authorTime = Number(field(\"author-time\"));\n return {\n commit,\n author: field(\"author\") ?? \"Unknown\",\n authorTime: Number.isFinite(authorTime) ? authorTime : undefined,\n summary: field(\"summary\") ?? \"\",\n committed: !/^0+$/.test(commit),\n };\n}\n\nfunction parseHistory(output: string): GitHistoryEntry[] {\n return output\n .split(\"\\x1e\")\n .filter(Boolean)\n .flatMap((record) => {\n const [metadata = \"\", ...pathRecords] = record.split(\"\\0\");\n const [commit, author, authorTime, ...summaryParts] = metadata.replace(/^\\n+/, \"\").split(\"\\x1f\");\n const path = pathRecords.map((value) => value.replace(/^\\n+/, \"\")).find(Boolean);\n const time = Number(authorTime);\n return commit && author && path && Number.isFinite(time)\n ? [{ commit, author, authorTime: time, summary: summaryParts.join(\"\\x1f\"), path }]\n : [];\n });\n}\n\nfunction compareStrings(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0;\n}\n\nfunction stripLineEnding(value: string): string {\n return value.endsWith(\"\\r\\n\") ? value.slice(0, -2) : value.endsWith(\"\\n\") ? value.slice(0, -1) : value;\n}\n\nfunction normalizeLines(contents: string): string[] {\n if (contents === \"\") return [];\n const lines = contents.replaceAll(\"\\r\\n\", \"\\n\").replaceAll(\"\\r\", \"\\n\").split(\"\\n\");\n if (lines.at(-1) === \"\") lines.pop();\n return lines;\n}\n", "import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport type { KeyId } from \"@earendil-works/pi-tui\";\nimport { editProjectFileInExternalEditor } from \"./external-editor.js\";\nimport {\n appendPendingQuote,\n discoverProjectFiles,\n type FileQuote,\n formatQuoteContext,\n loadProjectTextFile,\n MAX_PENDING_QUOTE_BYTES,\n MAX_PENDING_QUOTES,\n MAX_QUOTE_BYTES,\n MAX_QUOTE_LINES,\n} from \"./file-context.js\";\nimport type { FileQuoteExplorer, FileQuoteExplorerResult } from \"./file-context-explorer.js\";\nimport type { FileContextMenuQuote, FileContextMenuState } from \"./file-context-menu.js\";\nimport {\n awaitFileContextSettingsWrites,\n fileContextSettingsPath,\n type LoadedFileContextSettings,\n loadFileContextSettings,\n updateFileContextSettings,\n} from \"./file-context-settings.js\";\nimport { publishFilesWidget } from \"./files-widget.js\";\nimport { createGitContext } from \"./git-context.js\";\n\ninterface ActiveExplorer {\n controller: AbortController;\n component?: FileQuoteExplorer;\n}\ninterface PendingQuote {\n id: string;\n quote: FileQuote;\n}\ninterface ExplorerRunOptions {\n menuOwned?: boolean;\n signal?: AbortSignal;\n}\ntype ExplorerRunResult = \"stay\" | \"close\";\ntype ExplorerDiscovery = [string[], Awaited<ReturnType<typeof createGitContext>>];\n\nexport interface ContextOptions {\n settingsPath?: string;\n loadSettings?: () => Promise<LoadedFileContextSettings>;\n updateSettings?: typeof updateFileContextSettings;\n discoverFiles?: typeof discoverProjectFiles;\n createGit?: typeof createGitContext;\n onChange?(ctx: ExtensionContext, quotes: readonly FileQuote[]): void;\n}\nexport interface ContextService {\n open(ctx: ExtensionCommandContext, start?: \"main\" | \"remove\"): Promise<void>;\n browse(ctx: ExtensionContext): Promise<void>;\n getState(ctx: ExtensionContext): FileContextMenuState;\n saveShortcut(ctx: ExtensionContext, shortcut: KeyId | null, signal: AbortSignal): Promise<void>;\n signal(ctx: ExtensionContext): AbortSignal;\n}\ninterface ContextOwner {\n controller: AbortController;\n open: ContextService[\"open\"];\n browse: ContextService[\"browse\"];\n getState(): FileContextMenuState;\n saveShortcut: ContextService[\"saveShortcut\"];\n refresh(ctx: ExtensionContext): Promise<void>;\n consume(ctx: ExtensionContext): FileQuote[];\n release(ctx: ExtensionContext): Promise<void>;\n}\n\n/** Registers package-owned context services, not a second extension factory. */\nexport async function registerFileContext(\n pi: ExtensionAPI,\n dependencies: ContextOptions = {},\n): Promise<ContextService> {\n const settingsPath = dependencies.settingsPath ?? fileContextSettingsPath();\n const loadSettings = dependencies.loadSettings ?? (() => loadFileContextSettings(settingsPath));\n const updateSettings = dependencies.updateSettings ?? updateFileContextSettings;\n // Shortcut registration precedes session_start; reading preferences creates no resources.\n const initialSettings = await loadSettings();\n const discoverFiles = dependencies.discoverFiles ?? discoverProjectFiles;\n const createGit = dependencies.createGit ?? createGitContext;\n const owners = new Map<object, ContextOwner>();\n const requireOwner = (ctx: ExtensionContext): ContextOwner => {\n const owner = owners.get(ctx.sessionManager);\n if (!owner || owner.controller.signal.aborted) throw new Error(\"File Context is not initialized; reload first\");\n return owner;\n };\n\n function createOwner(context: ExtensionContext): ContextOwner {\n let loadedSettings = initialSettings;\n let pendingQuotes: PendingQuote[] = [];\n let nextPendingQuoteId = 1;\n const activeSessionManager = context.sessionManager;\n const sessionGeneration = 0;\n const sessionController = new AbortController();\n const activeExplorers = new Set<ActiveExplorer>();\n const nativeWork = new Set<Promise<unknown>>();\n const gitContexts = new Set<NonNullable<Awaited<ReturnType<typeof createGitContext>>>>();\n const track = <T>(work: Promise<T>): Promise<T> => {\n nativeWork.add(work);\n void work.then(\n () => nativeWork.delete(work),\n () => nativeWork.delete(work),\n );\n return work;\n };\n let activeExplorerLaunch: { promise: Promise<void> } | undefined;\n let activeMenuLaunch: { promise: Promise<void> } | undefined;\n const updatePendingWidget = (ctx: ExtensionContext) =>\n (dependencies.onChange ?? publishFilesWidget)(\n ctx,\n pendingQuotes.map((item) => item.quote),\n );\n const clearPending = (ctx: ExtensionContext) => {\n pendingQuotes = [];\n nextPendingQuoteId = 1;\n updatePendingWidget(ctx);\n };\n const validatePending = (quote: FileQuote) => {\n appendPendingQuote(\n pendingQuotes.map((item) => item.quote),\n quote,\n );\n };\n const appendPending = (quote: FileQuote, ctx: ExtensionContext) => {\n validatePending(quote);\n pendingQuotes = [...pendingQuotes, { id: `quote-${nextPendingQuoteId++}`, quote }];\n updatePendingWidget(ctx);\n };\n const isCurrentSession = (key: unknown, generation: number) =>\n key === activeSessionManager &&\n generation === sessionGeneration &&\n owners.get(activeSessionManager) === runtime &&\n !sessionController.signal.aborted;\n const menuQuote = ({ id, quote }: PendingQuote): FileContextMenuQuote => ({\n id,\n path: quote.path,\n startLine: quote.startLine,\n endLine: quote.endLine,\n text: quote.text,\n });\n const cancelExplorers = () => {\n for (const explorer of activeExplorers) {\n explorer.controller.abort(new DOMException(\"File Context explorer closed\", \"AbortError\"));\n explorer.component?.dispose();\n }\n activeExplorers.clear();\n };\n const runExplorer = async (ctx: ExtensionContext, options: ExplorerRunOptions = {}): Promise<ExplorerRunResult> => {\n if (ctx.mode !== \"tui\") {\n rejectCommand(ctx, \"File Context requires Pi's interactive TUI.\");\n return \"close\";\n }\n const owner = ctx.sessionManager;\n const generation = sessionGeneration;\n const activeExplorer: ActiveExplorer = { controller: new AbortController() };\n let explorerGit: NonNullable<Awaited<ReturnType<typeof createGitContext>>> | undefined;\n const { controller } = activeExplorer;\n const flowSignal = AbortSignal.any([\n controller.signal,\n sessionController.signal,\n ...(ctx.signal ? [ctx.signal] : []),\n ...(options.signal ? [options.signal] : []),\n ]);\n activeExplorers.add(activeExplorer);\n try {\n const [{ runCustomInteraction, runTask }, { FileQuoteExplorer }] = await Promise.all([\n import(\"@narumitw/pi-tui-kit\"),\n import(\"./file-context-explorer.js\"),\n ]);\n if (!isCurrentSession(owner, generation) || flowSignal.aborted) return \"close\";\n const task = await runTask(ctx, {\n label: \"Scanning project files\u2026\",\n signal: flowSignal,\n isCurrent: () => isCurrentSession(owner, generation) && !flowSignal.aborted,\n task: ({ signal }) =>\n Promise.all([track(discoverFiles(ctx.cwd, { signal })), track(createGit(ctx.cwd, signal))]),\n onError: (_taskContext, error) => {\n ctx.ui.notify(\n `File Context could not scan project files: ${escapeTerminalControls(formatError(error))}. Open File Context to retry.`,\n \"error\",\n );\n },\n });\n if (task.kind === \"cancelled\" || task.kind === \"error\") {\n return options.menuOwned ? \"stay\" : \"close\";\n }\n if (task.kind === \"stale\") return \"close\";\n const discovery: ExplorerDiscovery = task.value;\n if (!isCurrentSession(owner, generation) || flowSignal.aborted) return \"close\";\n const [files, gitContext] = discovery;\n if (gitContext) {\n explorerGit = gitContext;\n gitContexts.add(gitContext);\n }\n if (files.length === 0) {\n ctx.ui.notify(\"File Context found no project files. Choose Add context snippet to retry.\", \"warning\");\n return options.menuOwned ? \"stay\" : \"close\";\n }\n const interaction = await runCustomInteraction<FileQuoteExplorerResult | undefined, ExtensionContext>(ctx, {\n signal: flowSignal,\n isCurrent: () => isCurrentSession(owner, generation),\n onError: () => {},\n create: ({ tui, theme, keybindings, signal: interactionSignal, complete }) => {\n const component = new FileQuoteExplorer({\n tui,\n theme,\n keybindings,\n files,\n cwd: ctx.cwd,\n loadFile: (path, signal) =>\n track(\n loadProjectTextFile(ctx.cwd, path, {\n signal: signal ? AbortSignal.any([signal, interactionSignal]) : interactionSignal,\n }),\n ),\n editFile: (path, signal) =>\n track(\n editProjectFileInExternalEditor({\n root: ctx.cwd,\n projectPath: path,\n tui,\n projectTrusted: ctx.isProjectTrusted(),\n signal: signal ? AbortSignal.any([signal, interactionSignal]) : interactionSignal,\n isCurrent: () => isCurrentSession(owner, generation) && !interactionSignal.aborted,\n }),\n ),\n gitContext,\n rootNavigation: options.menuOwned,\n getSelectedContextState: () => ({\n count: pendingQuotes.length,\n totalBytes: pendingQuotes.reduce(\n (total, item) => total + Buffer.byteLength(item.quote.text, \"utf8\"),\n 0,\n ),\n maximumCount: MAX_PENDING_QUOTES,\n maximumBytes: MAX_PENDING_QUOTE_BYTES,\n maximumSnippetLines: MAX_QUOTE_LINES,\n maximumSnippetBytes: MAX_QUOTE_BYTES,\n }),\n validateQuote: validatePending,\n onAddAndContinue: (quote) => {\n if (!isCurrentSession(owner, generation) || interactionSignal.aborted) {\n throw new DOMException(\"File Context session replaced\", \"AbortError\");\n }\n appendPending(quote, ctx);\n try {\n ctx.ui.notify(\n `Added to next prompt context: ${escapeTerminalControls(quote.path)} \u00B7 lines ${quote.startLine}-${quote.endLine}.`,\n \"info\",\n );\n } catch {\n // The widget already reflects the selected snapshot.\n }\n },\n done: complete,\n });\n activeExplorer.component = component;\n return component;\n },\n });\n if (interaction.kind === \"error\") throw interaction.error;\n const result = interaction.kind === \"completed\" ? interaction.value : undefined;\n if (!isCurrentSession(owner, generation) || flowSignal.aborted) return \"close\";\n if (result?.kind === \"quote\") {\n appendPending(result.quote, ctx);\n return \"close\";\n }\n if (result?.kind === \"reference\") {\n ctx.ui.pasteToEditor(formatFileReference(result.path));\n return \"close\";\n }\n return result?.kind === \"back\" ? \"stay\" : \"close\";\n } catch (error: unknown) {\n if (!isCurrentSession(owner, generation) || flowSignal.aborted || isAbortError(error)) {\n return \"close\";\n }\n try {\n ctx.ui.notify(\n `File Context failed: ${escapeTerminalControls(formatError(error))}. Open File Context to retry.`,\n \"error\",\n );\n } catch {\n // The session may have been replaced while the picker was open.\n }\n return options.menuOwned ? \"stay\" : \"close\";\n } finally {\n activeExplorers.delete(activeExplorer);\n if (explorerGit) {\n await explorerGit.whenIdle();\n gitContexts.delete(explorerGit);\n }\n }\n };\n\n const openExplorer = (ctx: ExtensionContext): Promise<void> => {\n if (activeExplorerLaunch) return activeExplorerLaunch.promise;\n if (activeMenuLaunch) return activeMenuLaunch.promise;\n const launch = { promise: runExplorer(ctx).then(() => undefined) };\n activeExplorerLaunch = launch;\n void launch.promise\n .finally(() => {\n if (activeExplorerLaunch === launch) activeExplorerLaunch = undefined;\n })\n .catch(() => {});\n return launch.promise;\n };\n const openMenu = (ctx: ExtensionCommandContext, start: \"main\" | \"remove\" = \"main\"): Promise<void> => {\n if (ctx.mode !== \"tui\") {\n rejectCommand(ctx, \"File Context requires Pi's interactive TUI.\");\n return Promise.resolve();\n }\n if (activeMenuLaunch) return activeMenuLaunch.promise;\n if (activeExplorerLaunch) return activeExplorerLaunch.promise;\n if (start === \"remove\" && pendingQuotes.length === 0) {\n ctx.ui.notify(\"File Context has no context selected for the next prompt.\", \"warning\");\n return Promise.resolve();\n }\n const owner = ctx.sessionManager;\n const generation = sessionGeneration;\n const ownerController = {\n signal: AbortSignal.any([sessionController.signal, ...(ctx.signal ? [ctx.signal] : [])]),\n };\n const isCurrent = () => isCurrentSession(owner, generation) && !ownerController.signal.aborted;\n const promise = import(\"./file-context-menu.js\")\n .then(({ showFileContextMenu }) => {\n if (!isCurrent()) return;\n return showFileContextMenu(ctx, {\n start,\n signal: ownerController.signal,\n isCurrent,\n getState: () => {\n if (!isCurrent()) throw new DOMException(\"File Context session replaced\", \"AbortError\");\n const quotes = pendingQuotes.map(menuQuote);\n return {\n quotes,\n shortcut: loadedSettings.settings.openShortcut,\n maximumQuotes: MAX_PENDING_QUOTES,\n maximumBytes: MAX_PENDING_QUOTE_BYTES,\n totalBytes: quotes.reduce((total, quote) => total + Buffer.byteLength(quote.text, \"utf8\"), 0),\n settingsPath,\n settingsInvalidReason: loadedSettings.invalidReason,\n };\n },\n addQuote: (signal) => runExplorer(ctx, { menuOwned: true, signal }),\n saveShortcut: async (shortcut, signal) => {\n if (!isCurrent() || signal.aborted) {\n throw new DOMException(\"File Context session replaced\", \"AbortError\");\n }\n if (pendingQuotes.length > 0)\n throw new Error(\"Submit or remove selected context before changing the shortcut\");\n const saved = await updateSettings(shortcut, { settingsPath, signal });\n if (!isCurrent() || signal.aborted) return;\n loadedSettings = { settings: saved };\n },\n removeQuote: (id, signal) => {\n if (!isCurrent() || signal.aborted) return { kind: \"missing\" };\n const index = pendingQuotes.findIndex((item) => item.id === id);\n const selected = pendingQuotes[index];\n if (!selected) return { kind: \"missing\" };\n pendingQuotes = pendingQuotes.filter((_item, itemIndex) => itemIndex !== index);\n updatePendingWidget(ctx);\n return {\n kind: \"removed\",\n quote: menuQuote(selected),\n remaining: pendingQuotes.length,\n };\n },\n });\n })\n .then(() => undefined);\n const launch = { promise };\n activeMenuLaunch = launch;\n const clearLaunch = () => {\n if (activeMenuLaunch === launch) activeMenuLaunch = undefined;\n };\n void promise.then(clearLaunch, clearLaunch);\n return promise;\n };\n\n const runtime: ContextOwner = {\n controller: sessionController,\n open: openMenu,\n browse: openExplorer,\n getState: () => ({\n quotes: pendingQuotes.map(menuQuote),\n shortcut: loadedSettings.settings.openShortcut,\n maximumQuotes: MAX_PENDING_QUOTES,\n maximumBytes: MAX_PENDING_QUOTE_BYTES,\n totalBytes: pendingQuotes.reduce((total, item) => total + Buffer.byteLength(item.quote.text, \"utf8\"), 0),\n settingsPath,\n settingsInvalidReason: loadedSettings.invalidReason,\n }),\n saveShortcut: async (ctx, shortcut, signal) => {\n if (pendingQuotes.length) throw new Error(\"Submit or remove selected context before changing the shortcut\");\n const operation = AbortSignal.any([signal, sessionController.signal, ...(ctx.signal ? [ctx.signal] : [])]);\n operation.throwIfAborted();\n const saved = await updateSettings(shortcut, { settingsPath, signal: operation });\n if (!isCurrentSession(activeSessionManager, sessionGeneration) || operation.aborted) return;\n loadedSettings = { settings: saved };\n },\n refresh: async (ctx) => {\n const refreshed = await loadSettings();\n if (!isCurrentSession(activeSessionManager, sessionGeneration)) return;\n loadedSettings = refreshed;\n if (refreshed.warning && ctx.hasUI) ctx.ui.notify(escapeTerminalControls(refreshed.warning), \"warning\");\n },\n consume: (ctx) => {\n const quotes = pendingQuotes.map((item) => item.quote);\n clearPending(ctx);\n return quotes;\n },\n release: async (ctx) => {\n // A menu can be waiting for reload, which itself waits for this shutdown hook.\n const work = [activeExplorerLaunch?.promise];\n sessionController.abort(new DOMException(\"File Context session ended\", \"AbortError\"));\n cancelExplorers();\n const settled = await Promise.allSettled([\n awaitFileContextSettingsWrites(settingsPath),\n Promise.resolve().then(() => clearPending(ctx)),\n ...work,\n ...nativeWork,\n ...[...gitContexts].map((git) => git.whenIdle()),\n ]);\n for (const result of settled.slice(0, 2)) if (result.status === \"rejected\") throw result.reason;\n },\n };\n return runtime;\n }\n\n const service: ContextService = {\n open: (ctx, start) => requireOwner(ctx).open(ctx, start),\n browse: (ctx) => requireOwner(ctx).browse(ctx),\n getState: (ctx) => requireOwner(ctx).getState(),\n saveShortcut: (ctx, shortcut, signal) => requireOwner(ctx).saveShortcut(ctx, shortcut, signal),\n signal: (ctx) => requireOwner(ctx).controller.signal,\n };\n if (initialSettings.settings.openShortcut) {\n pi.registerShortcut(initialSettings.settings.openShortcut, {\n description: \"Open File Context\",\n handler: service.browse,\n });\n }\n pi.registerCommand(\"file-context\", {\n description: \"Open the File Context menu\",\n getArgumentCompletions: (prefix) => {\n const items = [\n { value: \"browse\", label: \"browse\", description: \"Open the file browser directly\" },\n { value: \"remove\", label: \"remove\", description: \"Review selected context directly\" },\n ].filter((item) => item.value.startsWith(prefix.trimStart().toLowerCase()));\n return items.length ? items : null;\n },\n handler: async (args, ctx) => {\n const route = args.trim().toLowerCase();\n if (route !== \"\" && route !== \"browse\" && route !== \"remove\")\n return rejectCommand(ctx, \"Usage: /file-context [browse|remove]\");\n if (ctx.mode !== \"tui\") return rejectCommand(ctx, \"File Context requires Pi's interactive TUI.\");\n if (route === \"browse\") await service.browse(ctx);\n else await service.open(ctx, route === \"remove\" ? \"remove\" : \"main\");\n },\n });\n pi.on(\"session_start\", async (_event, ctx) => {\n const key = ctx.sessionManager;\n const previous = owners.get(key);\n const owner = createOwner(ctx);\n owners.set(key, owner);\n if (previous) await previous.release(ctx);\n if (owners.get(key) !== owner || owner.controller.signal.aborted) return;\n (dependencies.onChange ?? publishFilesWidget)(ctx, []);\n await owner.refresh(ctx);\n });\n pi.on(\"before_agent_start\", (event, ctx) => {\n const owner = owners.get(ctx.sessionManager);\n if (event.messageType === \"custom\" || !owner || owner.controller.signal.aborted || !owner.getState().quotes.length)\n return;\n return {\n message: { customType: \"file-context-quotes\", content: formatQuoteContext(owner.consume(ctx)), display: false },\n };\n });\n pi.on(\"session_shutdown\", async (_event, ctx) => {\n const key = ctx.sessionManager;\n const owner = owners.get(key);\n if (!owner) return;\n try {\n await owner.release(ctx);\n } finally {\n if (owners.get(key) === owner) owners.delete(key);\n }\n });\n return service;\n}\n\nfunction formatFileReference(path: string): string {\n const escaped = path.replaceAll(\"\\\\\", \"\\\\\\\\\").replaceAll('\"', '\\\\\"');\n return /\\s|[\"\\\\]/.test(path) ? `@\"${escaped}\" ` : `@${path} `;\n}\nfunction escapeTerminalControls(text: string): string {\n return [...text]\n .map((character) => {\n const code = character.charCodeAt(0);\n return code <= 31 || (code >= 127 && code <= 159) ? `\\\\x${code.toString(16).padStart(2, \"0\")}` : character;\n })\n .join(\"\");\n}\nfunction rejectCommand(ctx: ExtensionContext, message: string): void {\n if (ctx.hasUI) {\n ctx.ui.notify(message, \"warning\");\n return;\n }\n throw new Error(message);\n}\nfunction isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === \"AbortError\";\n}\nfunction formatError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n", "import { join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { getAgentDir } from \"@earendil-works/pi-coding-agent\";\nimport { ANCHOR_TYPE, createScopeNotice, reconcileScopeContext } from \"./contracts.js\";\nimport { displayError, displayPath } from \"./display.js\";\nimport type { ScopeMenuOptions, ScopeView } from \"./menu.js\";\nimport { assertScopeTarget, resolveScopeTarget, type ScopeTarget } from \"./paths.js\";\nimport { assertUsableLoadout, decideTool } from \"./policy.js\";\nimport { type ScopeSettings, ScopeSettingsStore } from \"./settings.js\";\nimport { createSnapshot, restoreScope, type ScopeSnapshot, type ScopeState, STATE_TYPE, sameScope } from \"./state.js\";\n\nexport type { ScopeView } from \"./menu.js\";\nexport type { ScopeSettings } from \"./settings.js\";\nexport type ShowMenu = (ctx: ExtensionCommandContext, options: ScopeMenuOptions) => Promise<void>;\n\nexport interface ScopeOptions {\n showMenu?: ShowMenu;\n onChange?(ctx: ExtensionContext, view: ScopeView | undefined): void;\n}\n\nexport interface ScopeService {\n open(ctx: ExtensionCommandContext, signal?: AbortSignal): Promise<void>;\n off(ctx: ExtensionContext): void;\n getView(ctx: ExtensionContext): ScopeView;\n getSettings(ctx: ExtensionContext): ScopeSettings;\n saveShowHiddenFiles(ctx: ExtensionContext, value: boolean): Promise<void>;\n signal(ctx: ExtensionContext): AbortSignal;\n}\n\ninterface Flow {\n controller: AbortController;\n signal: AbortSignal;\n revision: number;\n promise?: Promise<void>;\n}\ninterface Owner {\n key: object;\n controller: AbortController;\n settings: ScopeSettingsStore;\n scope: ScopeState;\n revision: number;\n checks: Set<Promise<void>>;\n flow?: Flow;\n}\n\nconst uninitialized = (): ScopeState => ({ state: \"blocked\", error: \"Scope is not initialized; edits remain blocked\" });\nconst showMenu: ShowMenu = async (ctx, options) => {\n const { showScopeMenu } = await import(\"./menu.js\");\n options.signal.throwIfAborted();\n if (options.isCurrent()) await showScopeMenu(ctx, options);\n};\n\nexport function registerScope(pi: ExtensionAPI, options: ScopeOptions = {}): ScopeService {\n const show = options.showMenu ?? showMenu;\n const owners = new Map<object, Owner>();\n const starts = new WeakMap<object, object>();\n\n function current(owner: Owner, _ctx?: ExtensionContext): boolean {\n // Do not touch a captured context after its session has been replaced.\n return !owner.controller.signal.aborted && owners.get(owner.key) === owner;\n }\n function check(owner: Owner, ctx: ExtensionContext, flow?: Flow): void {\n owner.controller.signal.throwIfAborted();\n flow?.signal.throwIfAborted();\n if (!current(owner, ctx) || (flow && (owner.flow !== flow || owner.revision !== flow.revision))) {\n throw new DOMException(\"Scope session or selection changed\", \"AbortError\");\n }\n ctx.signal?.throwIfAborted();\n }\n function mutationCheck(owner: Owner, ctx: ExtensionContext, flow?: Flow): void {\n check(owner, ctx, flow);\n if (!ctx.isIdle() || ctx.hasPendingMessages()) {\n throw new Error(\"Stop the current run and clear pending messages before changing Scope\");\n }\n }\n function view(owner: Owner, ctx: ExtensionContext): ScopeView {\n return {\n state: owner.scope.state,\n cwd: ctx.cwd,\n targets: (owner.scope.snapshot?.targets ?? []).map((target) => ({ ...target })),\n ...(owner.scope.error ? { error: owner.scope.error } : {}),\n };\n }\n function updateView(owner: Owner, ctx: ExtensionContext): void {\n if (!current(owner, ctx)) return;\n options.onChange?.(ctx, view(owner, ctx));\n }\n function notify(ctx: ExtensionContext, message: string, kind: \"info\" | \"warning\" | \"error\" = \"info\"): void {\n if (ctx.hasUI) ctx.ui.notify(displayError(message).slice(0, 4_000), kind);\n }\n async function release(owner: Owner, ctx: ExtensionContext): Promise<void> {\n let report: Promise<void> | undefined;\n if (!owner.controller.signal.aborted) {\n owner.controller.abort(new DOMException(\"Scope session ended\", \"AbortError\"));\n report = Promise.resolve().then(() => options.onChange?.(ctx, undefined));\n }\n const settled = await Promise.allSettled([owner.flow?.promise, owner.settings.flush(), report, ...owner.checks]);\n if (settled[1].status === \"rejected\") throw settled[1].reason;\n if (settled[2].status === \"rejected\") throw settled[2].reason;\n }\n async function initialize(ctx: ExtensionContext): Promise<void> {\n const key = ctx.sessionManager;\n const token = {};\n starts.set(key, token);\n const previous = owners.get(key);\n const owner: Owner = {\n key,\n controller: new AbortController(),\n // Keep the existing settings path so the package rename preserves user preferences.\n settings: new ScopeSettingsStore(join(getAgentDir(), \"pi-scope.json\")),\n scope: uninitialized(),\n revision: 0,\n checks: new Set(),\n };\n owners.set(key, owner);\n if (previous) await release(previous, ctx);\n if (starts.get(key) !== token || !current(owner, ctx)) return;\n try {\n owner.scope = restoreScope(ctx.sessionManager.getBranch(), ctx.cwd);\n } catch {\n owner.scope = {\n state: \"blocked\",\n error: \"Scope restoration failed; edits remain blocked. Use /scope to repair or /scope off to exit.\",\n };\n }\n if (owner.scope.state === \"active\") {\n try {\n assertUsableLoadout(pi);\n } catch (error) {\n owner.scope = { ...owner.scope, state: \"blocked\", error: displayError(error) };\n }\n }\n try {\n await owner.settings.load();\n } catch (error) {\n if (current(owner, ctx)) notify(ctx, `Scope settings were not loaded: ${displayError(error)}`, \"warning\");\n }\n if (starts.get(key) !== token || !current(owner, ctx)) return;\n updateView(owner, ctx);\n if (owner.scope.state === \"blocked\") notify(ctx, owner.scope.error ?? \"Scope restoration failed\", \"error\");\n }\n\n function publish(owner: Owner, ctx: ExtensionContext): void {\n const notice = createScopeNotice(owner.scope);\n if (!notice) return;\n try {\n // sendMessage has no persistence acknowledgement. Context verifies retention and stores\n // a fixed restoration anchor if delivery is deferred or the handoff was compacted away.\n pi.sendMessage(notice, { triggerTurn: false });\n } catch (error) {\n notify(ctx, `Scope is applied, but its model notice needs recovery: ${displayError(error)}`, \"warning\");\n }\n }\n function commit(\n owner: Owner,\n ctx: ExtensionContext,\n snapshot: ScopeSnapshot,\n flow?: Flow,\n signal?: AbortSignal,\n ): void {\n signal?.throwIfAborted();\n mutationCheck(owner, ctx, flow);\n owner.scope = { state: \"blocked\", error: \"Scope update is incomplete; edits remain blocked\" };\n owner.revision += 1;\n if (flow) flow.revision = owner.revision;\n try {\n pi.appendEntry(STATE_TYPE, snapshot);\n check(owner, ctx, flow);\n const restored = restoreScope(ctx.sessionManager.getBranch(), ctx.cwd);\n if (restored.state === \"blocked\" || !sameScope(restored.snapshot, snapshot) || !restored.entryId) {\n throw new Error(\"Scope state was not retained by the session\");\n }\n owner.scope = restored;\n } catch (error) {\n // SessionManager can update memory before a disk failure. Do not claim rollback or\n // silently fall back to unrestricted editing when publication is uncertain.\n if (current(owner, ctx)) {\n owner.scope = {\n state: \"blocked\",\n error: `Scope state could not be saved; edits remain blocked. Repair with /scope or /scope off. ${displayError(error)}`,\n };\n updateView(owner, ctx);\n }\n throw error;\n }\n updateView(owner, ctx);\n publish(owner, ctx);\n }\n async function apply(\n owner: Owner,\n ctx: ExtensionContext,\n targets: readonly ScopeTarget[],\n flow: Flow,\n newFile = false,\n operation?: AbortSignal,\n ): Promise<void> {\n const signal = AbortSignal.any([owner.controller.signal, flow.signal, ...(operation ? [operation] : [])]);\n signal.throwIfAborted();\n mutationCheck(owner, ctx, flow);\n const proposed = createSnapshot(resolve(ctx.cwd), targets);\n // Newly resolved existing files must still exist at commit. Keep the\n // requirement across canonical alias deduplication; live reserved paths\n // without resolution metadata may still be missing.\n const existing = new Set(\n targets.filter((target) => \"exists\" in target && target.exists === true).map((target) => target.canonical),\n );\n if (newFile && proposed.targets?.length !== 1) throw new Error(\"New file requires exactly one target\");\n assertUsableLoadout(pi);\n const checked: ScopeTarget[] = [];\n for (const target of proposed.targets ?? []) {\n const candidate = await resolveScopeTarget(\n pathToFileURL(target.path).href,\n ctx.cwd,\n newFile ? \"new\" : existing.has(target.canonical) ? \"existing\" : \"either\",\n signal,\n );\n signal.throwIfAborted();\n mutationCheck(owner, ctx, flow);\n if (candidate.canonical !== target.canonical)\n throw new Error(`Scope target changed: ${displayPath(target.path)}; select it again`);\n checked.push({ path: candidate.path, canonical: candidate.canonical });\n }\n assertUsableLoadout(pi);\n const snapshot = createSnapshot(resolve(ctx.cwd), checked);\n createScopeNotice({ state: \"active\", snapshot, entryId: \"preview\" });\n if (owner.scope.state !== \"blocked\" && sameScope(owner.scope.snapshot, snapshot)) {\n notify(ctx, \"Scope unchanged\");\n return;\n }\n commit(owner, ctx, snapshot, flow, signal);\n notify(ctx, `Editing limited to ${checked.length} file${checked.length === 1 ? \"\" : \"s\"}`);\n }\n function off(owner: Owner, ctx: ExtensionContext, flow?: Flow): void {\n mutationCheck(owner, ctx, flow);\n if (owner.scope.state === \"inactive\") {\n notify(ctx, \"Scope is already inactive\");\n return;\n }\n commit(owner, ctx, createSnapshot(resolve(ctx.cwd), null), flow);\n notify(ctx, \"Scope restriction ended\");\n }\n\n pi.on(\"session_start\", (_event, ctx) => initialize(ctx));\n pi.on(\"session_tree\", (_event, ctx) => initialize(ctx));\n pi.on(\"session_shutdown\", async (_event, ctx) => {\n const key = ctx.sessionManager;\n starts.delete(key);\n const owner = owners.get(key);\n if (!owner) return;\n try {\n await release(owner, ctx);\n } finally {\n if (owners.get(key) === owner) owners.delete(key);\n }\n });\n\n pi.on(\"tool_call\", async (event, ctx) => {\n const owner = owners.get(ctx.sessionManager);\n const scope = owner && current(owner) ? owner.scope : uninitialized();\n if (scope.state === \"inactive\") return;\n const decision = decideTool(event.toolName, pi.getAllTools(), scope);\n if (decision.kind === \"pass\") return;\n if (decision.kind === \"block\") return { block: true, reason: displayError(decision.reason).slice(0, 4_000) };\n const revision = owner?.revision;\n let work: Promise<void> | undefined;\n try {\n const path =\n event.input && typeof event.input === \"object\" && \"path\" in event.input ? event.input.path : undefined;\n if (!owner || !scope.snapshot?.targets || typeof path !== \"string\")\n throw new Error(\"Invalid Scope write/edit call\");\n check(owner, ctx);\n const signal = ctx.signal ? AbortSignal.any([owner.controller.signal, ctx.signal]) : owner.controller.signal;\n work = assertScopeTarget(path, ctx.cwd, scope.snapshot.targets, signal);\n owner.checks.add(work);\n await work;\n check(owner, ctx);\n if (owner.revision !== revision || owner.scope !== scope)\n throw new Error(\"Scope changed while checking this call\");\n } catch (error) {\n return { block: true, reason: `Scope blocked this edit: ${displayError(error).slice(0, 4_000)}` };\n } finally {\n if (work) owner?.checks.delete(work);\n }\n });\n\n // Legacy context folds native prompt/tool deltas into the leading system message\n // after an insertion. Keep the full transcript so anchored recovery stays additive.\n pi.on(\"context_with_system\", (event, ctx) => {\n const owner = owners.get(ctx.sessionManager);\n if (!owner || !current(owner, ctx) || ctx.signal?.aborted) return;\n try {\n if (owner.scope.state === \"active\") {\n try {\n assertUsableLoadout(pi);\n } catch (error) {\n owner.scope = { ...owner.scope, state: \"blocked\", error: displayError(error) };\n owner.revision += 1;\n owner.flow?.controller.abort(new DOMException(\"Scope tool configuration changed\", \"AbortError\"));\n updateView(owner, ctx);\n notify(ctx, owner.scope.error ?? \"Scope tool configuration is unsupported\", \"error\");\n }\n }\n const result = reconcileScopeContext(event.messages, ctx.sessionManager.getBranch(), owner.scope);\n if (result.anchor) {\n pi.appendEntry(ANCHOR_TYPE, result.anchor);\n check(owner, ctx);\n }\n return { messages: result.messages };\n } catch (error) {\n if (!current(owner) || ctx.signal?.aborted) return;\n owner.scope = {\n ...owner.scope,\n state: \"blocked\",\n error: \"Scope context could not be restored; edits remain blocked. Repair with /scope or /scope off.\",\n };\n owner.revision += 1;\n owner.flow?.controller.abort(new DOMException(\"Scope context restoration failed\", \"AbortError\"));\n updateView(owner, ctx);\n throw error;\n }\n });\n\n function getOwner(ctx: ExtensionContext): Owner {\n const owner = owners.get(ctx.sessionManager);\n if (!owner) throw new Error(\"Scope is not initialized; reload the session first\");\n check(owner, ctx);\n return owner;\n }\n function assertNoFlow(owner: Owner): void {\n if (owner.flow) throw new Error(\"A Scope menu is already open; close it before issuing another Scope command\");\n }\n const service: ScopeService = {\n async open(ctx, signal) {\n if (ctx.mode !== \"tui\" || !ctx.hasUI) throw new Error(\"/edit-scope requires Pi TUI mode\");\n const owner = getOwner(ctx);\n mutationCheck(owner, ctx);\n assertNoFlow(owner);\n signal?.throwIfAborted();\n const controller = new AbortController();\n const flow: Flow = {\n controller,\n signal: AbortSignal.any([\n owner.controller.signal,\n controller.signal,\n ...(ctx.signal ? [ctx.signal] : []),\n ...(signal ? [signal] : []),\n ]),\n revision: owner.revision,\n };\n owner.flow = flow;\n const menu: ScopeMenuOptions = {\n signal: flow.signal,\n isCurrent: () =>\n current(owner, ctx) && !flow.signal.aborted && owner.flow === flow && owner.revision === flow.revision,\n getView: () => {\n check(owner, ctx, flow);\n return service.getView(ctx);\n },\n getSettings: () => {\n check(owner, ctx, flow);\n return service.getSettings(ctx);\n },\n saveShowHiddenFiles: async (value) => {\n check(owner, ctx, flow);\n await service.saveShowHiddenFiles(ctx, value);\n check(owner, ctx, flow);\n },\n apply: (targets, newFile, operation) => {\n const work = apply(owner, ctx, targets, flow, newFile, operation);\n owner.checks.add(work);\n void work.then(\n () => owner.checks.delete(work),\n () => owner.checks.delete(work),\n );\n return work;\n },\n off: async () => off(owner, ctx, flow),\n };\n flow.promise = Promise.resolve().then(() => {\n check(owner, ctx, flow);\n return show(ctx, menu);\n });\n try {\n await flow.promise;\n check(owner, ctx, flow);\n } catch (error) {\n if (current(owner, ctx)) throw error;\n } finally {\n controller.abort(new DOMException(\"Scope menu closed\", \"AbortError\"));\n if (owner.flow === flow) owner.flow = undefined;\n }\n },\n off(ctx) {\n const owner = getOwner(ctx);\n mutationCheck(owner, ctx);\n assertNoFlow(owner);\n off(owner, ctx);\n },\n getView(ctx) {\n return view(getOwner(ctx), ctx);\n },\n getSettings(ctx) {\n return getOwner(ctx).settings.current;\n },\n async saveShowHiddenFiles(ctx, value) {\n const owner = getOwner(ctx);\n await owner.settings.saveShowHiddenFiles(value);\n check(owner, ctx);\n },\n signal(ctx) {\n return getOwner(ctx).controller.signal;\n },\n };\n\n // Both routes use the same service; /scope keeps historical notices usable.\n for (const commandName of [\"edit-scope\", \"scope\"]) {\n pi.registerCommand(commandName, {\n description:\n commandName === \"scope\"\n ? \"Compatibility alias for /edit-scope\"\n : \"Temporarily restrict editing to selected files\",\n getArgumentCompletions: (prefix) =>\n \"off\".startsWith(prefix) ? [{ value: \"off\", label: \"off\", description: \"End the editing restriction\" }] : [],\n handler: async (args, ctx) => {\n const route = args.trim();\n if (route !== \"\" && route !== \"off\") throw new Error(`Usage: /${commandName} or /${commandName} off`);\n if (ctx.mode !== \"tui\" || !ctx.hasUI) throw new Error(`/${commandName} requires Pi TUI mode`);\n if (route === \"off\") return service.off(ctx);\n await service.open(ctx);\n },\n });\n }\n return service;\n}\n", "import { createHash } from \"node:crypto\";\nimport { pathToFileURL } from \"node:url\";\nimport type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport { convertToLlm, type SessionEntry } from \"@earendil-works/pi-coding-agent\";\nimport { displayPath } from \"./display.js\";\nimport { parseSnapshot, type ScopeState, STATE_TYPE } from \"./state.js\";\n\n// Keep legacy identities and notice text stable: saved anchors hash the model-visible prefix.\nexport const NOTICE_TYPE = \"scope-contract\";\nexport const ANCHOR_TYPE = \"scope-context-anchor\";\nexport const MAX_NOTICE_BYTES = 48_000;\n\nexport interface ContractAnchor {\n version: 1;\n stateId: string;\n state: ScopeState[\"state\"];\n count: number;\n digest: string;\n sentAt: number;\n /** A new instance of a contract that already exists earlier in the projected history. */\n recovery?: number;\n}\n\ntype ScopeNotice = Extract<AgentMessage, { role: \"custom\" }>;\ninterface PlacedAnchor {\n anchor: ContractAnchor;\n notice: ScopeNotice;\n}\n\nconst NON_MODEL_FIELDS = new Set([\n \"timestamp\",\n \"durationMs\",\n \"usage\",\n \"stopReason\",\n \"errorMessage\",\n \"api\",\n \"provider\",\n \"model\",\n \"details\",\n \"nestedCalls\",\n]);\n\nexport function createScopeNotice(state: ScopeState, timestamp = 0): ScopeNotice | undefined {\n if (!state.entryId) return undefined;\n let content: string;\n if (state.state === \"blocked\") {\n content =\n \"Scope state is unavailable. Do not edit files or use writing commands. The user must repair it with /scope or exit with /scope off. Reading remains available.\";\n } else if (state.state === \"inactive\") {\n content =\n \"The user ended the Scope editing restriction. Earlier Scope notices no longer apply. All other existing instructions and tool restrictions remain unchanged.\";\n } else {\n content = [\n \"Scope editing restriction is active. This notice replaces earlier Scope notices.\",\n \"Only write or edit these exact selected file targets. Use their file URLs as tool paths to preserve literal Unicode and control characters:\",\n ...(state.snapshot?.targets ?? []).map(\n (target) =>\n `- ${JSON.stringify(pathToFileURL(target.path).href)}${target.path === target.canonical ? \"\" : ` (canonical target ${displayPath(target.canonical)})`}`,\n ),\n \"Other files may be read. Shell commands and unsupported tools cannot bypass this editing boundary. Only the user can change or end the scope with /scope.\",\n ].join(\"\\n\");\n }\n if (Buffer.byteLength(content) > MAX_NOTICE_BYTES)\n throw new Error(`Scope notice exceeds ${MAX_NOTICE_BYTES} bytes; select fewer files`);\n return {\n role: \"custom\",\n customType: NOTICE_TYPE,\n content,\n display: false,\n details: { version: 1, stateId: state.entryId },\n timestamp,\n };\n}\n\nfunction contractKey(message: AgentMessage): string | undefined {\n if (message.role !== \"custom\" || message.customType !== NOTICE_TYPE || typeof message.content !== \"string\") return;\n const details = message.details as { version?: unknown; stateId?: unknown } | undefined;\n if (details?.version !== 1 || typeof details.stateId !== \"string\") return;\n return JSON.stringify([details.stateId, message.content]);\n}\n\nfunction noticeKey(message: AgentMessage): string | undefined {\n const key = contractKey(message);\n if (!key) return;\n const recovery = (message as ScopeNotice).details as { recovery?: unknown } | undefined;\n return JSON.stringify([key, typeof recovery?.recovery === \"number\" ? recovery.recovery : 0]);\n}\n\nfunction anchoredNotice(notice: ScopeNotice, anchor: ContractAnchor): ScopeNotice {\n return anchor.recovery === undefined\n ? notice\n : { ...notice, details: { version: 1, stateId: anchor.stateId, recovery: anchor.recovery } };\n}\n\nfunction prefixDigests(messages: readonly AgentMessage[], counts: ReadonlySet<number>): Map<number, string> {\n const hash = createHash(\"sha256\");\n const results = new Map<number, string>();\n if (counts.has(0)) results.set(0, hash.copy().digest(\"hex\"));\n for (let index = 0; index < messages.length; index += 1) {\n const normalized = convertToLlm([messages[index]]).map((message) =>\n Object.fromEntries(Object.entries(message).filter(([key]) => !NON_MODEL_FIELDS.has(key))),\n );\n hash.update(JSON.stringify(normalized));\n hash.update(\"\\n\");\n if (counts.has(index + 1)) results.set(index + 1, hash.copy().digest(\"hex\"));\n }\n return results;\n}\n\nfunction readAnchors(branch: readonly SessionEntry[]): PlacedAnchor[] {\n const states = new Map(\n branch\n .filter((entry) => entry.type === \"custom\" && entry.customType === STATE_TYPE)\n .map((entry) => [entry.id, entry]),\n );\n const found = new Map<string, PlacedAnchor>();\n for (const entry of branch) {\n if (entry.type !== \"custom\" || entry.customType !== ANCHOR_TYPE || !entry.data || typeof entry.data !== \"object\")\n continue;\n const value = entry.data as Partial<ContractAnchor>;\n if (\n value.version !== 1 ||\n typeof value.stateId !== \"string\" ||\n ![\"active\", \"inactive\", \"blocked\"].includes(value.state ?? \"\") ||\n !Number.isSafeInteger(value.count) ||\n (value.count as number) < 0 ||\n typeof value.digest !== \"string\" ||\n !/^[a-f0-9]{64}$/.test(value.digest) ||\n typeof value.sentAt !== \"number\" ||\n !Number.isFinite(value.sentAt) ||\n (value.recovery !== undefined && (!Number.isSafeInteger(value.recovery) || value.recovery <= 0))\n )\n continue;\n const source = states.get(value.stateId);\n if (source?.type !== \"custom\") continue;\n try {\n const state: ScopeState =\n value.state === \"blocked\"\n ? { state: \"blocked\", entryId: source.id }\n : { state: value.state as \"active\" | \"inactive\", entryId: source.id, snapshot: parseSnapshot(source.data) };\n if (state.state !== \"blocked\" && (state.snapshot?.targets === null) !== (state.state === \"inactive\")) continue;\n const anchor = value as ContractAnchor;\n const notice = createScopeNotice(state, anchor.sentAt);\n if (!notice) continue;\n const key = JSON.stringify([anchor.stateId, anchor.state, anchor.count, anchor.digest, anchor.recovery ?? 0]);\n if (!found.has(key)) found.set(key, { anchor, notice: anchoredNotice(notice, anchor) });\n } catch {\n // An invalid historical anchor cannot become model instructions.\n }\n }\n return [...found.values()];\n}\n\nfunction baseFor(\n messages: readonly AgentMessage[],\n anchors: readonly PlacedAnchor[],\n exclude: readonly ScopeNotice[] = [],\n): AgentMessage[] {\n const keys = new Set([...anchors.map(({ notice }) => noticeKey(notice)), ...exclude.map(noticeKey)]);\n return messages.filter((message) => !keys.has(noticeKey(message)));\n}\n\n/** Keep only anchors whose original message prefix still exists after compaction or tree changes. */\nfunction project(\n messages: readonly AgentMessage[],\n candidates: readonly PlacedAnchor[],\n exclude: readonly ScopeNotice[] = [],\n) {\n const anchors = [...candidates];\n let base: AgentMessage[];\n for (;;) {\n base = baseFor(messages, anchors, exclude);\n const hashes = prefixDigests(base, new Set(anchors.map(({ anchor }) => anchor.count)));\n const firstSlot = base[0]?.role === \"system\" ? 1 : 0;\n const stale = anchors.findIndex(\n ({ anchor }) => anchor.count < firstSlot || hashes.get(anchor.count) !== anchor.digest,\n );\n if (stale < 0) break;\n // Removing an older anchor restores its native notice to the base. A newer recovery\n // digest can include that notice, so recompute before rejecting the next candidate.\n anchors.splice(stale, 1);\n }\n const at = new Map<number, ScopeNotice[]>();\n for (const { anchor, notice } of anchors) {\n const notices = at.get(anchor.count) ?? [];\n notices.push(notice);\n at.set(anchor.count, notices);\n }\n const placed: AgentMessage[] = [];\n for (let index = 0; index <= base.length; index += 1) {\n placed.push(...(at.get(index) ?? []));\n if (index < base.length) placed.push(base[index]);\n }\n return { messages: placed, base, anchors };\n}\n\nexport function hasCurrentScopeNotice(messages: readonly AgentMessage[], state: ScopeState): boolean {\n const expected = createScopeNotice(state);\n if (!expected) return true;\n for (let index = messages.length - 1; index >= 0; index -= 1) {\n if (messages[index].role === \"custom\" && (messages[index] as ScopeNotice).customType === NOTICE_TYPE) {\n return contractKey(messages[index]) === contractKey(expected);\n }\n }\n return false;\n}\n\n/**\n * A missing contract is stored as a non-model session entry before it is returned from context.\n * Its fixed prefix anchor, rather than wall-clock ordering or a moving tail, also places a late\n * sendMessage publication at the boundary where the model first saw it. Untouched messages retain\n * their content and relative order. Anchors are retained across off/change notices, not erased.\n */\nexport function reconcileScopeContext(\n messages: readonly AgentMessage[],\n branch: readonly SessionEntry[],\n state: ScopeState,\n sentAt = Date.now(),\n): { messages: AgentMessage[]; anchor?: ContractAnchor } {\n const candidates = readAnchors(branch);\n const placed = project(messages, candidates);\n if (hasCurrentScopeNotice(placed.messages, state)) return { messages: placed.messages };\n const notice = createScopeNotice(state, sentAt);\n if (!notice || !state.entryId) return { messages: placed.messages };\n // A repeated contract starts a new instance. Removing its earlier native notice can\n // invalidate historical anchors whose digests include it, for example after blocked -> active.\n const repeated = placed.messages.some((message) => contractKey(message) === contractKey(notice));\n const prepared = project(messages, placed.anchors, repeated ? [] : [notice]);\n const count = prepared.base.length;\n const digest = prefixDigests(prepared.base, new Set([count])).get(count) as string;\n const anchor: ContractAnchor = { version: 1, stateId: state.entryId, state: state.state, count, digest, sentAt };\n if (repeated) {\n anchor.recovery =\n candidates.reduce(\n (latest, candidate) =>\n candidate.anchor.stateId === state.entryId ? Math.max(latest, candidate.anchor.recovery ?? 0) : latest,\n 0,\n ) + 1;\n if (!Number.isSafeInteger(anchor.recovery)) throw new Error(\"Scope recovery sequence is exhausted\");\n }\n const next = project(messages, [...prepared.anchors, { anchor, notice: anchoredNotice(notice, anchor) }]);\n return { messages: next.messages, anchor };\n}\n", "import type { ExtensionAPI, ToolInfo } from \"@earendil-works/pi-coding-agent\";\nimport type { ScopeState } from \"./state.js\";\n\nconst READERS = new Set([\"read\", \"ls\", \"find\", \"grep\"]);\nconst WRITERS = new Set([\"write\", \"edit\"]);\n\n/** Provenance is a stock-CLI compatibility check, not an implementation attestation. */\nexport function isStockBuiltin(tool: ToolInfo | undefined, name: string): boolean {\n return tool?.sourceInfo.source === \"builtin\" && tool.sourceInfo.path === `builtin:${name}`;\n}\n\nexport function assertUsableLoadout(pi: Pick<ExtensionAPI, \"getAllTools\" | \"getActiveTools\" | \"getSettings\">): void {\n const tools = pi.getAllTools();\n const active = new Set(pi.getActiveTools());\n const codemode = tools.find((tool) => tool.name === \"codemode\");\n if (active.has(\"codemode\") && isStockBuiltin(codemode, \"codemode\") && pi.getSettings().codemode?.mode === \"only\") {\n throw new Error(\n \"Scope cannot start with active codemode.mode=only: codemode can bypass tool guards and hides the direct editing tools. Set Pi's codemode.mode to on and reload first; Scope does not change that setting.\",\n );\n }\n if (\n ![...WRITERS].some(\n (name) =>\n active.has(name) &&\n isStockBuiltin(\n tools.find((tool) => tool.name === name),\n name,\n ),\n )\n ) {\n throw new Error(\n \"Scope requires an active, unmodified built-in write or edit tool; custom replacements are unsupported\",\n );\n }\n}\n\nexport type ToolDecision = { kind: \"pass\" } | { kind: \"target\" } | { kind: \"block\"; reason: string };\n\nexport function decideTool(name: string, tools: readonly ToolInfo[], scope: ScopeState): ToolDecision {\n if (scope.state === \"inactive\") return { kind: \"pass\" };\n const tool = tools.find((candidate) => candidate.name === name);\n if (READERS.has(name) && isStockBuiltin(tool, name)) return { kind: \"pass\" };\n if (scope.state === \"blocked\") {\n return {\n kind: \"block\",\n reason:\n scope.error ?? \"Scope is not initialized; edits remain blocked. Use /scope to repair or /scope off to exit.\",\n };\n }\n if (WRITERS.has(name) && isStockBuiltin(tool, name)) return { kind: \"target\" };\n return {\n kind: \"block\",\n reason: `${JSON.stringify(name)} is blocked while Scope is active. Only stock readers and target-checked write/edit calls are supported. Shell, codemode, delegation, and opaque tools require /scope off.`,\n };\n}\n", "import { randomUUID } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport { basename, dirname, join, resolve } from \"node:path\";\n\nexport interface ScopeSettings {\n showHiddenFiles: boolean;\n}\n\n// Keep one lexical key across first creation and atomic replacement. A file-identity\n// queue can change keys when a missing file appears through a parent symlink.\n// In-process ordering only, shared by stores with the same absolute lexical path.\n// This is not a cross-process lock and does not merge aliases of parent symlinks.\nconst queues = new Map<string, Promise<void>>();\n\nfunction enqueue<T>(path: string, operation: () => Promise<T>): Promise<T> {\n const result = (queues.get(path) ?? Promise.resolve()).then(operation);\n const settled = result.then(\n () => {},\n () => {},\n );\n queues.set(path, settled);\n void settled.then(() => {\n if (queues.get(path) === settled) queues.delete(path);\n });\n return result;\n}\n\nfunction isMissing(error: unknown): boolean {\n return !!error && typeof error === \"object\" && \"code\" in error && error.code === \"ENOENT\";\n}\n\nfunction validateDocument(value: unknown, path: string): Record<string, unknown> {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n throw new Error(`Invalid Scope settings at ${JSON.stringify(path)}: expected a JSON object.`);\n }\n const document = value as Record<string, unknown>;\n if (Object.hasOwn(document, \"showHiddenFiles\") && typeof document.showHiddenFiles !== \"boolean\") {\n throw new Error(`Invalid Scope settings at ${JSON.stringify(path)}: showHiddenFiles must be a boolean.`);\n }\n return document;\n}\n\nasync function readDocument(path: string): Promise<{ document: Record<string, unknown>; mode: number }> {\n let stats: Awaited<ReturnType<typeof fs.lstat>>;\n try {\n stats = await fs.lstat(path);\n } catch (error) {\n if (isMissing(error)) return { document: {}, mode: 0o600 };\n throw error;\n }\n // Atomic rename would replace a settings symlink instead of its referent.\n // Reject it explicitly, including dangling links, rather than surprise the user.\n if (stats.isSymbolicLink() || !stats.isFile()) {\n throw new Error(`Scope settings must be a regular file, not a symlink: ${JSON.stringify(path)}.`);\n }\n const text = await fs.readFile(path, \"utf8\");\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (error) {\n throw new Error(`Invalid Scope settings JSON at ${JSON.stringify(path)}. Repair the file before saving.`, {\n cause: error,\n });\n }\n return { document: validateDocument(parsed, path), mode: Number(stats.mode) & 0o777 };\n}\n\nexport class ScopeSettingsStore {\n readonly #filePath: string;\n #current: ScopeSettings = { showHiddenFiles: false };\n\n constructor(filePath: string) {\n this.#filePath = resolve(filePath);\n }\n\n get current(): ScopeSettings {\n return { ...this.#current };\n }\n\n /** Side-effect-free load; a failed read leaves the last effective value intact. */\n load(): Promise<ScopeSettings> {\n return enqueue(this.#filePath, async () => {\n const { document } = await readDocument(this.#filePath);\n this.#current = { showHiddenFiles: (document.showHiddenFiles as boolean | undefined) ?? false };\n return this.current;\n });\n }\n\n /** Read the latest valid document and change only the owned field. */\n saveShowHiddenFiles(value: boolean): Promise<ScopeSettings> {\n return enqueue(this.#filePath, async () => {\n if (typeof value !== \"boolean\") throw new Error(\"showHiddenFiles must be a boolean.\");\n const { document, mode } = await readDocument(this.#filePath);\n const directory = dirname(this.#filePath);\n await fs.mkdir(directory, { recursive: true });\n const temporary = join(directory, `.${basename(this.#filePath)}.${process.pid}.${randomUUID()}.tmp`);\n let handle: Awaited<ReturnType<typeof fs.open>> | undefined;\n let ownedTemporary = false;\n try {\n handle = await fs.open(temporary, \"wx\", mode);\n ownedTemporary = true;\n await handle.writeFile(`${JSON.stringify({ ...document, showHiddenFiles: value }, null, 2)}\\n`, \"utf8\");\n await handle.close();\n handle = undefined;\n await fs.rename(temporary, this.#filePath);\n ownedTemporary = false;\n } catch (error) {\n const failures: unknown[] = [error];\n if (handle) {\n try {\n await handle.close();\n } catch (closeError) {\n failures.push(closeError);\n }\n }\n if (ownedTemporary) {\n try {\n await fs.unlink(temporary);\n } catch (cleanupError) {\n if (!isMissing(cleanupError)) failures.push(cleanupError);\n }\n }\n if (failures.length > 1) {\n throw new AggregateError(failures, `Scope settings save and cleanup failed at ${JSON.stringify(temporary)}.`);\n }\n throw error;\n }\n // Publish runtime state only after the complete file was renamed successfully.\n this.#current = { showHiddenFiles: value };\n return this.current;\n });\n }\n\n /** Drain prior operations; load/save promises carry their own errors to the caller. */\n flush(): Promise<void> {\n return enqueue(this.#filePath, async () => {});\n }\n}\n", "import type { ExtensionAPI, ExtensionCommandContext, ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { type ContextOptions, type ContextService, registerFileContext } from \"./context-service.js\";\nimport type { FileQuote } from \"./file-context.js\";\nimport { publishFilesWidget } from \"./files-widget.js\";\nimport { registerScope, type ScopeOptions, type ScopeView } from \"./scope/edit-scope.js\";\n\ninterface Flow {\n kind: \"context\" | \"scope\" | \"manager\";\n controller: AbortController;\n promise: Promise<void>;\n}\ninterface Display {\n quotes: readonly FileQuote[];\n scope?: ScopeView;\n}\nexport interface FilesOptions {\n context?: ContextOptions;\n scope?: Pick<ScopeOptions, \"showMenu\">;\n}\n\n/** The single extension factory; services share UI ownership, not permission state. */\nexport async function registerFiles(pi: ExtensionAPI, options: FilesOptions = {}): Promise<void> {\n const flows = new Map<object, Flow>();\n const displays = new Map<object, Display>();\n const display = (ctx: ExtensionContext): Display => {\n let state = displays.get(ctx.sessionManager);\n if (!state) {\n state = { quotes: [] };\n displays.set(ctx.sessionManager, state);\n }\n return state;\n };\n const redraw = (ctx: ExtensionContext) => {\n const state = display(ctx);\n publishFilesWidget(ctx, state.quotes, state.scope);\n };\n const runFlow = (\n ctx: ExtensionContext,\n kind: Flow[\"kind\"],\n run: (ctx: ExtensionContext) => Promise<void> | void,\n ): Promise<void> => {\n const key = ctx.sessionManager;\n const previous = flows.get(key);\n if (previous) {\n if (kind === \"context\" && previous.kind === kind) return previous.promise;\n throw new Error(\n kind === \"scope\" && previous.kind === \"scope\"\n ? \"A Scope menu is already open; close it before issuing another Scope command\"\n : \"A Files menu is already open; close it before opening another file flow\",\n );\n }\n const flow: Flow = { kind, controller: new AbortController(), promise: Promise.resolve() };\n const signal = AbortSignal.any([flow.controller.signal, ...(ctx.signal ? [ctx.signal] : [])]);\n const scoped = Object.create(ctx) as ExtensionContext;\n Object.defineProperty(scoped, \"signal\", { value: signal });\n flows.set(key, flow);\n flow.promise = Promise.resolve()\n .then(() => {\n signal.throwIfAborted();\n return run(scoped);\n })\n .finally(() => {\n if (flows.get(key) === flow) flows.delete(key);\n });\n return flow.promise;\n };\n const cancelFlow = (ctx: ExtensionContext) => {\n const flow = flows.get(ctx.sessionManager);\n if (!flow) return;\n flow.controller.abort(new DOMException(\"Files session or branch changed\", \"AbortError\"));\n // Reload waits for lifecycle hooks: do not wait here for the command that requested it.\n // Services drain native work and writes; aborted UI continuations cannot publish new state.\n if (flows.get(ctx.sessionManager) === flow) flows.delete(ctx.sessionManager);\n };\n // Run before the service handlers so replaced UI cannot outlive restoration.\n pi.on(\"session_start\", (_event, ctx) => cancelFlow(ctx));\n pi.on(\"session_tree\", (_event, ctx) => cancelFlow(ctx));\n pi.on(\"session_shutdown\", (_event, ctx) => cancelFlow(ctx));\n\n const serviceAPI: ExtensionAPI = {\n ...pi,\n registerCommand(name, command) {\n pi.registerCommand(name, {\n ...command,\n handler: (args, ctx) => {\n const route = name === \"file-context\" ? args.trim().toLowerCase() : args.trim();\n const valid =\n name === \"file-context\" ? [\"\", \"browse\", \"remove\"].includes(route) : [\"\", \"off\"].includes(route);\n if (!valid || ctx.mode !== \"tui\") return command.handler(args, ctx);\n return runFlow(ctx, name === \"file-context\" ? \"context\" : \"scope\", (scoped) =>\n command.handler(args, scoped as ExtensionCommandContext),\n );\n },\n });\n },\n registerShortcut(key, shortcut) {\n pi.registerShortcut(key, {\n ...shortcut,\n handler: (ctx) => runFlow(ctx, \"context\", (scoped) => shortcut.handler(scoped)),\n });\n },\n };\n // Enforcement is registered before the first settings await and stays eager.\n const scope = registerScope(serviceAPI, {\n ...options.scope,\n onChange: (ctx, state) => {\n display(ctx).scope = state;\n redraw(ctx);\n },\n });\n const context: ContextService = await registerFileContext(serviceAPI, {\n ...options.context,\n onChange: (ctx, quotes) => {\n display(ctx).quotes = quotes;\n redraw(ctx);\n },\n });\n\n pi.registerCommand(\"files\", {\n description: \"Browse file context and manage allowed edit targets\",\n getArgumentCompletions: (prefix) =>\n [\n { value: \"browse\", label: \"browse\", description: \"Open the context browser\" },\n { value: \"off\", label: \"off\", description: \"End the editing restriction\" },\n ].filter((item) => item.value.startsWith(prefix)),\n handler: async (args, ctx) => {\n const route = args.trim();\n if (route !== \"\" && route !== \"browse\" && route !== \"off\") throw new Error(\"Usage: /files [browse|off]\");\n if (ctx.mode !== \"tui\" || !ctx.hasUI) throw new Error(\"/files requires Pi TUI mode\");\n await runFlow(ctx, route === \"browse\" ? \"context\" : \"manager\", async (scoped) => {\n if (route === \"browse\") return context.browse(scoped);\n const commandCtx = scoped as ExtensionCommandContext;\n if (route === \"off\") {\n scope.off(commandCtx);\n return;\n }\n const { showFilesMenu } = await import(\"./files-menu.js\");\n scoped.signal?.throwIfAborted();\n await showFilesMenu(commandCtx, context, scope);\n });\n },\n });\n pi.on(\"session_shutdown\", (_event, ctx) => {\n displays.delete(ctx.sessionManager);\n if (ctx.hasUI) ctx.ui.setWidget(\"files\", undefined);\n });\n}\nexport default registerFiles;\n"],
5
+ "mappings": "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",
6
+ "names": ["realpath", "isAbsolute", "relative", "resolve", "sep", "project", "join", "resolve", "pathToFileURL", "getAgentDir", "resolve", "join", "getAgentDir", "resolve", "pathToFileURL"]
7
+ }
@@ -0,0 +1,142 @@
1
+ # Edit scope reference
2
+
3
+ Pi Files retains the checked-file picker and native-tool guard from Edit Scope. Start with the [README](../README.md) for installation and the shared manager. Allowed edit targets and queued context are independent: permission changes do not load file contents or clear context.
4
+
5
+ ## Manager and command behavior
6
+
7
+ Open scope management through `/files`, or use the dedicated `/edit-scope` manager (`/scope` is an identical compatibility alias). Select or replace targets, reserve one new file, change discovery preferences, or read the complete Status list and Help. `/files off`, `/edit-scope off`, and `/scope off` explicitly end the same restriction without consuming queued context.
8
+
9
+ Scope commands and permission changes require TUI mode, an idle agent, and no pending messages. Unknown or trailing arguments are rejected. RPC, print, and JSON commands fail before custom UI; restored tool enforcement works in all modes. Pi reports command errors through RPC `extension_error` events or CLI stderr. A handled command error does not imply a nonzero process exit code.
10
+
11
+ Successful activation, replacement, or exit returns to the prompt. Closing a manager, cancelling selection, Escape, Ctrl+C, interrupting work, or ending an agent run does **not** end an applied restriction. An empty selection leaves it unchanged. Reapplying the same lexical paths and canonical targets is a no-op.
12
+
13
+ The combined widget shows context and restriction separately, including active or blocked scope and its exit command. Scope Status lists every target, including reserved paths, and explains deferred creation.
14
+
15
+ ## File selection
16
+
17
+ Discovery starts at cwd, not the Git root. It scans breadth-first, completes each directory level before the next, and keeps only one directory handle open at a time. The completed inventory is sorted by absolute path. It skips symlink entries, nonregular files, and these exact directory names at every depth:
18
+
19
+ ```text
20
+ .git .hg .svn node_modules .venv venv __pycache__
21
+ build coverage dist target .next
22
+ ```
23
+
24
+ Discovery does not read `.gitignore`. Scan errors and work limits fail the scan instead of showing a partial inventory. Use a narrower cwd when the scan exceeds its limits. Current targets appear first and remain available even when hidden, excluded, outside cwd, or not yet created. Filtering and checks change only the draft until explicit apply.
25
+
26
+ The list contains files only. The fzf-inspired interaction uses Pi's native fuzzy search, including whitespace and directory fragments; typing never adds a path. Default keys are Tab to toggle and move down, Shift+Tab to toggle and move up, arrows to move only, and Enter to apply checks. Marking stops at the first or last match; arrow navigation still wraps. Repeated marking at a boundary toggles the same file, and marking with no match does nothing. An empty checked set stays open with an explanation; it neither selects the highlight nor ends a restriction. Space is search text under default bindings.
27
+
28
+ Project labels are cwd-relative. Ordinary names are unquoted; ambiguous whitespace and escaped identities stay quoted. External paths remain absolute with an `(outside)` marker. **Full paths**, using Pi's `app.session.togglePath` binding (default Ctrl+P), changes only the view and resets when the picker closes. Query, cursor, raw absolute IDs, checks, and canonical validation remain unchanged. A clipped selected label gets an exact Path preview; canonical redirects retain their own detail.
29
+
30
+ ### Add an existing target
31
+
32
+ Use **Add file…** to enter an external, hidden, or excluded file in a separate field. The default key is Ctrl+O, with Alt+O as the fallback if native selection, editing, or host input reserves Ctrl+O. If both conflict, no key is advertised; the fullscreen button remains available.
33
+
34
+ Accepted forms include absolute, home, file URL, and cwd-relative paths, including bare relative filenames:
35
+
36
+ ```text
37
+ ~/src/another_project/AGENTS.md
38
+ ~/.plan/PLAN_A.md
39
+ /absolute/path/PLAN_B.md
40
+ ~/.config/atuin/config.toml
41
+ ```
42
+
43
+ The configured Input submit key (default Enter) starts lookup; typing does not. Search text is never reinterpreted as a path. A successful add checks the resolved path, clears only the path field, and returns to the list with search and earlier checks intact. It focuses the added file if it matches the query; otherwise the matching highlight stays and an Added status appears. Repeat, then apply with the configured selection-confirm key (default Enter) or **Start restriction** / **Apply changes**. There is no function-key completion accelerator or completion row.
44
+
45
+ Missing paths, directories, hard links, and invalid input retain the field and draft with an error. Back (default Escape) returns to search without changing checks. During lookup, Back aborts only that lookup and prevents new work until it drains. Cancelling the file list abandons the draft, not the applied restriction. No selection action creates content or parent directories.
46
+
47
+ Manually added files must still exist at selection completion and final commit. Normalized duplicates are idempotent. Canonical aliases are deduplicated, without removing the existing-file requirement. Previously approved missing reservations remain valid. Existing-file and canonical checks run again before apply.
48
+
49
+ ### Reserve a new target
50
+
51
+ **New file…** / **Use only a new file…** replaces the complete allowed-file set with one reviewed target. Relative input uses cwd. Existing targets are rejected without discarding the input draft. Review marks both canonical redirects and outside-cwd lexical or canonical paths. Selection only reserves the target: the first normal stock `write` creates the file and required parents. Opening or cancelling this flow creates no target content.
52
+
53
+ Target resolution follows native `write` and `edit` behavior for dot segments, leading `@`, `~`, file URLs, and Unicode whitespace. Exact unusual filenames use file URLs, which model notices supply. Escaped display labels do not change raw targets. Read-only macOS filename fallbacks do not apply to mutation targets.
54
+
55
+ ## Target validation and recovery
56
+
57
+ Version-1 snapshots belong to the active session branch, not settings. Reload, resume, fork, and tree navigation restore that branch's last snapshot. A new session without a snapshot starts inactive. Compaction does not release the restriction.
58
+
59
+ Each mutation freshly rechecks both the selected reservation and candidate. A missing path resolves through its nearest existing parent plus the missing suffix. A changed symlink cannot expand the approved set. Nonregular targets and files with multiple hard links are rejected even if a hard-link alias was selected. A dangling symlink is not a missing target that `write` may create. Metadata or access errors also do not mean a path is missing.
60
+
61
+ Uninitialized, invalid, uncertain, or unsupported restored state blocks edits. Repair the allowed set through `/files` or the dedicated scope manager, or explicitly end it with `/files off`. A broken selected alias can be removed and replaced with its real path; this changes the saved reservation even when the canonical target is unchanged. Do not delete settings or session entries to force unrestricted recovery. These checks do not provide race-free containment against other processes.
62
+
63
+ ## Fixed limits
64
+
65
+ These are safety limits, not settings:
66
+
67
+ | Item | Limit |
68
+ | --- | --- |
69
+ | Discovered files | 5,000 |
70
+ | Visited directory entries | 20,000, including hidden or excluded names encountered |
71
+ | Directory depth | 64 below cwd; cwd is depth zero |
72
+ | Selected entries | 100 before canonical deduplication |
73
+ | Distinct manually added paths | 100 per picker, including later-unchecked entries |
74
+ | Serialized scope snapshot | 24,000 bytes |
75
+ | Model notice | 48,000 bytes |
76
+
77
+ Unlike the bounded context browser, scope never treats a truncated inventory as complete authorization input.
78
+
79
+ ## Settings and persistence
80
+
81
+ The user store is `<getAgentDir()>/pi-scope.json`, normally `~/.pi/agent/pi-scope.json`:
82
+
83
+ ```json
84
+ {
85
+ "showHiddenFiles": false
86
+ }
87
+ ```
88
+
89
+ `showHiddenFiles` accepts a boolean and defaults to `false`. Effective settings use defaults followed by recognized user fields. It changes scope discovery, not context discovery or the active restriction. Open `/files` → **Settings** or the dedicated `/edit-scope` → **Settings** route. UI changes apply and save immediately without reload; Back or Escape does not undo a save. This preference remains editable while context is queued in the common settings screen. Manual file changes require reload. Settings reload on session start and tree navigation.
90
+
91
+ There are no project overrides, extension-specific environment variables, policy switches, or saved file groups. The retained `pi-scope.json` basename is a deliberate compatibility exception. There is no filename migration; neither `pi-edit-scope.json` nor `pi-files.json` is read.
92
+
93
+ Missing-file reads create nothing. The top level must be a JSON object. Malformed JSON or an invalid owned field produces an error and leaves the prior effective value intact; repair the file before saving. Invalid content is not replaced with defaults. Settings-file symlinks and nonregular files are unsupported.
94
+
95
+ Saves read the latest valid document, preserve unknown fields, and publish a same-directory temporary file by rename. First creation uses private permissions; replacement retains the existing file's permissions. Effective state changes only after publication succeeds. A failed save preserves the previous effective value and file and never ends the restriction.
96
+
97
+ Reads, saves, and teardown are ordered within a Pi process for the same absolute lexical path, including first creation and replacement. A failed operation does not prevent later reads or saves. Teardown drains preceding preference operations. This is not a cross-process lock and does not coordinate aliases of a parent directory. Native target mutations retain Pi's own queue.
98
+
99
+ ## Protection boundary
100
+
101
+ The supported boundary is stock Pi CLI tool implementations with trusted extensions. Public source metadata cannot attest to arbitrary SDK `baseToolsOverride` or custom `prepareLoadout` implementations; those embeddings are unsupported. The guard is in-process, not an OS sandbox, and does not grant permissions or override another guard.
102
+
103
+ While restricted:
104
+
105
+ | Stock tool | Behavior |
106
+ | --- | --- |
107
+ | `read`, `ls`, `find`, `grep` | Available for unselected files |
108
+ | `write`, `edit` | Require an exact, freshly validated selected canonical target |
109
+ | `bash`, `powershell` | Blocked; arbitrary shell commands cannot be proved read-only |
110
+ | `codemode`, `tool_search`, custom or delegation tools | Blocked, including tools with `readOnlyHint` |
111
+
112
+ Only recognized, unmodified native tools are accepted. A replacement is not trusted because its name matches. At least one stock direct writer must be active. Active native `codemode.mode=only` is unsupported: activation is refused, or scope becomes blocked on restore or the next request after a loadout change. Pi Files does not activate tools or change core settings to work around the configuration.
113
+
114
+ Native nested tool calls use the same guard, but this does not make an opaque parent safe. Codemode also has model, image, and temporary-artifact paths outside nested tool calls; it is not accepted as a generic router.
115
+
116
+ Selected paths and canonical redirects enter the model notice; selection does not load their contents. The guard does not contain user-issued `!` commands, RPC shell commands, existing background processes, trusted extension code, later argument-mutating handlers, other programs, or filesystem changes between validation and the native operation. Pi's own session, settings, cache, and artifact writes are not target-file editing calls. The user-controlled [external editor](file-context-reference.md#external-editing) remains outside the guard.
117
+
118
+ ## Session and model-contract compatibility
119
+
120
+ The persisted identities remain `scope-state`, `scope-contract`, and `scope-context-anchor`, with snapshot version 1 and original session entry IDs. `src/scope/state.ts` owns snapshots; `src/scope/contracts.ts` owns notices and anchors. Historical notice bytes retain the `Scope` name and `/scope` instructions. Rebranding those bytes would invalidate saved prefix digests. See [migration](migration.md) before changing resources or restoring a predecessor.
121
+
122
+ Scope adds no tools and changes no active tool definitions, snippets, guidelines, or leading system prompt. Explicit activation, changed reservation, or exit appends a fixed model notice without starting a turn. Reapplying an identical reservation is a no-op. Ordinary requests preserve the existing serialized model-visible prefix.
123
+
124
+ When a required notice is absent or compacted away, recovery appends one deterministic tail contract and persists a non-model prefix anchor. Ordinary requests reuse that boundary; deferred publication retains its first-seen boundary rather than moving past newer output. Historical contracts remain ordered after replacement or exit. Original entry IDs, state IDs, and old anchors without a recovery sequence remain valid. A repeated logical contract can have a distinct recovery sequence without erasing earlier history. Uncertain state or anchor publication must not open the guard.
125
+
126
+ ## UI compatibility and cancellation
127
+
128
+ Scope's interactive screens retain full-width bottom overlays in regular and fullscreen TUI modes. This deliberately differs from editor-area dialogs: Pi does not expose allocated dock height, and an editor child can be clipped by widgets or footers. Overlays can cover those components temporarily but do not remove them. Only the owned overlay closes; unrelated overlays keep their ownership and position. The common settings screen follows Pi's `SettingsList` pattern.
129
+
130
+ The dedicated selector keeps in-place validation, a live count, exact path previews, separate Add input, and the checked draft after failure. Action buttons stay outside the file list and support fullscreen clicks. Very short screens prioritize the focused file, search field, and reachable primary hints. Read-only reviews have no activatable cursor. Configured selection and editing actions take priority over additive shortcuts. Hints advertise only usable keys; invalid strings are not repaired. Earlier consuming input listeners, transcript search, debug actions, and another focused overlay keep native priority.
131
+
132
+ Ctrl+C remains hard cancel outside paste. Input retains native editing, undo-before-submit, Backspace, cursor, and submission behavior. Whole or fragmented bracketed paste cannot toggle, apply, add, change views, or cancel. A paste retains its original input owner across panel changes; pending validation discards pasted input. Pointer actions are blocked during paste.
133
+
134
+ File clicks retain the originally pressed raw file ID if scrolling or canonical detail changes layout. Query, panel, and resize changes cancel unfinished gestures. Display masking preserves input and mouse geometry without changing the submitted raw path. Paths and drafts are untrusted terminal input and are escaped at display boundaries.
135
+
136
+ Scanning, lookup, and validation disable new work. Cancellation, disposal, session replacement, and shutdown abort owned tasks and drain in-progress operations. Final apply rechecks idle state, revision, ownership, canonical targets, and usable loadout. Cancelling a flow never releases an applied restriction. Filesystem calls already in progress must settle before cleanup completes; no detached I/O is implied.
137
+
138
+ ## Verification limits
139
+
140
+ The moved suites exercise native path behavior, the Pi loader and tool pipeline, cancellation, settings failures, input/paste, and model-prefix compatibility. Test presence or predecessor results are not new merge evidence. Current results and remaining checks belong in [verification](verification.md).
141
+
142
+ Deterministic tests do not prove physical key delivery, IME placement, terminal usability, live-provider behavior, or untested operating systems. The physical-terminal and interactive `/reload` handoff remains separate from offline loader checks. Do not issue built-in `/reload` as an RPC prompt command: it can become model input rather than safely reload the runner.
@@ -0,0 +1,91 @@
1
+ # File context reference
2
+
3
+ Pi Files retains the File Context explorer and immutable snapshot workflow. Start with the [README](../README.md) for installation and the shared manager. Browsing, quoting, whole-file references, review, and removal never grant edit permission.
4
+
5
+ ## Browser workflow
6
+
7
+ 1. Run `/files` and open context browsing, or use `/files browse`, `/file-context browse`, or the configured shortcut. `/file-context` keeps the dedicated context manager and its **Add context snippet** action. Every browser route starts the same cancellable project scan.
8
+ 2. Use Up/Down to select an immediate child folder or file, then Enter to open it. When the search field is empty, Escape, Left, or Backspace returns to the parent folder. Type to fuzzy-search file names across discovered folders in relevance order. Tab on a file inserts a normal whole-file `@path` reference; Enter opens its quote preview.
9
+ 3. Ctrl+F switches between file-name and content search. Content search is literal and case-insensitive by default. Alt+C toggles case sensitivity; Alt+F toggles ordered fuzzy matching. The current states stay visible above results.
10
+ 4. Content results are highlighted path-and-line cards. Up/Down selects a card, Tab inserts a whole-file reference, and Enter previews the file at the matching line. Escape from preview restores the query, selected result, and scroll position.
11
+ 5. In preview, Space anchors a selection; Up/Down extends the range. Enter queues the snapshot and closes. The `a` action queues it and returns to the originating folder or results so browsing can continue. The adaptive footer shows selection, capacity, navigation, Git actions, external editing, and `?` help.
12
+ 6. Use Pi's `app.editor.external` action (default Ctrl+G) to edit the current worktree file. See [External editing](#external-editing) before use.
13
+ 7. Press `?` in preview to review actions. In a Git worktree, `[`/`]` selects changed hunks, `b` shows current-line blame, `h` opens file history, `r` opens a commit/branch/tag, and `d` reviews and adds explicit diff context. Historical previews are read-only.
14
+ 8. Open context review through `/files` or `/file-context` → **Review selected context**. Enter opens the exact snapshot before offering **Remove from next prompt**. `/file-context remove` is the compatibility review route; it does not delete files. Submit the question normally to attach queued snippets once, in order, then clear the queue.
15
+
16
+ The explorer and the scope checked-file picker are separate components. In the explorer, Tab inserts a whole-file reference; it does not check an allowed edit target. No action both queues context and authorizes editing.
17
+
18
+ ## Cancellation and queue lifetime
19
+
20
+ Escape returns from a preview to its originating folder or results. With an empty file-search field, Escape from a nested folder returns to its parent. A menu-opened browser returns to its menu at the root; a direct browser route cancels there. Ctrl+C closes the flow. Either cancel key stops project scanning; cancellation does not open a stale explorer.
21
+
22
+ Review cancellation does not remove snapshots. Each queued snippet retains the text visible when selected; submission does not silently reread changed files. The next ordinary prompt consumes all queued snippets together. Extension-triggered custom turns do not consume them.
23
+
24
+ The queue is session-owned and in memory. It does not survive `/reload`, session replacement, or shutdown. Same-session tree navigation does not apply a blanket queue reset. Scope restoration has a separate branch-owned lifetime. Starting, replacing, or ending scope does not clear context; selecting or attaching context does not change scope.
25
+
26
+ ## Quote format and Git provenance
27
+
28
+ The agent receives explicit blocks such as:
29
+
30
+ ```xml
31
+ <user_file_quote path="src/runtime.ts" lines="12-18" git_head="a1b2c3d4..." git_branch="main" git_status="modified (unstaged)" git_blob="e5f6..." content_sha256="9abc..." source="worktree" git_base="HEAD">
32
+ selected content
33
+ </user_file_quote>
34
+ ```
35
+
36
+ Non-Git quotes retain their original `path` and `lines` attributes. Git quotes add ordered optional provenance: HEAD at selection time, branch, file status, revision or baseline, tracked blob when available, source (`worktree`, `revision`, or `git_diff`), and SHA-256 of the exact attached text. HEAD alone does not identify uncommitted content; `content_sha256` identifies the snapshot. The session message identity remains `file-context-quotes`.
37
+
38
+ Token counts use `ceil(UTF-8 bytes / 4)`. They are deterministic estimates, not provider billing predictions. Diff context is never attached automatically. Git failures fall back to filesystem-only browsing where possible. File history shows up to 20 recent commits. Untracked files have status and provenance but no HEAD diff hunk until tracked.
39
+
40
+ ## External editing
41
+
42
+ External editing is a user-controlled operation **outside the native-tool scope guard**. The editor runs with user permissions and may modify other files or access external services. An active scope neither authorizes nor contains it.
43
+
44
+ The action requires project trust and a canonical, project-contained regular worktree file. It rejects a final symlink. Pi's file mutation queue covers the complete editor lifetime. Pi Files pauses its TUI, waits for editor exit, restores the terminal, then reloads bounded text and Git state. A reload failure stays visible in preview. Cancellation and disposal cancel owned work; filesystem operations already in progress must settle before cleanup completes.
45
+
46
+ Only a current worktree File Preview exposes this action. Historical revisions, Git diffs, history lists, revision input, file lists, and search results do not. Historical text is never written over the current worktree by this action.
47
+
48
+ Pi's effective `app.editor.external` keybinding and `externalEditor` setting are authoritative. Configure them in Pi's `keybindings.json` and `settings.json`, not an extension file. A remapped action takes priority over additive preview letter shortcuts, and the old Ctrl+G is not retained. Use a wait option such as `code --wait` when an editor would otherwise return before editing ends.
49
+
50
+ On Windows, editor targets containing command-shell metacharacters are rejected rather than passed to Pi's shell-compatible editor launch.
51
+
52
+ ## Settings
53
+
54
+ The optional user store is `<getAgentDir()>/pi-file-context.json`, normally `~/.pi/agent/pi-file-context.json`:
55
+
56
+ ```json
57
+ {
58
+ "openShortcut": "ctrl+shift+x"
59
+ }
60
+ ```
61
+
62
+ `openShortcut` accepts a valid Pi key identifier or `null` to disable the shortcut. Strings are trimmed and normalized to lowercase. Recognized modifiers are `ctrl`, `shift`, `alt`, and `super`, without duplicates. Supported bases include letters, digits, punctuation, standard navigation/editing keys, and unmodified F1–F12. Examples include `ctrl+shift+x`, `f8`, and `ctrl+alt+f`; modified function-key strings are not accepted by this validator. Use `/files browse` when the shortcut is disabled.
63
+
64
+ Effective settings use defaults followed by recognized user fields. There are no project overrides or extension-specific environment variables. The retained basename is a deliberate compatibility exception; there is no `pi-files.json` or filename migration.
65
+
66
+ Open `/files` → **Settings** → **Open shortcut** to save. Registration requires reload, so only this action is blocked while snippets are queued. The common settings screen remains available for the immediate hidden-file preference. The dedicated `/file-context` manager retains its original shortcut-only settings route, which is unavailable while context is queued. A successful shortcut save reloads Pi automatically. If reload fails after saving, the file stays saved; run `/reload` to apply it. Manual JSON changes also require `/reload`.
67
+
68
+ The default Ctrl+Shift+X requires Kitty keyboard protocol or modifyOtherKeys to distinguish it from Ctrl+X. Use F8 if the terminal cannot do so. Choose a key that does not conflict with Pi or another extension. Ctrl+F is the explorer's search-mode toggle; Pi reserves Ctrl+X for `app.message.copy` by default, so remap that action before assigning Ctrl+X here. The extension does not replace another extension's custom editor.
69
+
70
+ Missing settings use the default without creating a file or parent directory. Invalid JSON, invalid values, oversized files, and unsupported file types produce a warning, leave the source unchanged, and use the default on load. Repair the file and reload before saving; the affected shortcut settings remain read-only until then. The document must be a UTF-8 JSON object no larger than 64 KiB. Settings-file symlinks are unsupported.
71
+
72
+ Saves read the latest valid document, preserve unknown fields, and publish a same-directory temporary file by rename with private permissions. Saves run in request order within one Pi process; reads await preceding writes, and shutdown drains them. A failed save leaves the previous effective shortcut and file intact. This is not cross-process coordination. Settings failure never releases scope or discards queued context.
73
+
74
+ ## Security and data limits
75
+
76
+ Only trusted extensions should be loaded. Paths and canonical targets are checked against the real project root before reading. Discovery skips symlinks and common generated directories, visits shallow files before deeper ones, and stops at 5,000 files. It uses a built-in ignore list, not `.gitignore`. This bounded inventory must not be used as a complete scope authorization inventory.
77
+
78
+ | Item | Limit or behavior |
79
+ | --- | --- |
80
+ | Preview and searched file | 1,000,000 bytes; NUL-containing files are treated as binary |
81
+ | File-name or content-search query | 256 characters |
82
+ | Content-search cards | 100; truncation and unreadable, oversized, or binary skips are reported |
83
+ | One snapshot | 500 lines and 50,000 bytes |
84
+ | Queued snapshots | Eight, with 100,000 aggregate text bytes |
85
+ | Git command | Five-second timeout and 1,100,000-byte output bound |
86
+
87
+ Content search uses the same discovery, size, canonical-project-path, and no-symlink boundaries as preview loading. Searches and file opens are cancelled when replaced. Search scans discovered files natively and sequentially, so large projects and no-match queries can be slower than indexed or external tools. Fuzzy content search matches characters in order on one line; it is not semantic or multiline search.
88
+
89
+ Terminal controls are escaped before paths, Git refs, authors, summaries, search context, or content are displayed; raw snapshot payloads are not changed for display. Git runs read-only without a shell, pager, external diff, or text conversion. Refs resolve to a commit before loading; historical files have the same size and binary guards. Blame includes author names, not email. Commit summaries, diffs, and file text can contain sensitive data; inspect them before sending to a model.
90
+
91
+ Line selection is keyboard-only; mouse drag selection is not implemented. Exact review and repeated removal are supported, but bulk clear, undo, and reorder are not.