Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ BATCH = $(EMACS) --batch -Q -L . \
LOCAL_LOAD_PATH = --eval "(setq load-path (cons (expand-file-name \".\") load-path))"

# Pi CLI version — single source of truth (workflows extract this automatically)
PI_VERSION ?= 0.65.0
PI_VERSION ?= 0.75.4
PI_PACKAGE ?= @earendil-works/pi-coding-agent
PI_BIN ?= .cache/pi/node_modules/.bin/pi
PI_BIN_DIR = $(abspath $(dir $(PI_BIN)))

Expand Down Expand Up @@ -150,9 +151,9 @@ setup-pi:
fi; \
fi
@if [ ! -x "$(PI_BIN)" ]; then \
echo "Installing pi@$(PI_VERSION) to .cache/pi/..."; \
echo "Installing $(PI_PACKAGE)@$(PI_VERSION) to .cache/pi/..."; \
rm -rf .cache/pi; \
npm install --prefix .cache/pi @mariozechner/pi-coding-agent@$(PI_VERSION) --silent; \
npm install --prefix .cache/pi --ignore-scripts $(PI_PACKAGE)@$(PI_VERSION) --silent; \
fi
@echo "Using pi: $(PI_BIN)"
@$(PI_BIN) --version || (echo "ERROR: pi not working"; exit 1)
Expand Down
4 changes: 2 additions & 2 deletions README.org
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ shortest path to a usable session:

#+begin_src bash
# 1. Install the pi CLI
npm install -g @mariozechner/pi-coding-agent
npm install -g @earendil-works/pi-coding-agent

# Or with mise
mise use -g npm:@mariozechner/pi-coding-agent
mise use -g npm:@earendil-works/pi-coding-agent

# 2. Authenticate the CLI
pi --login
Expand Down
10 changes: 8 additions & 2 deletions pi-coding-agent-core.el
Original file line number Diff line number Diff line change
Expand Up @@ -293,8 +293,8 @@ This is the single source of truth for session activity state.
Status transitions are driven by events from pi:
- `idle' -> `streaming' on agent_start
- `streaming' -> `idle' on agent_end
- `idle' -> `compacting' on auto_compaction_start
- `compacting' -> `idle' on auto_compaction_end")
- `idle' -> `compacting' on compaction_start/auto_compaction_start
- `compacting' -> `idle' on compaction_end/auto_compaction_end")

