feat: configurable shell keymap, AI permissions, GUI foundation#12
Merged
Conversation
…e 8 M1) Three lisp-machine fixes surfaced during dogfooding with Claude Code: 1. Shell exit sequence (Ctrl-\ Ctrl-n) was hardcoded in Rust, bypassing the keymap system entirely. Now ShellInsert participates in keymap dispatch like every other mode — users can rebind via init.scm: (define-key "shell-insert" "C-c C-c" "shell-normal-mode") 2. AI permission tier was hardcoded to Shell. Now configurable via config.toml (auto_approve_tier) and MAE_AI_PERMISSIONS env var. Tiers: readonly, standard, trusted (default), full. 3. Renderer trait extracted as HAL — TerminalRenderer implements it, new mae-gui crate (winit + skia-safe) provides GPU-backed alternative. InputEvent type in mae-core abstracts over crossterm/winit input. New crate: mae-gui (optional, behind "gui" feature flag) New tests: 26 (total: 1,329) Clippy: 0 warnings Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds --gui CLI flag (prints foundation-only message for now), MAE_AI_PERMISSIONS to --help env vars section. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
mae-gui depends on skia-safe which requires system fontconfig/freetype libs not available in the GitHub Actions Ubuntu runner. Since the GUI crate is behind an optional feature flag and can't run in CI anyway (no display server), exclude it from check/test/clippy steps. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire up `mae --gui` with a real event loop using winit's pump_app_events() integrated into the tokio current_thread runtime. Each iteration pumps winit events (~16ms for 60fps), then yields to tokio::select! for AI/LSP/DAP/MCP channel draining. Key changes: - WinitCallback implements ApplicationHandler (window init, keyboard input, resize, close, modifier tracking) - GUI reuses existing key dispatch via keypress_to_crossterm() bridge - run_gui_loop() mirrors terminal loop's shell/agent/intent handling - Fix init.scm load: inject editor state before evaluation so *buffer-name* et al. are defined - Fix --gui flag being parsed as a file path argument Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Skia raster surface was rendering pixels into memory but never presenting them to the OS window. Add softbuffer to blit the pixel buffer to the winit window surface on each end_frame(). Also update ROADMAP.md with a GUI feature status table showing what's implemented (window, input, status bar, channels) vs. what's planned (cursor, gutter, syntax colors, images, PDF, mouse). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Shell PTY now receives the owning window's dimensions instead of the full terminal size. Dynamic resize each tick detects split/focus changes. Agent auto-approval: write .claude/settings.local.json with mcp__mae-editor wildcard on shell spawn (auto_approve_tools config, MAE_AGENTS_AUTO_APPROVE env override). Contributor guide in agents.rs module docs. New ai_permissions tool lets the AI introspect its own permission tier. Register agent-list/agent-setup as interactive commands. Update KB concept:agent-bootstrap docs. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
parse_key_seq (used by Scheme define-key) now handles angle-bracket notation like <F1>, <Esc>, <C-x>. Previously only bare named keys (escape, tab) and C-/M- prefixes worked — <F1> was parsed as four literal characters. This fixes user init.scm keybindings like: (define-key "shell-insert" "<F1>" "shell-normal-mode") Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Log a warning when a Scheme (define-key ...) call produces an empty key sequence instead of silently skipping. Also log key count at debug level for successful bindings to aid troubleshooting. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… tools - Fix 3 bugs where editor.mode wasn't synced after focus/buffer changes: focus-left/right/up/down, alternate-file, and command palette SwitchBuffer now call sync_mode_to_buffer() to match ShellInsert/Normal to BufferKind - Fix invisible cursor in AI conversation input by splitting the input line into spans and applying ui.cursor style to the character under cursor - Add missing buffer_name parameter to buffer_read/write tool definitions (implementations already supported it via resolve_buffer_idx) - Add set_option to ai_tool_names array (was missing, worked by accident) - Add lsp_workspace_symbol tool: search symbols by name across the workspace - Add lsp_document_symbols tool: list all symbols in a document hierarchy - Full stack for new LSP tools: LspIntent, protocol types, client methods, manager dispatch, deferred AI tool handling, MCP re-export (42 tools total) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… update - Extract ai_event_handler.rs (217 lines): unified AI event dispatch, deferred LSP timeout, MCP request handling — eliminates terminal/GUI duplication - Extract shell_lifecycle.rs (185 lines): spawn, resize, reset, close, poll, input drain, viewport cache — eliminates terminal/GUI duplication - Delete handle_ai_event_gui (120 lines of pure duplication) - main.rs: 2,247 → 1,611 lines (-28%) - Add Conversation::end_streaming() replacing 7 inline two-field resets - Use std::mem::take over .drain(..).collect() in shell lifecycle (4 sites) - ROADMAP.md: 1,329 → 1,369 tests, Phase 8 M2 features documented, Tier 2 auto-reload + :set marked DONE - CLAUDE.md: Phase 4 LSP AI tools marked complete, Phase 8 updated Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move input_locked check from handle_key to event loop level so it covers ShellInsert mode (was bypassed when user was in a shell buffer) - Add same check to GUI event loop (WinitCallback) - Add input_lock AI/MCP tool: external agents (Claude Code) can now lock/unlock editor input during multi-step operations like self-tests - Update self-test prompt to call input_lock before/after test execution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
MCP/AI tool calls can modify buffer content, leaving window cursor_row past the end of the buffer. The next render/dispatch then calls rope.line(cursor_row) which panics with "called Option::unwrap() on a None value" in ropey. Fix: add Editor::clamp_all_cursors() and call it at the top of both terminal and GUI event loops, before ensure_scroll/render. This is a safety net that catches all stale-cursor bugs regardless of source. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two related improvements to the MCP bridge: 1. Session-scoped input lock: MCP tool calls now auto-lock keyboard input while active and auto-unlock after 500ms of inactivity. Prevents user keystrokes from leaking into buffers during AI agent operations. Esc/Ctrl-C still provides immediate unlock. 2. Deferred MCP tools: LSP-dependent tools (lsp_definition, lsp_references, lsp_hover, lsp_workspace_symbol, lsp_document_symbols) now resolve asynchronously via the MCP bridge instead of failing with "not supported". Uses the same DeferredReply pattern as the built-in AI path with 15s timeout. Also includes: visual anchor/last_visual clamping in clamp_all_cursors(), search match recomputation after buffer_write operations. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
cuttlefisch
pushed a commit
that referenced
this pull request
May 26, 2026
Add stance #12 documenting the Chibi-Scheme-derived exception system: unified handler stack, continuable tagging, handler isolation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Three lisp-machine fixes surfaced during dogfooding MAE with Claude Code in the embedded terminal:
Shell exit sequence now uses keymap system —
Ctrl-\ Ctrl-nwas hardcoded in Rust, bypassing the keymap entirely. ShellInsert now participates in keymap dispatch like every other mode. Users can rebind via(define-key "shell-insert" "C-c C-c" "shell-normal-mode")in init.scm.AI permission tier is configurable —
PermissionPolicy::default()auto-approved up toShellwith no way to setPrivileged. Now configurable viaconfig.toml(auto_approve_tier) andMAE_AI_PERMISSIONSenv var. Tiers:readonly,standard,trusted(default),full.Renderer trait extracted + GUI crate created —
Renderertrait provides the backend-agnostic HAL that CLAUDE.md promised.TerminalRendererimplements it. Newmae-guicrate (winit + skia-safe) provides GPU-backed alternative with monospace text rendering, theme color mapping, and winit→KeyPress input translation. Optional behindguifeature flag.New files
crates/core/src/input.rs—InputEventenum (backend-agnostic input)crates/gui/— new crate:Cargo.toml,lib.rs,canvas.rs,input.rs,text.rs,theme.rsKey changes
crates/renderer/src/lib.rs—Renderertrait +TerminalRendererimplements itcrates/core/src/editor/keymaps.rs—shell-insertkeymap with defaultC-\ C-nbindingcrates/mae/src/config.rs—auto_approve_tierfield +resolve_permission_policy()crates/mae/src/main.rs— keymap-based shell key handling,--guiflag, permission configTest plan
cargo fmtcleanCtrl-\ Ctrl-nexits ShellInsertmaeworks as before(define-key "shell-insert" ...)rebinding works in init.scmMAE_AI_PERMISSIONS=full maeauto-approves all tools🤖 Generated with Claude Code