feat: file-read gate allows Edit, add legacy-peer-deps for grammar install

- Change file-read gate from deny to allow with limit:1, injecting the
  observation timeline as additionalContext. Edit now works on gated files
  since the file registers as "read" with near-zero token cost.
- Add updatedInput to HookResult type for PreToolUse hooks.
- Add .npmrc with legacy-peer-deps=true for tree-sitter peer dep conflicts.
- Add --legacy-peer-deps to npm fallback paths in smart-install.js so end
  users without bun can install the 24 grammar packages.
- Rebuild plugin artifacts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Alex Newman
2026-04-07 14:06:07 -07:00
parent 7996dfd5cd
commit d0676aa049
7 changed files with 263 additions and 184 deletions
+12 -8
View File
@@ -138,11 +138,11 @@ function formatFileTimeline(observations: ObservationRow[], filePath: string): s
const lines: string[] = [
`Current: ${currentDate} ${currentTime} ${currentTimezone}`,
`Read blocked: This file has prior observations. Choose the cheapest path:`,
`This file has prior observations. Only line 1 was read to save tokens.`,
`- **Already know enough?** The timeline below may be all you need (semantic priming).`,
`- **Need details?** get_observations([IDs]) — ~300 tokens each.`,
`- **Need current code?** smart_outline("${safePath}") for structure (~1-2k tokens), smart_unfold("${safePath}", "<symbol>") for a specific function (~400-2k tokens).`,
`- **Need to edit?** Use smart tools for line numbers, then sed via Bash (Edit requires Read, but you already have the context).`,
`- **Need full file?** Read again with offset/limit for the section you need.`,
`- **Need to edit?** Edit works — the file is registered as read. Use smart_outline("${safePath}") for line numbers.`,
];
for (const [day, dayObservations] of sortedDays) {
@@ -233,15 +233,19 @@ export const fileContextHandler: EventHandler = {
return { continue: true, suppressOutput: true };
}
// Deny the read with the timeline as the reason — Claude sees the timeline
// and decides: work from semantic priming, use get_observations(), or ask user to allow read
// Allow the read with limit: 1 line — just enough for Edit's "file must be read"
// check to pass, while keeping token cost near zero. The observation timeline
// gives Claude full context about prior work on this file.
const timeline = formatFileTimeline(dedupedObservations, filePath);
return {
hookSpecificOutput: {
hookEventName: 'PreToolUse',
additionalContext: '',
permissionDecision: 'deny',
permissionDecisionReason: timeline,
additionalContext: timeline,
permissionDecision: 'allow',
updatedInput: {
file_path: filePath,
limit: 1,
},
},
};
} catch (error) {
+1
View File
@@ -22,6 +22,7 @@ export interface HookResult {
additionalContext: string;
permissionDecision?: 'allow' | 'deny';
permissionDecisionReason?: string;
updatedInput?: Record<string, unknown>;
};
systemMessage?: string;
exitCode?: number;