(defvar-local pi-coding-agent--state nil
"Current state of the pi session (buffer-local in chat buffer).
Expand Down Expand Up @@ -366,6 +366,12 @@ Sets status to `streaming' on agent_start, `idle' on agent_end."
(pi-coding-agent--handle-tool-update event))
("tool_execution_end"
(pi-coding-agent--handle-tool-end event))
((or "compaction_start" "auto_compaction_start")
(setq pi-coding-agent--status 'compacting)
(setq pi-coding-agent--state-timestamp (float-time)))
((or "compaction_end" "auto_compaction_end")
(setq pi-coding-agent--status 'idle)
(setq pi-coding-agent--state-timestamp (float-time)))
("auto_retry_start"
(plist-put pi-coding-agent--state :is-retrying t)
(plist-put pi-coding-agent--state :retry-attempt (plist-get event :attempt))
Expand Down
8 changes: 4 additions & 4 deletions pi-coding-agent-render.el
Original file line number Diff line number Diff line change
Expand Up @@ -1042,18 +1042,18 @@ Updates buffer-local state and renders display updates."
(pi-coding-agent--display-tool-update
(plist-get event :partialResult)
(pi-coding-agent--tool-block-get (plist-get event :toolCallId))))
("auto_compaction_start"
((or "compaction_start" "auto_compaction_start")
(setq pi-coding-agent--status 'compacting)
(pi-coding-agent--set-activity-phase "compact")
(let ((reason (plist-get event :reason)))
(message "Pi: %sAuto-compacting... (C-c C-k to cancel)"
(message "Pi: %sCompacting... (C-c C-k to cancel)"
(if (equal reason "overflow") "Context overflow, " ""))))
("auto_compaction_end"
((or "compaction_end" "auto_compaction_end")
(setq pi-coding-agent--status 'idle)
(pi-coding-agent--set-activity-phase "idle")
(if (pi-coding-agent--normalize-boolean (plist-get event :aborted))
(progn
(message "Pi: Auto-compaction cancelled")
(message "Pi: Compaction cancelled")
;; Clear queue on abort (user wanted to stop)
(pi-coding-agent--clear-followup-queue))
(when-let* ((result (plist-get event :result)))
Expand Down
2 changes: 1 addition & 1 deletion pi-coding-agent-ui.el
Original file line number Diff line number Diff line change
Expand Up @@ -1477,7 +1477,7 @@ Returns t if available, nil otherwise."
"Check all required dependencies.
Displays warnings for missing dependencies."
(unless (pi-coding-agent--check-pi)
(display-warning 'pi (format "%s not found in PATH. Install with: npm install -g @mariozechner/pi-coding-agent"
(display-warning 'pi (format "%s not found in PATH. Install with: npm install -g @earendil-works/pi-coding-agent"
(car pi-coding-agent-executable))
:error))
(pi-coding-agent--maybe-install-essential-grammars)
Expand Down
23 changes: 23 additions & 0 deletions test/pi-coding-agent-core-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,29 @@ Display is handled by the display handler, not by state updates."
:isError :false))
(should (null (gethash "call_123" tools)))))

(ert-deftest pi-coding-agent-test-event-compaction-start-sets-compacting ()
"compaction_start event sets pi-coding-agent--status to compacting."
(let ((pi-coding-agent--status 'idle)
(pi-coding-agent--state nil))
(pi-coding-agent--update-state-from-event '(:type "compaction_start" :reason "threshold"))
(should (eq pi-coding-agent--status 'compacting))))

(ert-deftest pi-coding-agent-test-event-compaction-end-sets-idle ()
"compaction_end event sets pi-coding-agent--status to idle."
(let ((pi-coding-agent--status 'compacting)
(pi-coding-agent--state nil))
(pi-coding-agent--update-state-from-event '(:type "compaction_end" :reason "threshold" :aborted :false))
(should (eq pi-coding-agent--status 'idle))))

(ert-deftest pi-coding-agent-test-event-auto-compaction-aliases-supported ()
"Legacy auto_compaction_* events remain supported."
(let ((pi-coding-agent--status 'idle)
(pi-coding-agent--state nil))
(pi-coding-agent--update-state-from-event '(:type "auto_compaction_start" :reason "threshold"))
(should (eq pi-coding-agent--status 'compacting))
(pi-coding-agent--update-state-from-event '(:type "auto_compaction_end" :reason "threshold" :aborted :false))
(should (eq pi-coding-agent--status 'idle))))

(ert-deftest pi-coding-agent-test-ensure-active-tools-from-nil ()
"pi-coding-agent--ensure-active-tools works when pi-coding-agent--state is nil."
(let ((pi-coding-agent--state nil))
Expand Down
16 changes: 9 additions & 7 deletions test/pi-coding-agent-input-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,8 @@
(kill-buffer chat-buf)
(kill-buffer input-buf))))

(ert-deftest pi-coding-agent-test-auto-compaction-success-sends-queued-messages ()
"auto_compaction_end with aborted=false processes followup queue.
(ert-deftest pi-coding-agent-test-compaction-success-sends-queued-messages ()
"compaction_end with aborted=false processes followup queue.
Uses :false (JSON false representation) to verify boolean normalization."
(with-temp-buffer
(pi-coding-agent-chat-mode)
Expand All @@ -191,26 +191,28 @@ Uses :false (JSON false representation) to verify boolean normalization."
(cl-letf (((symbol-function 'pi-coding-agent--send-prompt)
(lambda (text) (setq sent-text text))))
(pi-coding-agent--handle-display-event
'(:type "auto_compaction_end"
'(:type "compaction_end"
:reason "threshold"
:aborted :false
:result (:tokensBefore 1000 :summary "Summary" :timestamp 1234567890000))))
;; Queue should be empty after processing
(should (null pi-coding-agent--followup-queue))
;; The queued message should have been sent
(should (equal sent-text "queued message")))))

(ert-deftest pi-coding-agent-test-auto-compaction-end-aborted-clears-queue ()
"auto_compaction_end when aborted clears followup queue without sending."
(ert-deftest pi-coding-agent-test-compaction-end-aborted-clears-queue ()
"compaction_end when aborted clears followup queue without sending."
(with-temp-buffer
(pi-coding-agent-chat-mode)
(let ((sent-text nil))
(setq pi-coding-agent--status 'compacting)
(setq pi-coding-agent--followup-queue '("queued message"))
(cl-letf (((symbol-function 'pi-coding-agent--send-prompt)
(lambda (text) (setq sent-text text))))
;; Simulate auto_compaction_end event (aborted)
;; Simulate compaction_end event (aborted)
(pi-coding-agent--handle-display-event
'(:type "auto_compaction_end"
'(:type "compaction_end"
:reason "threshold"
:aborted t)))
;; Queue should be cleared (user cancelled)
(should (null pi-coding-agent--followup-queue))
Expand Down
35 changes: 22 additions & 13 deletions test/pi-coding-agent-render-test.el
Original file line number Diff line number Diff line change
Expand Up @@ -5292,12 +5292,12 @@ Commands with embedded newlines should not have any lines deleted."
(should (equal pi-coding-agent--activity-phase "thinking"))))

(ert-deftest pi-coding-agent-test-activity-phase-compact-on-compaction ()
"Activity phase becomes compact on auto_compaction_start."
"Activity phase becomes compact on compaction_start."
(with-temp-buffer
(pi-coding-agent-chat-mode)
(setq pi-coding-agent--activity-phase "idle")
(pi-coding-agent--handle-display-event
'(:type "auto_compaction_start" :reason "threshold"))
'(:type "compaction_start" :reason "threshold"))
(should (equal pi-coding-agent--activity-phase "compact"))))

(ert-deftest pi-coding-agent-test-activity-phase-idle-on-agent-end ()
Expand All @@ -5309,12 +5309,12 @@ Commands with embedded newlines should not have any lines deleted."
(should (equal pi-coding-agent--activity-phase "idle"))))

(ert-deftest pi-coding-agent-test-activity-phase-idle-on-compaction-end ()
"Activity phase becomes idle on auto_compaction_end."
"Activity phase becomes idle on compaction_end."
(with-temp-buffer
(pi-coding-agent-chat-mode)
(setq pi-coding-agent--activity-phase "compact")
(pi-coding-agent--handle-display-event
'(:type "auto_compaction_end" :aborted t :result nil))
'(:type "compaction_end" :aborted t :result nil))
(should (equal pi-coding-agent--activity-phase "idle"))))

(ert-deftest pi-coding-agent-test-display-compaction-result-shows-header-tokens-summary ()
Expand Down Expand Up @@ -5348,20 +5348,20 @@ Commands with embedded newlines should not have any lines deleted."
(should (string-match-p "\\*\\*Bold\\*\\*" (buffer-string)))
(should (string-match-p "`code`" (buffer-string)))))

(ert-deftest pi-coding-agent-test-display-handler-handles-auto-compaction-start ()
"Display handler processes auto_compaction_start events."
(ert-deftest pi-coding-agent-test-display-handler-handles-compaction-start ()
"Display handler processes compaction_start events."
(with-temp-buffer
(pi-coding-agent-chat-mode)
(pi-coding-agent--handle-display-event '(:type "auto_compaction_start" :reason "threshold"))
(pi-coding-agent--handle-display-event '(:type "compaction_start" :reason "threshold"))
;; Status should change to compacting
(should (eq pi-coding-agent--status 'compacting))))

(ert-deftest pi-coding-agent-test-display-handler-handles-auto-compaction-end ()
"Display handler processes auto_compaction_end with successful result."
(ert-deftest pi-coding-agent-test-display-handler-handles-compaction-end ()
"Display handler processes compaction_end with successful result."
(with-temp-buffer
(pi-coding-agent-chat-mode)
(pi-coding-agent--handle-display-event
'(:type "auto_compaction_end"
'(:type "compaction_end"
:aborted nil
:result (:summary "Context was compacted."
:tokensBefore 50000
Expand All @@ -5370,16 +5370,25 @@ Commands with embedded newlines should not have any lines deleted."
(should (string-match-p "Compaction" (buffer-string)))
(should (string-match-p "50,000" (buffer-string)))))

(ert-deftest pi-coding-agent-test-display-handler-handles-auto-compaction-aborted ()
"Display handler processes auto_compaction_end when aborted."
(ert-deftest pi-coding-agent-test-display-handler-handles-compaction-aborted ()
"Display handler processes compaction_end when aborted."
(with-temp-buffer
(pi-coding-agent-chat-mode)
(setq pi-coding-agent--status 'compacting)
(pi-coding-agent--handle-display-event
'(:type "auto_compaction_end" :aborted t :result nil))
'(:type "compaction_end" :aborted t :result nil))
;; Status should return to idle
(should (eq pi-coding-agent--status 'idle))))

(ert-deftest pi-coding-agent-test-display-handler-handles-auto-compaction-aliases ()
"Display handler keeps legacy auto_compaction_* event aliases working."
(with-temp-buffer
(pi-coding-agent-chat-mode)
(pi-coding-agent--handle-display-event '(:type "auto_compaction_start" :reason "threshold"))
(should (eq pi-coding-agent--status 'compacting))
(pi-coding-agent--handle-display-event '(:type "auto_compaction_end" :aborted t :result nil))
(should (eq pi-coding-agent--status 'idle))))

(ert-deftest pi-coding-agent-test-thinking-rendered-as-blockquote ()
"Thinking content renders as markdown blockquote."
(let ((pi-coding-agent-thinking-display 'visible))
Expand Down