cc-sessions
Guia de receitas específicas emacsclient --eval para gerenciar sessões claude-code-ide.el a partir do
"Mordomo" (ou de qualquer outra sessão com acesso ao mesmo servidor Emacs). Os princípios (por que assim)
estão em majordom.md; aqui — apenas as chamadas prontas.
CRÍTICO: o único comando seguro para iniciar é claude-code-ide-start
claude-code-ide-continue, claude-code-ide-resume e o antigo claude-code-ide (todos sem argumentos) —
NUNCA devem ser chamados diretamente headless. Cada um deles, quando já existe uma sessão ativa para o diretório, invoca
um bloqueante (read-string "Instance name (empty for auto): "), que trava para sempre sem um usuário
vivo no teclado (confirmado empiricamente em todos os três). A única exceção é o M-x interativo direto por um humano.
claude-code-ide-start (assinatura (&optional instance-name continue resume)) cobre todos os três
casos com uma única chamada que NUNCA pergunta o nome interativamente:
;; início normal de uma nova conversa
(let ((default-directory "/home/eugine/job/<projeto>/")
(claude-code-ide-cli-extra-flags "--remote-control <projeto>"))
(claude-code-ide-start "<nome-ou-nil>"))
;; em vez de claude-code-ide-continue
(claude-code-ide-start "<nome-ou-nil>" t nil)
;; em vez de claude-code-ide-resume (abre um picker interativo DENTRO do terminal do claude,
;; não pelo minibuffer do Emacs — a chamada em si não bloqueia o emacsclient)
(claude-code-ide-start "<nome-ou-nil>" nil t)
instance-name (1º argumento) nil ⇒ numeração automática (vedmid-2, vedmid-3...). Defina-o explicitamente apenas
quando o usuário nomeou o destino/nome da nova cópia — se não souber, pergunte ao usuário, não invente.
Antes de iniciar: verifique o estado atual
(mapcar (lambda (b) (list (buffer-name b) (and (get-buffer-process b) (process-status (get-buffer-process b)))))
(seq-filter (lambda (b) (string-match-p "claude-code\\[<projeto>" (buffer-name b))) (buffer-list)))
- Nenhuma sessão ativa →
(claude-code-ide-start nil t nil)(retoma a última conversa do projeto, comocontinue). - Há uma sessão ativa → NUNCA a pare "para reiniciar". Abra uma CÓPIA ADICIONAL com o mesmo
claude-code-ide-start(comresume=tse o objetivo for escolher uma conversa antiga específica, ou sem — nova conversa ao lado).
Após iniciar: confirme remote-control com busca real no buffer
Ausência de travamento AINDA não prova que --remote-control funcionou (episódio: esqueceu a flag —
a sessão subiu limpa, mas permaneceu não remota).
(with-current-buffer "*claude-code[<projeto>]*"
(save-excursion (goto-char (point-min))
(if (search-forward "remote-control" nil t) "FOUND" "NOT FOUND")))
Se NOT FOUND — nem sempre é erro: verifique o conteúdo completo do buffer (buffer-substring-no-properties (point-min) (point-max)), a linha pode estar fora da parte renderizada, ou (para sessões grandes) fora do
scrollback visível de eat — o próprio terminal mantém apenas o histórico recente, não a conversa toda.
Parar sessão: primeiro desabilite query-on-exit, depois pare
claude-code-ide-stop por si só é seguro e NÃO depende de a sessão ter tido remote-control.
Mas o prompt normal do Emacs Buffer "..." has a running process; kill it? aparece ASSINCRONAMENTE (no
process-sentinel, APÓS claude-code-ide-stop já ter retornado) — portanto, um let-wrapper ao redor
da própria chamada não salva. Desabilite a flag no processo ANTES, com uma chamada separada:
(let ((proc (get-buffer-process "*claude-code[<projeto>]*")))
(when proc (set-process-query-on-exit-flag proc nil)))
(with-current-buffer "*claude-code[<projeto>]*" (claude-code-ide-stop))
Se houver várias cópias para o mesmo diretório (vedmid, vedmid:2, vedmid:masazh...) — chame claude-code-ide-stop
no contexto DO PRÓPRIO buffer (with-current-buffer), não via default-directory: a mensagem
confirmará qual foi parada ("Stopping Claude Code vedmid:masazh...").
Se o prompt ainda assim aparecer e travar (ps aux | grep emacsclient mostrar um processo pendurado) — NUNCA
tente desbloquear sozinha (nenhum --eval repetido, nenhum sinal para o emacs) — peça ao
usuário para responder no Emacs ativo. Veja emacs.md para contexto completo desse perigo.
Navegação em pickers interativos (lista de retomada, solicitações de permissão)
A lista de sessões (resume) e os menus de confirmação de permissões são renderizados DENTRO do terminal (não pelo
minibuffer do Emacs) — controlados por sequências ANSI comuns via process-send-string, não
por emacsclient --eval com chamadas elisp:
(process-send-string (get-buffer-process "*claude-code[<projeto>]*") "\e[B") ; seta para baixo
(process-send-string (get-buffer-process "*claude-code[<projeto>]*") "\e[A") ; seta para cima
(process-send-string (get-buffer-process "*claude-code[<projeto>]*") "\e[6~") ; Page Down
(process-send-string (get-buffer-process "*claude-code[<projeto>]*") "\r") ; Enter (confirmar)
Após cada pressionamento enviado, releia o conteúdo do buffer (buffer-substring-no-properties) para
ver a nova posição do cursor (❯) e o título (N of M), antes de avançar ou confirmar —
não se mova às cegas vários passos sem verificar. Teclas numéricas ("3") para seleção direta de item de menu
de confirmação NÃO são confirmadas como funcionais — navegação com setas + Enter é mais confiável.
Alternância de permission-mode (Shift+Tab) — é outra coisa, não auto-accept
A linha de status do terminal (⏸ manual mode on, ⏵⏵ accept edits on, ⏸ plan mode on, ⏵⏵ auto mode on) é controlada exclusivamente pelo binário claude — o Emacs não sabe nada sobre isso. Ciclo (Shift+Tab,
quatro estados): manual → accept edits → plan → auto → manual.
(process-send-string (get-buffer-process "*claude-code[<projeto>]*") "\e[Z")
A alternância ÀS VEZES invoca uma solicitação intermediária de confirmação de ferramenta (Do you want to proceed?) — escolha o item "Yes, and switch to auto mode" via navegação com setas (não com número) +
Enter, se o objetivo for retornar ao auto.
claude-code-ide-toggle-auto-accept — função Emacs separada, não o mesmo estado
Não confundir com o ciclo permission-mode acima. Esta função alterna apenas uma flag elisp na estrutura
da sessão e afeta APENAS a ferramenta MCP openDiff (oferta de visualização ediff antes de salvar
arquivo): ON → ediff não é mostrado, o arquivo é salvo imediatamente (FILE_SAVED); OFF → ediff
normal. A linha de status do terminal NÃO muda com esta chamada.
(with-current-buffer "*claude-code[<projeto>]*" (claude-code-ide-toggle-auto-accept))
Mostrar conteúdo do buffer para o usuário "como se estivesse olhando"
(with-current-buffer "*claude-code[<projeto>]*" (buffer-substring-no-properties (point-min) (point-max)))
Para sessões grandes, corte ((max (point-min) (- (point-max) N)) (point-max)) — o conteúdo completo pode ser
de megabytes, embora o scrollback visível de eat seja sempre limitado (a parte realmente renderizada não cresce
indefinidamente).