claudedo/src/claudedo/keys.py
disqualifier 2fa3abab63 v0.2.0: context injection + system daemon-control namespace
context injection — named reference blurbs from contexts.toml injected ahead of a
dictated instruction, read-before-send (never auto-submits):
- new contexts.py mirrors config.py: [contexts] name = "blurb"; missing file = empty
  set; names validated as simple words, looked up on a despaced/lowercased key so
  "web hooks"/"web-hooks"/"webhooks" all resolve the same block.
- grammar: context|prepare <name> <instruction> -> Action("context", (name, dictation)).
  same-utterance dictation (everything after <name> is literal, incl. "send"); bare
  context <name> injects just the blurb. one-shot targeting composes:
  [target <name>] [context <ctx>] [filler] <dictation>.
- daemon assembles blurb + (Shift+Enter soft newline | flattened separator) + dictation
  via the existing send_literal/type path, tracks the uncommitted-input buffer, and
  WAITS. config-gated by behavior.context_multiline / context_separator. unknown context
  name announces and injects nothing.

system daemon-control namespace — lands the pass-through vs control split the router was
structured for. reserved leading "system" routes to _do_system (never injects to
claude): system status (mode/target/model/contexts) and system reload [config|contexts].

live reload — voice reload + CLI claudedo reload (SIGHUP) re-read config.toml +
contexts.toml without reinitializing the loaded whisper model. customs now lists loaded
contexts. install.sh installs the contexts.toml template copy-if-absent (else .new).

keys.NEWLINE (S-Enter) added for the soft-newline assembly. wake list unchanged.

Signed-off-by: disqualifier <dev@disqualifier.me>
2026-06-26 18:08:08 -04:00

64 lines
2.4 KiB
Python

"""the claude code prompt keymap — single source of truth.
these keystrokes were confirmed empirically against a live ``claude`` v2.1.191
session in a throwaway tmux session (capture-pane observation of benign prompts:
the folder-trust prompt and a bash-command permission prompt). do not guess or
assume — if claude code changes its prompt ui, re-confirm against a live session
and update here.
confirmed behaviour (claude code v2.1.191):
- numbered select menus (trust prompt, permission prompt): pressing the bare
digit selects AND confirms IMMEDIATELY. NO trailing enter. (sending an extra
enter would leak into the next prompt or the input box.)
- arrow keys (Up/Down) move the highlight WITHOUT acting; Enter then confirms —
available as a robust alternative, modelled as a sequence.
- the permission prompt is "1. Yes / 2. Yes, and don't ask again / 3. No".
- Escape backs out of / cancels a prompt ("Esc to cancel" footer).
- the main input box: literal text is inserted via ``send-keys -l`` without
submitting; a bare Enter submits.
each value is a list of tmux key tokens to send in order. a single-element list
is a single keypress. ``type`` literal text is handled separately by inject.py
via ``send-keys -l`` and is not part of this keymap.
"""
YES = ["1"]
NO = ["3"]
SELECT_1 = ["1"]
SELECT_2 = ["2"]
SELECT_3 = ["3"]
SELECT_4 = ["4"]
APPROVE = ["1"]
APPROVE_ALWAYS = ["2"]
DENY = ["3"]
SUBMIT = ["Enter"]
CANCEL = ["Escape"]
# NEWLINE is a soft newline inside the input box that does NOT submit — Shift+Enter,
# which tmux names ``S-Enter`` (requires the extended-keys / xterm extkeys tmux
# settings install.sh appends). used to separate a context blurb from the dictated
# instruction in multiline assembly; if it proves flaky the daemon flattens to one
# line with a separator instead (behavior.context_multiline = false).
NEWLINE = ["S-Enter"]
# BACKSPACE deletes one char left; SPACE inserts one literal space. both are emitted
# repeatedly for `backspace <n>` / `space <n>` and for `erase` (n = the daemon's
# tracked uncommitted-input count). BSpace is tmux's name for the backspace key.
BACKSPACE = ["BSpace"]
SPACE = [" "]
SELECT_BY_INDEX = {
1: SELECT_1,
2: SELECT_2,
3: SELECT_3,
4: SELECT_4,
}
SELECT_1_ARROW = ["Up", "Up", "Up", "Enter"]
SELECT_2_ARROW = ["Up", "Up", "Enter"]
SELECT_3_ARROW = ["Up", "Enter"]
SELECT_4_ARROW = ["Enter"]