killeros 3.0.5 → 3.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -4,6 +4,53 @@ All notable changes to KillerOS are documented here. Releases use New Features f
4
4
 
5
5
  ## [Unreleased]
6
6
 
7
+ ## [3.0.7] - 2026-10-15
8
+
9
+ ### Changed
10
+
11
+ - Raised the minimum supported Pi version to 1.2.0 and pinned development packages to that baseline. Removed the retired experimental server dependency and updated the packaged theme to Pi's published schema URL.
12
+ - Batched the passive Git conversion matrix into three isolated repositories instead of thirty. All 5,940 real-Git comparisons and thirty attribute/configuration cases remain covered, with explicit per-case comparison counts and fewer Git process launches.
13
+ - Removed unnecessary Pi runtime imports from process controls, footer end-to-end tests, failure-report checks, and bounded-text tests. Directory cleanup now lives in a lightweight shared helper with unchanged Windows lock retries.
14
+
15
+ ### Fixed
16
+
17
+ - Excluded Git-ignored private `docs/` artifacts from linting while retaining checks for repository code, so local documentation helpers cannot block release validation.
18
+ - Made footer terminal checks wait for a rendered footer before testing command results, preventing reload frames without a footer from satisfying absent-hint checks.
19
+ - Made the cleanup-disabled process-tree control wait for the descendant's complete late write. Shell exit alone does not prove that a slowly started descendant has finished.
20
+ - Removed the old personal-instruction copy from goal continuation messages. Pi 1.2.0 now applies the shared startup handler to custom runs, so the copy duplicated instructions on each goal turn and after startup cancellation.
21
+ - Scoped the unsupported Windows RPC shutdown case to Node 24.19.0, including Pi 1.2.0, and documented Node's 24.20.0 fix. Kept the pilots active on other Node versions.
22
+ - Restored latest-Pi compatibility installs after Pi 1.2.0 stopped publishing the experimental server package. CI now installs only the matched runtime packages, and test diagnostics no longer require the retired server.
23
+ - Isolated the real-Pi tool-exposure matrix from the parent checkout's Git state. CLI and SDK fixtures share a checked repository initializer, and the exposure matrix shuts down extensions before disposal so its watchers do not outlive the tests.
24
+
25
+ ### Added
26
+
27
+ - Added real-CLI Pi 1.2.0 startup checks for single-copy personal instructions, custom and user events, queued next-turn messages, startup cancellation, goal turn accounting, reload, and ordinary compaction recovery. CI repeats them five times without forced exit and now checks Windows at both the Node floor and current LTS.
28
+ - Added bounded failure reports for CLI, terminal, process-control, and Git branch-isolation tests. Nested assertion and cleanup errors remain visible in test output with forced exit, and failed CI jobs retain report-only artifacts for seven days.
29
+
30
+ ## [3.0.6] - 2026-10-12
31
+
32
+ ### Changed
33
+
34
+ - Rewrote the README with installation and onboarding steps, grouped usage and configuration guidance, development checks, and release and security expectations.
35
+ - Raised the minimum supported Pi version to 1.1.0, pinned development packages to 1.1.0, and moved all CI Pi targets to that baseline or newer. Cancellation and retry coverage now runs without old-version gates.
36
+ - Removed repository checks that required exact historical changelog wording while retaining current README, Pi compatibility, package, and release safeguards.
37
+
38
+ ### Added
39
+
40
+ - Added remaining token counts and Pi's configured context capacity to the TUI footer, with capacity-only and percentage-only fallbacks at narrow widths and explicit unknown states.
41
+ - Added real Pi TUI coverage for context budgets, model switching, automatic-compaction commands, settings failures, reload, and responsive footer output.
42
+ - Added real-Pi cancellation coverage through the complete KillerOS entry point, checking paused goal state, stopped receipts, and suppressed completion sounds.
43
+ - Added an installed-CLI goal workflow covering package loading, public command routing, model-issued completion, durable session restoration, and no unsolicited model work. Verified on Windows Node 22.19.0 with Pi 1.1.0; the exact Windows Node 24.19.0/Pi 1.1.0 combination is skipped after reproduced native RPC shutdown crashes.
44
+ - Added a native-terminal installed-package pilot for question selection after resize, cancellation without partial answers, and multiline editor text across fullscreen transcript navigation. Kept focused question and editor tests, added terminal startup and assertion-failure cleanup controls, and added five-run no-forced-exit CI checks for the installed pilots and changed cancellation lifecycle before the normal-concurrency full suite. Terminal failure logs retain selected versions, sanitized launch arguments, and bounded screen and raw-output diagnostics.
45
+
46
+ ### Fixed
47
+
48
+ - Fixed release validation for the README's rewritten npm and Git pin examples while retaining stale-version rejection. Made the executable-mode Git oracle refresh index metadata before comparing changes, preventing metadata-only CI failures without changing its assertions.
49
+ - Show the footer's manual `/compact` hint at the existing rounded 15% warning threshold only when KillerOS automatic compaction is known to be off. Commands and turn-end settings reads refresh the hint, including external changes and read failures, without changing automatic triggers or Pi's independent compaction behavior.
50
+ - Fixed unhandled RPC test-driver stdin errors when a child exits during a write. A real-process control now verifies write rejection, retained diagnostics, and orderly cleanup.
51
+ - Fixed the native-terminal test driver's exit race by draining queued screen parsing before rejecting final-output waits or disposing the parser. Kept the native capability probe alive until its input confirmation is observed before orderly shutdown. Successful cleanup waits for queued parsing and tracked native callbacks to quiesce before disposing the parser; cleanup errors are surfaced and the observer is disabled on every close path.
52
+ - Fixed RPC fixture cancellation checks before sending commands or returning cached records. Added subprocess controls that reject unauthorized writes and prevent PID-tree escalation after the parent exits but inherited pipes remain open.
53
+
7
54
  ## [3.0.5] - 2026-10-08
8
55
 
9
56
  ### Fixed
package/Killeros.ts CHANGED
@@ -20,6 +20,7 @@ import { registerCodexFastMode } from "./killeros/codex-fast.ts";
20
20
  import { registerPersonalInstructions } from "./killeros/personal-instructions.ts";
21
21
  import { registerQuestionTool } from "./killeros/question.ts";
22
22
  import { createGoalRuntime } from "./killeros/runtime.ts";
23
+ import { createKillerosSettingsStore } from "./killeros/settings.ts";
23
24
  import { registerShellUi } from "./killeros/shell-ui.ts";
24
25
  import { registerWorkedFor } from "./killeros/worked-for.ts";
25
26
 
@@ -42,12 +43,13 @@ export default function Killeros(pi: ExtensionAPI, options: KillerosOptions = {}
42
43
  registerAliases(pi);
43
44
  registerHandoff(pi, goalRuntime, options.handoffMaxTokens);
44
45
  registerSlashAutocomplete(pi, commandResolver);
45
- registerFooter(pi, goalRuntime);
46
+ const settingsStore = createKillerosSettingsStore();
47
+ const updateFooterPreference = registerFooter(pi, goalRuntime, settingsStore);
46
48
  registerCodexFastMode(pi);
47
49
  registerLifecycleHooks(pi);
48
50
  registerWorkedFor(pi);
49
51
  const goalCompaction = registerGoalSettlement(pi, goalRuntime);
50
- registerAutoCompaction(pi, { goal: goalCompaction });
52
+ registerAutoCompaction(pi, { goal: goalCompaction, settingsStore, onPreferenceChange: updateFooterPreference });
51
53
  registerRequestActivity(pi);
52
54
  registerCompletionNotifications(pi, options.completionNotifications);
53
55
  }
package/README.md CHANGED
@@ -1,207 +1,363 @@
1
1
  # KillerOS
2
2
 
3
- A TypeScript extension for the [Pi coding agent](https://github.com/earendil-works/pi) that replaces the stock TUI and adds long-running goals, reasoning controls, and workflow commands.
3
+ ## A custom terminal UI and goal runner for the Pi coding agent
4
4
 
5
- ![KillerOS TUI showing the startup masthead, an active goal, a settled task receipt, and the goal footer](.github/assets/killeros-preview.png)
5
+ KillerOS is a TypeScript extension for the [Pi coding agent](https://github.com/earendil-works/pi). It replaces Pi's stock terminal UI and lets the agent work toward an objective across turns and context compaction. You can pause or resume a goal, carry context into a fresh session, and answer structured questions without leaving the terminal.
6
6
 
7
- ## What you get
7
+ The UI has a dark theme with coral accents, a multiline editor with slash-command completion, and a footer for model, context, and goal state. The startup masthead shows versions, model, working directory, and Git branch. Settled task receipts show duration and token usage. Completion sounds are optional.
8
8
 
9
- - A custom TUI: startup masthead with versions, model, working directory, and Git branch; a dark theme with coral accents; a multiline editor with slash-command completion; a footer that tracks model, context, and goal state; settled task receipts with duration and token usage.
10
- - `/goal`: set an objective and Pi keeps working toward it across turns, compaction, reloads, and branch navigation. Each turn must record `continue` with evidence and one next action, `complete`, or the existing blocker decision; otherwise the goal pauses. New goals pause after 20 turns; `/goal resume` grants another 20.
11
- - `/codex-fast`: toggles the `priority` service tier on legacy Codex and eligible native OpenAI Responses requests or reports its status.
12
- - `/handoff`: starts a fresh linked session carrying visible continuation context.
13
- - `/auto-compact`: reports, enables, disables, or tunes automatic context compaction.
14
- - A `question` tool with single-select and multi-select modes.
15
- - Lifecycle hooks (`tool_call`, `tool_result`, `agent_settled`) from `.pi/killeros-hooks.json`, plus `AGENTS.local.md` loading for trusted projects.
16
- - Optional completion sounds for settled requests.
9
+ ![KillerOS terminal UI with the startup masthead, an active goal, a settled task receipt, and the goal footer](.github/assets/killeros-preview.png)
17
10
 
11
+ - [Install and start](#install-and-start)
12
+ - [Use KillerOS](#use-killeros)
13
+ - [Configuration and Pi compatibility](#configuration-and-pi-compatibility)
14
+ - [Set up for development](#set-up-for-development)
15
+ - [Contribution and release expectations](#contribution-and-release-expectations)
16
+ - [Known issues and limits](#known-issues-and-limits)
17
+ - [Security](#security)
18
18
 
19
- ## Requirements
19
+ ## Install and start
20
+
21
+ ### Requirements
20
22
 
21
23
  - Node.js 22.19.0+
22
- - Pi 1.0.4 or later below 2.0.0
23
- - An interactive TUI session for the custom header, editor, footer, and `question`
24
+ - Pi 1.2.0 or later below 2.0.0
25
+ - An interactive TUI session for the custom header, editor, footer, and questions
24
26
 
25
- Users on older Pi versions must upgrade.
27
+ KillerOS supports Windows and Linux. Users on older Pi versions must upgrade.
26
28
 
27
- ## Install
29
+ Pi extensions run with your user permissions. Review the source before installing globally. See [Security](#security) before trusting project hooks. Goal work sends model requests and can incur provider charges.
28
30
 
29
- ```bash
30
- pi install npm:killeros
31
- ```
31
+ 1. [Install Pi](https://github.com/earendil-works/pi#getting-started) if you do not already have it.
32
+ 2. Install KillerOS:
33
+
34
+ ```bash
35
+ pi install npm:killeros
36
+ ```
37
+
38
+ 3. Restart Pi in the project you want to work on:
39
+
40
+ ```bash
41
+ pi
42
+ ```
32
43
 
33
- Or from GitHub:
44
+ 4. If you have not configured a provider, run `/login` in Pi. Then enter an objective:
45
+
46
+ ```text
47
+ /goal Create `./summary.md` describing this repository's entry points, verified against the source files
48
+ ```
49
+
50
+ The goal appears in the footer. The agent works until it records completion, reports a blocker, or pauses. On completion, `summary.md` contains the repository summary. A new goal has a 20-turn limit. Use `/goal` to inspect it and `/goal resume` to grant another 20 turns when the limit is exhausted.
51
+
52
+ You can also install from GitHub:
34
53
 
35
54
  ```bash
36
55
  pi install git:github.com/KyrosHendrix/pi-KillerOS
37
56
  ```
38
57
 
39
- Pin a release by appending its tag, for example `@v3.0.5`. Add `-l` to install only for the current project. Restart Pi after installing.
58
+ To pin a release, use `npm:killeros@3.0.7` or append a Git tag such as `@v3.0.7` to the Git source. Add `-l` to install only for the current project. Pi loads project packages only after you grant project trust.
40
59
 
41
- ## Commands
60
+ ### Commands
42
61
 
43
62
  ```text
44
63
  /goal View the current goal
45
- /goal <objective> Set an objective
64
+ /goal <objective> Set an objective
46
65
  /goal pause Stop automatic continuation
47
66
  /goal resume Resume automatic continuation
48
67
  /goal clear Remove the current goal
49
- /codex-fast [status] Toggle or report Codex fast mode
68
+ /codex-fast [status] Toggle or report fast mode
50
69
  /auto-compact [status|on|off|<percent>]
51
70
  Inspect or change automatic compaction
52
71
  /notification Configure the completion sound
53
- /handoff [focus] Fresh session with continuation context
54
- /clear New session after confirmation
72
+ /handoff [focus] Start a linked session with continuation context
73
+ /clear Start a new session after confirmation
55
74
  /exit Quit Pi gracefully
56
75
  ```
57
76
 
58
- Fast mode is off by default. `/codex-fast` toggles it, and `/codex-fast status` reports the preference without changing it. The preference stays in the current Pi process across model switches and extension reloads, but is not saved between processes. The command name and notification strings retain their legacy Codex wording.
77
+ ## Use KillerOS
78
+
79
+ ### Goals and proof
80
+
81
+ Include the result you want and how the agent can verify it:
82
+
83
+ ```text
84
+ /goal Reduce p95 checkout latency below 120 ms, verified by the checkout benchmark, while keeping the correctness suite green
85
+ ```
86
+
87
+ KillerOS cannot independently prove arbitrary objectives from prose. The assistant must check the whole objective before reporting completion.
59
88
 
60
- Fast mode applies to legacy `openai-codex` requests and selected native models with provider `openai`, API `openai-responses`, and base URL `https://api.openai.com/v1`, with an optional trailing slash. Native request payloads must also name the selected model's exact ID. Both API-key and ChatGPT subscription authentication use this rule. Custom endpoints, Azure, other APIs, and ambiguous native requests pass through unchanged. When enabled, fast mode overrides an eligible request's existing service tier with `priority`; when disabled, it leaves the request unchanged.
89
+ A directly quoted, path-shaped file target adds silent file proof. KillerOS captures its baseline at goal start and accepts completion only after the file is created or changed. For an extensionless relative file, use an explicit path such as `./summary` or `.\summary`. Bare quoted prose uses model-reported completion. File proof verifies the deliverable, not every acceptance criterion in the objective.
61
90
 
62
- The TUI footer's `fast` label means the enabled preference applies to the selected model, not that OpenAI accepted priority service. OpenAI controls account and model eligibility, the actual service tier, and charges. Priority can affect billing; subscription login does not guarantee accepted or free priority service. KillerOS leaves provider errors to Pi and does not silently retry with a different tier.
91
+ Each goal turn must record a decision through `killeros_goal_update`:
63
92
 
64
- ## Behavior by mode
93
+ - `continue`, with evidence and one next action.
94
+ - `complete`, after verification.
95
+ - A blocker decision that stops automatic work.
65
96
 
66
- Pi 1.0 defaults to fullscreen. Use `--tui-mode regular` for one invocation or set `"tuiMode": "regular"` in Pi's [settings](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/settings.md#terminal-and-display) to restore terminal-owned scrollback. KillerOS leaves Pi's `tuiMode` and `quietStartup` preferences unchanged.
97
+ An ordinary response does not authorize another turn. Missing decisions, repeated continuation reports, and an unavailable goal tool pause the goal. New goals pause after 20 turns without warning. An explicit `/goal resume` on an exhausted goal grants another 20. Restored goals retain their saved limit.
67
98
 
68
- | Mode | What works |
69
- | --- | --- |
70
- | TUI | Everything |
71
- | RPC | Goals, proactive compaction; no TUI components, sounds, title indicator |
72
- | Print/JSON | No interactive questions, `/goal`, or proactive compaction |
99
+ Goals survive compaction, reloads, and session-tree navigation. Compaction recovery never grants turns. Recovery within the same turn requires an actual interruption. Successful or skipped compaction cannot restart a stopped goal without an accepted decision. A request that completes or blocks a goal stays goal-owned through settlement, so automatic compaction cannot restart it as ordinary work.
73
100
 
74
- With Pi 1.0.3's default [keybindings](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/keybindings.md#fullscreen), `Home` and `End` move within the current editor line. In fullscreen mode, `Ctrl+Home` and `Ctrl+End` scroll to the transcript's beginning or end without moving the editor cursor. KillerOS inherits these bindings; user overrides still apply.
101
+ A blocked goal stays stopped. During a later ordinary TUI or RPC request in a saved session, the assistant can complete the same unchanged objective with current evidence. This path accepts only `complete`. It preserves the original file baseline and records `file` proof or `model-reported` completion. It does not resume automatic work, grant turns, restart the active clock, or require `/goal clear`. Failed proof or a response without a completion decision leaves the goal blocked. Paused goals still require explicit user controls.
75
102
 
76
- `question` and the active `killeros_goal_update` tool use Pi's `model-only` exposure. They stay directly declared to the model when codemode is disabled or enabled in `on` or `only` mode, but scripts and other tools cannot call them through `ctx.executeTool()`. The goal tool is active for active goals and, during a later ordinary request, for completion only of a saved blocked goal. It is inactive outside those cases. Questions still require TUI mode; a direct RPC call fails with `The question tool requires interactive TUI mode`. KillerOS does not enable or configure codemode.
103
+ Completed goals leave the footer but remain available through `/goal` and session history.
77
104
 
78
- Pi 1.0.4's native `--tools` and `--exclude-tools` support `*` patterns. Quote patterns such as `"killeros_*"` so the shell does not expand them. An explicit allowlist must include `question` for interactive questions and permit `killeros_goal_update` for goals. Selection does not make questions usable outside TUI mode. Excluding the goal tool pauses goals that are active before any model request. A later ordinary request with a blocked goal still runs, but cannot record its completion when the tool is excluded. See Pi's [tool selection](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/cli.md#tools).
105
+ Pending goal commands do not replace newer state. If committed navigation changes the branch or another mutation changes the goal during confirmation, idle waiting, or file-baseline reading, KillerOS discards the pending mutation. Cancelled navigation preserves valid pending commands. Discarding a stale replacement preserves the surviving goal's authorized continuation.
106
+
107
+ ### Context and automatic compaction
108
+
109
+ Compaction triggers by default at 15% tokens remaining. Use `/auto-compact status` to inspect the effective KillerOS preference, `on` or `off` to toggle it, or an integer from 0 through 100 to set the threshold:
110
+
111
+ ```text
112
+ /auto-compact 20
113
+ /auto-compact off
114
+ ```
79
115
 
80
- Pi 1.1.0 also accepts a list containing only exact `+name` and `-name` entries to change its default selection. For example, `pi --tools +codemode` adds codemode while retaining the default tools, and `pi --tools +codemode,-write` also removes `write` from that selection. Plain allowlists still replace the selection. Pi rejects mixed lists such as `read,+codemode` and modifier patterns such as `-killeros_*`. Use `--exclude-tools "killeros_*"` for wildcard exclusions.
116
+ Turning off KillerOS automatic compaction does not turn off Pi's own automatic compaction. A threshold of 0 does not disable Pi's token reserve. Later ordinary requests can still use compaction continuation.
81
117
 
82
- In Pi 1.1.0, removing an extension tool with a `-name` modifier does not prevent Pi from activating it on registration or reload. To exclude the goal tool reliably, use `pi --exclude-tools killeros_goal_update`. KillerOS delegates selection to Pi; it does not parse or repair native CLI filters.
118
+ The footer shows remaining context as `ctx 85% (849K/1M)`. The percentage and numerator both mean remaining. The denominator is Pi's configured context capacity, not an independently verified provider limit. Counts use decimal K and M units and round down.
83
119
 
84
- Pi 1.1.0 reports program status through OSC 7501 when the terminal supports it. KillerOS questions use Pi's native selectors and text inputs. Each open question step reports `blocked` and restores the previous status after an answer, cancellation, or abort. Existing native confirmation dialogs do the same. `PI_PROGRAM_STATUS=1` forces native reporting and `PI_PROGRAM_STATUS=0` disables it. KillerOS preserves this preference and lets Pi handle all status reporting. See Pi's [program status](https://github.com/earendil-works/pi/blob/v1.1.0/packages/coding-agent/docs/terminal-setup.md#program-status).
120
+ On narrow terminals, the footer drops the remaining count, then capacity, before dropping the model name and percentage. Unknown usage shows only capacity. Unknown capacity shows `ctx —%` without an invented limit. Usage estimates can be unavailable after compaction.
121
+
122
+ At the rounded 15% remaining warning threshold, the footer suggests `/compact` only when KillerOS automatic compaction is known to be off. Successful `/auto-compact` changes refresh the hint immediately. Turn-end settings reads also refresh it, including changes made by another process. The warning stays at 15% regardless of the automatic threshold. Settings-read errors omit the hint.
123
+
124
+ ### Fast mode and billing
125
+
126
+ Priority service can affect billing. OpenAI controls account and model eligibility, the accepted service tier, and charges. A ChatGPT subscription does not guarantee accepted or free priority service.
127
+
128
+ Fast mode is off by default. `/codex-fast` toggles it. `/codex-fast status` reports the preference without changing it. The preference lasts for the current Pi process, including model switches and extension reloads. KillerOS does not save it between processes. The command and notifications retain their legacy Codex wording.
129
+
130
+ Fast mode applies to legacy `openai-codex` requests and native requests that meet all these conditions:
131
+
132
+ - The selected model uses provider `openai` and API `openai-responses`.
133
+ - Its base URL is `https://api.openai.com/v1`, with an optional trailing slash.
134
+ - The request payload names the selected model's exact ID.
135
+
136
+ API-key and ChatGPT subscription authentication follow the same rule. Custom endpoints, Azure, other APIs, and ambiguous native requests pass through unchanged.
137
+
138
+ When enabled, fast mode overrides an eligible request's service tier with `priority` and preserves sampling and other payload fields. When disabled, it leaves the request unchanged. The footer's `fast` label means the preference applies to the selected model, not that OpenAI accepted priority service. KillerOS leaves provider errors and retries to Pi. It does not silently retry with a different tier.
85
139
 
86
140
  ### Questions
87
141
 
88
- The `question` tool asks one question per invocation. Single-select is the default; choosing a numbered option submits that answer. In multi-select mode, choosing an option toggles its selection and opens the selector again. Choose **Submit answers** when finished. KillerOS checks the requested minimum and maximum and returns the selected predefined answers in their original order, followed by an optional custom answer. Dialog steps stay within the same tool execution and do not start model requests or grant goal turns.
142
+ The `question` tool supports single-select and multi-select answers. It asks one question per invocation and requires TUI mode.
143
+
144
+ In single-select mode, choosing a numbered option submits the answer. In multi-select mode, choosing an option toggles its selection and reopens the selector. Choose **Submit answers** when finished. KillerOS checks the requested minimum and maximum. It returns predefined answers in their original order, followed by an optional custom answer.
145
+
146
+ Choose **Custom answer** to open Pi's text input. Multi-select allows one custom answer, which you can edit or remove. Blank answers do not submit. Custom answers accept up to 4,000 Unicode characters. Cancelling custom input returns to the selector without losing selections.
147
+
148
+ **More actions** lets you filter options, clear a filter, review the full question and option details, or select a recent custom answer. Filters match labels and descriptions without losing hidden selections. Filter queries accept up to 4,000 Unicode characters and 16,000 UTF-8 bytes.
89
149
 
90
- Choose **Custom answer** to open Pi's text input. Multi-select allows one custom answer, which you can edit or remove. Blank answers do not submit; custom answers accept up to 4,000 Unicode characters. Cancelling custom input returns to the selector with existing selections intact. **More actions** includes filtering, clearing a filter, reviewing the full question and option details, and selecting a recent custom answer. Filtering matches labels and descriptions without losing hidden selections. Filter queries accept up to 4,000 Unicode characters and 16,000 UTF-8 bytes.
150
+ Option summaries have bounded descriptions. Review displays full descriptions and proposal previews as paged plain text, including Markdown source. Use **Next page**, **Previous page**, and **Back** to navigate. Long option lists and recent-answer history also use pages. History holds at most 100 distinct answers and 64 KiB of text in the current session. Session changes clear it.
91
151
 
92
- Descriptions appear in bounded option summaries. Review shows full descriptions and proposal previews as paged plain text, including any markdown source. **Next page**, **Previous page**, and **Back** navigate the review. Long option lists and recent-answer history also use pages. Recent answers stay within the current session, with at most 100 distinct entries and 64 KiB of text; session changes clear them.
152
+ Questions use Pi's native dialog keybindings and text editing. Multi-select uses successive selection steps, not the former space-toggle and slash-filter shortcuts. Resizing preserves selections and text-input drafts.
93
153
 
94
- The flow uses Pi's native dialog keybindings and editing behavior. Multi-select takes successive selection steps; the former space-toggle and slash-filter shortcuts are replaced by explicit actions. Resizing selectors rebuilds their pages while preserving selections, and Pi resizes text input without discarding its draft. Cancelling the main selector discards unsubmitted answers. Cancelling a filter, history, or review step returns to the selector. Agent cancellation or session invalidation closes the question and prevents another step from opening.
154
+ Cancelling the main selector discards unsubmitted answers. Cancelling a filter, history, or review step returns to the selector. Agent cancellation or session invalidation closes the question and prevents another step. Dialog steps stay within one tool execution. They do not send model requests or grant goal turns.
95
155
 
96
- ## Configuration
156
+ ### Handoff
97
157
 
98
- The packaged `killeros` theme activates on TUI start. Compaction triggers by default at 15% tokens remaining, stored in global `killeros.json`:
158
+ `/handoff [focus]` creates a fresh linked session with visible continuation context. It saves that context as a user message before reporting success, so you can exit and resume immediately without another prompt. Creating the handoff does not start an agent turn.
159
+
160
+ The summary output limit is 8192 tokens by default. Raise `handoffMaxTokens` if long sessions truncate it. Handoff validation rejects recognized credentials before creating the destination session. See [Security](#security) for the limits of that check.
161
+
162
+ ### Receipts, sounds, and Git status
163
+
164
+ Receipts and completion sounds wait for final `agent_settled`, including across provider retries. Sounds are off by default. Configure them with `/notification` in TUI mode. The tab-title indicator requires a Nerd Font.
165
+
166
+ Final request cancellation records `Stopped` and suppresses the sound even if the last assistant response ended normally. The receipt retains recorded changes and checks. Cancellation stops goal continuation and provider retry work. Cancelling pending compaction prevents its recovery.
167
+
168
+ Session replacement, reload, and committed tree navigation discard unfinished receipts. Late results cannot write or notify through the old session context or attach to another branch. Cancelled navigation preserves pending receipts.
169
+
170
+ Failed Git reads retain the footer's last successful file counts and mark task changes unavailable. KillerOS does not treat a failed scan as an empty repository. Passive scans compare content without executing repository filters or repository-owned Git shims.
171
+
172
+ ## Configuration and Pi compatibility
173
+
174
+ ### KillerOS settings
175
+
176
+ KillerOS activates its packaged `killeros` theme on TUI start. It stores settings in `killeros.json` in Pi's global agent directory, normally `~/.pi/agent/killeros.json`:
99
177
 
100
178
  ```json
101
179
  {
102
- "autoCompaction": {
103
- "enabled": true,
104
- "percentRemaining": 15
105
- },
106
- "handoffMaxTokens": 8192
180
+ "autoCompaction": {
181
+ "enabled": true,
182
+ "percentRemaining": 15
183
+ },
184
+ "handoffMaxTokens": 8192
107
185
  }
108
186
  ```
109
187
 
110
- Use `/auto-compact status` to inspect the effective KillerOS preference, `/auto-compact on` or `/auto-compact off` to toggle it, and `/auto-compact <percent>` to set an integer threshold from 0 through 100. A threshold of 0 does not disable Pi's own token reserve.
188
+ Concurrent `/auto-compact` commands preserve independent changes to the enabled flag and threshold, along with unrelated settings. Malformed settings and failed writes report errors without claiming success or replacing the original file.
111
189
 
112
- Concurrent `/auto-compact` commands preserve independent changes to the enabled flag and threshold, along with unrelated settings. Malformed settings and failed writes report an error without claiming success or replacing the original file.
190
+ Trusted projects can use lifecycle hooks for `tool_call`, `tool_result`, and `agent_settled` in `.pi/killeros-hooks.json`. KillerOS also loads `AGENTS.local.md` for trusted projects. Review these files before granting trust.
113
191
 
114
- `handoffMaxTokens` caps the `/handoff` summary output at 8192 tokens by default; raise it when long sessions truncate the summary. The handoff is saved as a visible user-context message before success is reported, so the linked session survives immediate exit and resume without sending another prompt. Creating the handoff does not start an agent turn.
192
+ Pi 1.2.0 runs `before_agent_start` for user prompts and extension-triggered requests. KillerOS applies trusted personal instructions through that shared setup, including goal and compaction continuations, without copying them into each continuation message.
115
193
 
116
- State proof in the objective so the agent can verify it with its normal tools:
194
+ ### Supported modes and navigation
117
195
 
118
- ```text
119
- /goal Reduce p95 checkout latency below 120 ms, verified by the checkout benchmark, while keeping the correctness suite green
120
- ```
196
+ Pi defaults to fullscreen. Use `--tui-mode regular` for one invocation or set `"tuiMode": "regular"` in [Pi settings](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/settings.md#terminal-and-display) to restore terminal-owned scrollback. KillerOS leaves Pi's `tuiMode` and `quietStartup` preferences unchanged.
197
+
198
+ | Mode | Support |
199
+ | --- | --- |
200
+ | TUI | Custom UI, questions, goals, proactive compaction, sounds, and title indicator |
201
+ | RPC | Goals and proactive compaction, without TUI components, sounds, or title indicator |
202
+ | Print or JSON | No interactive questions, `/goal`, or proactive compaction |
121
203
 
122
- A direct quoted path-shaped file target binds silent file proof. For an extensionless relative file, use an explicit path such as `./summary` or `.\summary`:
204
+ With Pi's default [keybindings](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/keybindings.md#fullscreen), `Home` and `End` move within the current editor line. In fullscreen mode, `Ctrl+Home` and `Ctrl+End` scroll to the transcript's beginning or end without moving the editor cursor. KillerOS inherits these bindings and preserves user overrides.
123
205
 
124
- ```text
125
- /goal Fix `killeros/footer.ts`, verified by npm test
126
- ```
206
+ Pi reports program status through OSC 7501 in supported terminals. Each question step reports `blocked` and restores the previous status after an answer, cancellation, or abort. Native confirmation dialogs do the same. `PI_PROGRAM_STATUS=1` forces reporting and `PI_PROGRAM_STATUS=0` disables it. KillerOS preserves the preference and uses Pi's native reporting. See [program status](https://github.com/earendil-works/pi/blob/v1.2.0/packages/coding-agent/docs/terminal-setup.md#program-status).
207
+
208
+ ### Tool selection and codemode
209
+
210
+ `question` and the active `killeros_goal_update` tool are sequential and use Pi's `model-only` exposure. Pi declares them directly to the model with codemode disabled or enabled in `on` or `only` mode. Scripts and other tools cannot call them through `ctx.executeTool()`.
211
+
212
+ The goal tool is active for active goals. During a later ordinary request, it can also be active for completion only of the same saved blocked goal. It is inactive outside those cases. A direct RPC question call fails with `The question tool requires interactive TUI mode`. KillerOS does not enable or configure codemode.
213
+
214
+ Pi's native `--tools` and `--exclude-tools` support `*` patterns. Quote patterns such as `"killeros_*"` so the shell does not expand them. An explicit allowlist must include `question` for interactive questions and permit `killeros_goal_update` for goals. Tool selection does not make questions usable outside TUI mode.
215
+
216
+ Excluding the goal tool pauses goals that are active before any model request. A later ordinary request with a blocked goal still runs, but cannot record completion if the tool is excluded. See [Pi tool selection](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/cli.md#tools).
127
217
 
128
- Bare quoted prose remains model-reported. KillerOS captures the file baseline at goal start and only completes when the file is created or changed. File proof verifies that deliverable, not every natural-language acceptance criterion. The assistant must audit the whole objective before reporting completion; KillerOS cannot independently prove arbitrary objectives from prose. A normal response never continues a goal by itself: the agent must record `continue`, `complete`, or a blocker decision through `killeros_goal_update`. Repeated continuation reports and unavailable goal tools pause the goal. New goals pause after 20 turns without warning. An explicit `/goal resume` on an exhausted goal grants another 20 turns; compaction recovery never grants turns. Same-turn recovery requires an actual interruption; successful or skipped compaction cannot restart a normally stopped goal without a decision. A request that completes or blocks a goal remains goal-owned through settlement, so automatic compaction cannot restart it as ordinary work. Later ordinary requests still support compaction continuation. Restored goals keep their persisted limit.
218
+ Pi accepts lists containing only exact `+name` and `-name` entries to change the default selection:
129
219
 
130
- A blocked goal stays stopped. In a later ordinary TUI or RPC request in a saved session, the assistant can complete the same unchanged objective through `killeros_goal_update` after verification and current evidence. Only `complete` is accepted on this path. Completion does not resume automatic work, grant turns, restart the active clock, or require `/goal clear`. It retains the original file baseline and records either `file` proof or `model-reported` completion. Failed proof and ordinary responses without a completion decision leave the goal blocked. Paused goals still require explicit user controls. A completed goal leaves the footer but remains inspectable through `/goal` and session history.
220
+ ```bash
221
+ pi --tools +codemode
222
+ pi --tools +codemode,-write
223
+ ```
131
224
 
132
- Session replacement, reload, and committed tree navigation discard unfinished task receipts. Late receipt results do not write or notify through the old session context or attach to a different branch. Cancelled navigation preserves the pending receipt.
225
+ Plain allowlists replace the selection. Pi rejects mixed lists such as `read,+codemode` and modifier patterns such as `-killeros_*`. Use `--exclude-tools "killeros_*"` for wildcard exclusions.
133
226
 
134
- Pending `/goal` commands discard their mutation if committed navigation changes the branch or another mutation changes the goal while confirmation, idle waiting, or file-baseline reading is in progress. Cancelled navigation preserves valid pending commands. Discarding a stale replacement after its idle wait preserves the surviving goal's authorized continuation.
227
+ A `-name` modifier does not reliably prevent an extension tool from activating on registration or reload. To exclude the goal tool, use:
135
228
 
136
- Failed Git metadata reads keep the footer's last successful file counts and mark task changes unavailable. They are not treated as an empty repository.
229
+ ```bash
230
+ pi --exclude-tools killeros_goal_update
231
+ ```
137
232
 
138
- Passive Git scans compare content without running repository filters. CRLF text that normalizes to the indexed LF content stays unchanged, including with `text=auto eol=lf` or bare `eol=lf`. Git's `status` can still report a modification after a size-changing rewrite because of its index stat cache, even when `git diff` is empty.
233
+ KillerOS delegates selection to Pi. It does not parse or repair native CLI filters.
139
234
 
140
- Completion sounds are off by default; change with `/notification` in TUI mode. The tab-title indicator requires a Nerd Font.
235
+ In Pi codemode, `tools.read()` on an image returns an image block that you can pass to native `image()`. See [codemode tool calls](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/codemode.md#call-tools).
141
236
 
142
- On Pi 1.1.0, final request cancellation records `Stopped` and suppresses the completion sound even when the last assistant response ended normally. Recorded changes and checks remain in the receipt. Cancellation stops goal continuation and provider retry work; cancelling a pending compaction prevents its recovery. Pi 1.0.4 retains the assistant stop-reason fallback. Receipts and sounds still wait for final `agent_settled`, including across provider retries.
237
+ Pi's `image()` also saves images to temporary files and returns their paths for later turns to copy or move. On POSIX systems, Pi creates these files with user-only permissions. Windows access follows inherited filesystem ACLs. See [image generation](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/codemode.md#generate-images).
143
238
 
144
- ### Pi model and MCP settings
239
+ ### Reasoning, sampling, and Azure
145
240
 
146
- Use Pi's native `/thinking` selector for reasoning levels. Pi 1.0.2 can also apply different sampling parameters for each level through `samplingParamsByThinkingLevel` in `models.json`. For example, a model entry can contain:
241
+ Use Pi's native `/thinking` selector for reasoning levels. Pi supports per-level sampling through `samplingParamsByThinkingLevel` in `models.json`. A model entry can contain:
147
242
 
148
243
  ```json
149
244
  {
150
- "samplingParams": { "temperature": 1.0, "top_p": 0.95 },
151
- "samplingParamsByThinkingLevel": {
152
- "off": { "temperature": 0.7, "top_p": 0.8 },
153
- "high": { "temperature": 0.6 }
154
- }
245
+ "samplingParams": { "temperature": 1.0, "top_p": 0.95 },
246
+ "samplingParamsByThinkingLevel": {
247
+ "off": { "temperature": 0.7, "top_p": 0.8 },
248
+ "high": { "temperature": 0.6 }
249
+ }
155
250
  }
156
251
  ```
157
252
 
158
- Only configure parameters the endpoint accepts. These settings apply to `openai-completions`, `openai-responses`, and `azure-openai-responses`, not legacy `openai-codex`. Missing levels inherit model defaults; request-level sampling parameters take precedence. `/codex-fast` preserves sampling parameters when it adds the priority service tier. See Pi's [sampling configuration](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/models.md#configure-sampling-by-thinking-level) for the full file format and merge rules.
253
+ Configure only parameters your endpoint accepts. These settings apply to `openai-completions`, `openai-responses`, and `azure-openai-responses`, not legacy `openai-codex`. Missing levels inherit model defaults. Request-level sampling parameters take precedence. `/codex-fast` preserves sampling when it adds the priority tier. See [sampling configuration](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/models.md#configure-sampling-by-thinking-level) for the full format and merge rules.
254
+
255
+ Pi's Azure provider is `azure`. It supports Azure Foundry Chat Completions, including `azure/deepseek-v4-pro`. If you still use the old `azure-openai-responses` provider key:
159
256
 
160
- Pi 1.0.3 renames the Azure **provider** from `azure-openai-responses` to `azure` and adds Azure Foundry Chat Completions, including `azure/deepseek-v4-pro`. Rename the provider key in `auth.json` or run `/login azure` again, rename it in `models.json`, and update `defaultProvider`, `enabledModels` patterns, and `modelThinkingLevels` keys in `settings.json`. The Responses **API** identifier remains `azure-openai-responses`; Foundry Chat Completions uses `openai-completions`. The `AZURE_OPENAI_*` environment variables are unchanged. Old Azure sessions fall back to another model on resume and lose prompt-cache reuse. KillerOS does not migrate credentials or Pi settings. See Pi's [Azure configuration](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/providers.md#azure-openai).
257
+ 1. Rename it to `azure` in `auth.json`, or run `/login azure` again.
258
+ 2. Apply the same rename in `models.json`.
259
+ 3. Update `defaultProvider`, `enabledModels` patterns, and `modelThinkingLevels` keys in `settings.json`.
161
260
 
162
- In Pi 1.0.4 codemode, `tools.read()` on an image returns an image block that you can pass to native `image()`. See Pi's [codemode tool calls](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/codemode.md#call-tools).
261
+ The Responses API identifier remains `azure-openai-responses`. Foundry Chat Completions uses `openai-completions`. The `AZURE_OPENAI_*` environment variables are unchanged. Old Azure sessions fall back to another model on resume and lose prompt-cache reuse. KillerOS does not migrate credentials or Pi settings. See [Azure configuration](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/providers.md#azure-openai).
163
262
 
164
- When codemode is enabled, Pi 1.0.3's `image()` also saves images to temporary files and includes their paths in the result, so later turns can copy or move them. Pi creates these output files with user-only permissions on POSIX systems; Windows access follows inherited filesystem ACLs. KillerOS uses Pi's native codemode behavior; see [image generation](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/codemode.md#generate-images).
263
+ ### MCP settings
165
264
 
166
- Use Pi's `/mcp` command and `.pi/mcp.json` to enable, disable, or change the exposure of a user-level MCP server for a trusted project without copying its credentials. See [project MCP overrides](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/mcp.md#configure-servers). KillerOS does not manage MCP configuration.
265
+ Use Pi's `/mcp` command and `.pi/mcp.json` to enable, disable, or change the exposure of a user-level MCP server for a trusted project without copying credentials. KillerOS does not manage MCP configuration. See [project MCP overrides](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/mcp.md#configure-servers).
167
266
 
168
- In Pi 1.0.4, a nonempty `--tools` list with no `mcp__` entry preserves MCP tool availability according to each server's exposure. For example, `--tools codemode` does not disable MCP connections or remove their tools. Use `--no-mcp` to disable MCP connections through Pi's built-in MCP support for one run. Explicit MCP selection and exclusion remain Pi's responsibility; see [MCP tool exposure](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/mcp.md#control-tool-exposure).
267
+ In Pi, a nonempty `--tools` list with no `mcp__` entry preserves MCP tool availability according to server exposure. For example, `--tools codemode` does not disable MCP connections or remove their tools. Use `--no-mcp` to disable MCP connections through Pi's built-in support for one run. Explicit MCP selection and exclusion remain Pi's responsibility. See [MCP tool exposure](https://github.com/earendil-works/pi/blob/main/packages/coding-agent/docs/mcp.md#control-tool-exposure).
169
268
 
170
- ## Development
269
+ ## Set up for development
171
270
 
172
- Strict TypeScript throughout. Tests run on Node's built-in test runner:
271
+ Clone the repository and install its pinned development dependencies:
173
272
 
174
273
  ```bash
175
- npm ci && npm run check && npm test
274
+ git clone https://github.com/KyrosHendrix/pi-KillerOS.git
275
+ cd pi-KillerOS
276
+ npm ci --ignore-scripts --no-audit --no-fund
176
277
  ```
177
278
 
279
+ Run the local package for one Pi invocation:
280
+
281
+ ```bash
282
+ npx pi -e .
283
+ ```
284
+
285
+ Disable any installed copy of KillerOS for that invocation to avoid loading it alongside the checkout. Restart Pi or use `/reload` after edits.
286
+
287
+ There is no build step. The project uses strict TypeScript, and Node's built-in test runner executes the `.ts` tests directly:
288
+
289
+ ```bash
290
+ npm run check
291
+ npm test
292
+ ```
293
+
294
+ `check` runs TypeScript without emitting files, then ESLint. For README or changelog-only changes, run the repository contracts:
295
+
296
+ ```bash
297
+ node --test --test-force-exit --experimental-strip-types test/RepositoryContracts.test.ts
298
+ ```
299
+
300
+ The default suite is authoritative. To run the installed CLI and native-terminal scenarios without forced exit:
301
+
302
+ ```bash
303
+ node --test --experimental-strip-types --test-name-pattern="installed Pi CLI|installed Pi terminal|terminal driver reports|RPC driver controls" test/PiExtensionContract.test.ts test/PiTerminalControls.test.ts test/PiRpcControls.test.ts
304
+ node --test --experimental-strip-types test/PiStartup.test.ts
305
+ ```
306
+
307
+ Development dependencies and the minimum supported Pi version are 1.2.0. CI checks the Node.js floor and current LTS on Linux and Windows, plus the minimum and latest compatible Pi releases. Compatibility installs leave the tracked manifest and lockfile unchanged. Required jobs repeat startup lifecycle checks and installed CLI and terminal pilots five times without forced exit. The latest-Pi job also repeats cancellation lifecycle checks five times. Each job runs the full default suite once.
308
+
309
+ CLI, terminal, process-control, and Git branch-isolation failures include nested errors in the test output and save a bounded `failure.txt` in the retained fixture directory. Failed CI jobs upload only these reports as `test-failures-*` artifacts, retained for seven days. Successful scenarios create no reports. A passing rerun does not establish the cause of an intermittent failure.
310
+
311
+ ## Contribution and release expectations
312
+
313
+ Run `npm run check` and `npm test` before submitting code changes. Changes to commands, configuration, requirements, or mode behavior also need README updates. Record completed changes to shipped behavior, supported workflows, public documentation, or build, test, and release guarantees under `[Unreleased]` in [CHANGELOG.md](CHANGELOG.md). Do not bump the version unless preparing a release.
314
+
178
315
  ### Release process
179
316
 
180
- 1. Prepare the release on `dev`: update `package.json` and both root versions in `package-lock.json`, update the README's pinned tag, and move completed changelog entries into a dated version section under `[Unreleased]`.
181
- 2. Run `npm run check` and `npm test`, commit and push to `dev`, then open a pull request into `main`. Push later fixes to the same PR so CI reruns.
182
- 3. Wait for every required check to pass, including CodeQL and dependency review, and resolve review conversations. The PR must be up to date with `main`.
183
- 4. Merge using **Create a merge commit**. Squash and rebase merging are disabled because the automated `main`-to-`dev` sync requires shared ancestry. Avoid advancing `dev` until that sync finishes.
184
- 5. Check the `main` CI run and the following Release run. Successful CI on the current `main` commit triggers npm publication with provenance, creates the GitHub release, and fast-forwards `dev` to the released commit. A green PR alone never publishes.
317
+ 1. Prepare the release on `dev`. Update `package.json` and both root versions in `package-lock.json`. Update this README's pinned release examples. Move completed changelog entries into a dated version section below `[Unreleased]`.
318
+ 2. Run `npm run check` and `npm test`. Commit and push to `dev`, then open a pull request into `main`. Push later fixes to the same PR so CI reruns.
319
+ 3. Wait for every required check, including CodeQL and dependency review. Resolve review conversations and bring the PR up to date with `main`.
320
+ 4. Merge with **Create a merge commit**. Squash and rebase merging are disabled because the automated `main`-to-`dev` sync requires shared ancestry. Do not advance `dev` until the sync finishes.
321
+ 5. Check the `main` CI run and the following Release run. Successful CI on the current `main` commit triggers npm publication with provenance, creates the GitHub release, and fast-forwards `dev` to the released commit. A green PR alone does not publish.
322
+
323
+ Releases go through CI on `main`; do not push version tags manually. `main` requires pull requests and required CI checks, including for administrators. Force pushes and branch deletion are blocked.
324
+
325
+ The release workflow publishes the source directory with `npm publish . --ignore-scripts=false`. At that boundary, `prepublishOnly` invokes the release validator once, even if an inherited npm setting disables scripts. Validation failure stops publication and release creation. The workflow also checks the verified commit, provenance, version, tag, and release notes.
326
+
327
+ The prepublish guard rejects ordinary direct publication, but does not prove GitHub authorization. Disabling scripts or publishing a tarball bypasses it. Configure npm's trusted publisher for `release.yml`, require two-factor authentication and disallow tokens in package publishing access, and revoke unused publish tokens. Maintainers can still publish interactively with 2FA, so workflow-only publication also depends on maintainer policy.
328
+
329
+ ### Required-check maintenance
330
+
331
+ Pi compatibility checks have stable names, `Pi compatibility (minimum)` and `Pi compatibility (latest)`, independent of tested versions. When adopting those names, confirm both passed on the release PR's exact head commit. Replace only the old versioned Pi requirements. Preserve their GitHub Actions app bindings and every other protection.
332
+
333
+ `Quality (Node 22.19.0)` validates release metadata before the full suite and tests the publication lifecycle with the release workflow's pinned npm CLI in an isolated installation. The packed-package Pi test checks the archive's entry point, README, changelog, theme, package name, and current version before activation and reload. Those assertions run across the Node, Windows, and Pi compatibility matrix.
185
334
 
186
- `main` requires pull requests and the CI workflow's required checks, including for administrators. Force pushes and branch deletion are blocked. Pi compatibility checks use the stable names `Pi compatibility (minimum)` and `Pi compatibility (latest)`, independent of the tested versions.
335
+ `Package smoke test` remains during the required-check migration. Before removing its requirement:
187
336
 
188
- When adopting these names, first confirm both checks passed on the release PR's exact head commit. Replace only the two old versioned Pi required checks with their corresponding stable names, preserving their GitHub Actions app bindings and every other protection.
337
+ 1. Land the replacement coverage and confirm it exists on `main`.
338
+ 2. Confirm the Node-floor quality check passed on the replacement PR's exact head commit.
339
+ 3. Inspect live branch protection and rulesets, and obtain approval.
340
+ 4. Remove only the smoke requirement. Preserve the required quality check's GitHub Actions binding and all other protections.
341
+ 5. Remove the job in a follow-up PR and recheck that PR's final head.
189
342
 
190
- `Quality (Node 22.19.0)` validates current release metadata before the full test suite. It also tests the publication lifecycle with the release workflow's pinned npm CLI in an isolated installation. The packed-package Pi test checks the installed archive's entry point, README, changelog, theme, package name, and current version before activation and reload. These archive assertions run throughout the Node, Windows, and Pi compatibility matrix.
343
+ Keep the job if those prerequisites are unmet.
191
344
 
192
- `Package smoke test` remains during the required-check migration. Before removing it, land the replacement coverage and confirm the Node-floor quality check passed on the replacement PR's exact head commit and that the coverage is present on `main`. Inspect live branch protection and rulesets, obtain approval, and remove only the smoke requirement while preserving the required quality check's GitHub Actions binding and every other protection. Remove the job in a follow-up PR and recheck that PR's final head. Do not remove the job if these prerequisites are unmet.
345
+ ## Known issues and limits
193
346
 
194
- Releases go through CI on `main`; do not push version tags manually. The release workflow publishes the source directory with `npm publish . --ignore-scripts=false`. The package's `prepublishOnly` lifecycle invokes the existing release validator once at that boundary, even if an inherited npm setting disables scripts. A validator failure stops publication and release creation. The earlier verified-commit, provenance, version, tag, and release-note checks remain.
347
+ - Windows Node 24.19.0 can crash during native RPC shutdown with Pi 1.2.0. RPC pilots skip that exact Node version, not other Windows versions. Use Node 22.19.0 or 24.20.0+, which includes [Node's shutdown fix](https://github.com/nodejs/node/pull/61999). Terminal pilots and cleanup controls still run on 24.19.0.
348
+ - Pi's `-name` tool modifiers do not reliably exclude extension tools. Use `--exclude-tools`, as described in [Tool selection and codemode](#tool-selection-and-codemode).
349
+ - Passive Git scans treat CRLF text as unchanged when it normalizes to the indexed LF content, including with `text=auto eol=lf` or bare `eol=lf`. After a size-changing rewrite, Git's index stat cache can still make `git status` report a modification even when `git diff` is empty.
350
+ - Questions require TUI mode. Print and JSON modes do not support goals or proactive compaction. See [Supported modes and navigation](#supported-modes-and-navigation).
195
351
 
196
- The prepublish guard rejects ordinary direct publication, but it does not prove GitHub authorization. Disabling scripts or publishing a tarball bypasses `prepublishOnly`. Configure npm's trusted publisher for `release.yml`, set package publishing access to "Require two-factor authentication and disallow tokens", and revoke unused publish tokens. npm maintainers can still publish interactively with 2FA, so workflow-only publishing also depends on maintainer policy.
352
+ Report bugs through the [issue tracker](https://github.com/KyrosHendrix/pi-KillerOS/issues). Check the [changelog](CHANGELOG.md) for release-specific changes.
197
353
 
198
354
  ## Security
199
355
 
200
- Pi extensions run with your user permissions. Review the source before installing globally. Hook commands run only for projects Pi marks as trusted; check `.pi/killeros-hooks.json` before enabling project trust. KillerOS accepts that configuration only as a regular, non-linked file no larger than 64 KiB in the project's real `.pi` directory.
356
+ KillerOS hooks run only in projects Pi marks as trusted. Check `.pi/killeros-hooks.json` before granting trust. KillerOS accepts hook configuration only as a regular, non-linked file of at most 64 KiB in the project's real `.pi` directory.
201
357
 
202
- On POSIX systems, timed-out or cancelled hooks receive `SIGTERM`, followed by `SIGKILL` if cleanup remains pending. Shell exit alone does not confirm that the original process group stopped. Cleanup uses a two-second window and reports uncertainty if group exit cannot be confirmed.
358
+ On POSIX systems, timed-out or cancelled hooks receive `SIGTERM`, then `SIGKILL` if cleanup remains pending. Shell exit alone does not confirm that the original process group stopped. Cleanup has a two-second window and reports uncertainty if it cannot confirm group exit.
203
359
 
204
- Handoff validation rejects recognized credential assignments in plain text, Markdown lists, bold labels, and inline-code labels before creating a destination session. This pattern check cannot detect every secret or personally identifying value. Rejected values are not included in the error notification.
360
+ Handoff validation rejects recognized credential assignments in plain text, Markdown lists, bold labels, and inline-code labels before creating a destination session. Rejected values do not appear in the error notification. This pattern check cannot detect every secret or personally identifying value. Review context before carrying it into another session.
205
361
 
206
362
  ## License
207
363
 
@@ -35,6 +35,7 @@ export interface AutoCompactionDependencies {
35
35
  loadPreference?: (ctx: ExtensionContext) => AutoCompactionPreference;
36
36
  getCompactionSettings?: (ctx: ExtensionContext) => CompactionSettings;
37
37
  settingsStore?: KillerosSettingsStore;
38
+ onPreferenceChange?: (preference: AutoCompactionPreference | undefined) => void;
38
39
  goal?: AutoCompactionGoalHandlers;
39
40
  }
40
41
 
@@ -142,10 +143,12 @@ export function registerAutoCompaction(
142
143
  if (!argument || argument === "status") {
143
144
  try {
144
145
  const preference = readAutoCompactionPreference(settingsStore.load());
146
+ dependencies.onPreferenceChange?.(preference);
145
147
  ctx.ui.notify(preference.enabled
146
148
  ? `Automatic compaction: on at ${preference.percentRemaining}% remaining`
147
149
  : `Automatic compaction: off (threshold ${preference.percentRemaining}%)`, "info");
148
150
  } catch (error) {
151
+ dependencies.onPreferenceChange?.(undefined);
149
152
  ctx.ui.notify(`Automatic compaction setting could not be read: ${errorMessage(error)}`, "error");
150
153
  }
151
154
  return;
@@ -167,6 +170,7 @@ export function registerAutoCompaction(
167
170
  return { autoCompaction: updated };
168
171
  });
169
172
  if (!updated) throw new Error("Settings update did not apply the preference");
173
+ dependencies.onPreferenceChange?.(updated);
170
174
  ctx.ui.notify(percent === undefined
171
175
  ? updated.enabled
172
176
  ? `Automatic compaction: on at ${updated.percentRemaining}% remaining`
@@ -269,7 +273,9 @@ export function registerAutoCompaction(
269
273
  try {
270
274
  preference = loadPreference(ctx);
271
275
  compactionSettings = getCompactionSettings(ctx);
276
+ dependencies.onPreferenceChange?.(preference);
272
277
  } catch (error) {
278
+ dependencies.onPreferenceChange?.(undefined);
273
279
  if (!settingsErrorReported) {
274
280
  settingsErrorReported = true;
275
281
  ctx.ui.notify(`Automatic compaction settings could not be read: ${errorMessage(error)}`, "error");
@@ -3,6 +3,8 @@ import { watch } from "node:fs";
3
3
  import path from "node:path";
4
4
  import { type ExtensionAPI, type ExtensionContext, type Theme, type ThemeColor } from "@earendil-works/pi-coding-agent";
5
5
  import { truncateToWidth, visibleWidth, type TUI } from "@earendil-works/pi-tui";
6
+ import { readAutoCompactionPreference, type AutoCompactionPreference } from "./auto-compaction.ts";
7
+ import { createKillerosSettingsStore, type KillerosSettingsStore } from "./settings.ts";
6
8
  import { isCodexFastEnabled, isCodexFastModel, subscribeCodexFast } from "./codex-fast-state.ts";
7
9
  import { formatCwd, formatTime, modelDisplayName, padRight } from "./display.ts";
8
10
  import { inspectWorktreeWithoutFilters, passiveGitCommand, passiveGitEnv, type PassiveGitRunner } from "./passive-git-status.ts";
@@ -255,14 +257,33 @@ export function contextPercentRemaining(ctx: ExtensionContext): number | null {
255
257
  return Math.round(Math.max(0, Math.min(100, percentRemaining)));
256
258
  }
257
259
 
258
- export function formatContextProgress(tokensUsed: number | null, contextWindow: number, theme: Theme): string {
259
- if (tokensUsed === null || !Number.isFinite(tokensUsed)) return theme.fg("dim", "ctx —%");
260
- const windowSize = Number.isFinite(contextWindow) && contextWindow > 0 ? contextWindow : 128_000;
261
- const used = Math.max(0, tokensUsed);
262
- const percentRemaining = Math.max(0, Math.min(100, Math.round(((windowSize - used) / windowSize) * 100)));
260
+ function validCapacity(value: number | undefined): value is number {
261
+ return value !== undefined && Number.isFinite(value) && value > 0;
262
+ }
263
+
264
+ function formatContextTokens(tokens: number): string {
265
+ const unit = tokens >= 1_000_000 ? 1_000_000 : tokens >= 1_000 ? 1_000 : 1;
266
+ // Floor labels so rounding never promises tokens that are not available.
267
+ const count = unit === 1 ? Math.floor(tokens) : Math.floor(tokens / (unit / 100)) / 100;
268
+ return `${count}${unit === 1_000_000 ? "M" : unit === 1_000 ? "K" : ""}`;
269
+ }
270
+
271
+ export function formatContextProgress(
272
+ tokensUsed: number | null,
273
+ contextWindow: number,
274
+ theme: Theme,
275
+ showManualHint = false,
276
+ detail: "full" | "capacity" | "percentage" = "full",
277
+ ): string {
278
+ if (!validCapacity(contextWindow)) return theme.fg("dim", "ctx —%");
279
+ const capacity = detail === "percentage" ? "" : ` (${formatContextTokens(contextWindow)})`;
280
+ if (tokensUsed === null || !Number.isFinite(tokensUsed)) return theme.fg("dim", `ctx —%${capacity}`);
281
+ const remaining = Math.max(0, contextWindow - Math.max(0, tokensUsed));
282
+ const percentRemaining = Math.round(remaining / contextWindow * 100);
263
283
  const color: ThemeColor = percentRemaining > 50 ? "success" : percentRemaining >= 20 ? "warning" : "error";
264
- const action = percentRemaining <= 15 ? " · /compact" : "";
265
- return theme.fg(color, `ctx ${percentRemaining}%${action}`);
284
+ const counts = detail === "full" ? ` (${formatContextTokens(remaining)}/${formatContextTokens(contextWindow)})` : capacity;
285
+ const action = showManualHint && percentRemaining <= 15 ? " · /compact" : "";
286
+ return theme.fg(color, `ctx ${percentRemaining}%${counts}${action}`);
266
287
  }
267
288
 
268
289
  function sumSessionCost(ctx: ExtensionContext): number {
@@ -367,7 +388,16 @@ function formatGitFileChanges(changes: GitFileChanges, theme: Theme): string {
367
388
  return `${theme.fg("dim", `±${total} [`)}${theme.fg("warning", `~${changes.modified}`)} ${theme.fg("success", `+${changes.added}`)} ${theme.fg("error", `−${changes.deleted}`)}${theme.fg("dim", "]")}`;
368
389
  }
369
390
 
370
- export function registerFooter(pi: ExtensionAPI, goalRuntime: GoalRuntime): void {
391
+ export function registerFooter(
392
+ pi: ExtensionAPI,
393
+ goalRuntime: GoalRuntime,
394
+ settingsStore: KillerosSettingsStore = createKillerosSettingsStore(),
395
+ ): (preference: AutoCompactionPreference | undefined) => void {
396
+ let autoCompactionEnabled: boolean | undefined;
397
+ const updatePreference = (preference: AutoCompactionPreference | undefined): void => {
398
+ autoCompactionEnabled = preference?.enabled;
399
+ activeTui?.requestRender();
400
+ };
371
401
  let currentModel: ExtensionContext["model"];
372
402
  let thinkingLevel: ThinkingLevel = "off";
373
403
  let activeTui: TUI | undefined;
@@ -394,6 +424,11 @@ export function registerFooter(pi: ExtensionAPI, goalRuntime: GoalRuntime): void
394
424
  pi.on("session_start", (_event, ctx) => {
395
425
  resetSessionCost();
396
426
  if (ctx.mode !== "tui") return;
427
+ try {
428
+ autoCompactionEnabled = readAutoCompactionPreference(settingsStore.load()).enabled;
429
+ } catch {
430
+ autoCompactionEnabled = undefined;
431
+ }
397
432
  unsubscribeCodexFast?.();
398
433
  unsubscribeCodexFast = subscribeCodexFast(() => activeTui?.requestRender());
399
434
  const sessionStart = Date.now();
@@ -437,31 +472,31 @@ export function registerFooter(pi: ExtensionAPI, goalRuntime: GoalRuntime): void
437
472
  } catch {
438
473
  usage = undefined;
439
474
  }
440
- const contextWindow = usage?.contextWindow ?? model?.contextWindow ?? 128_000;
441
- const context = formatContextProgress(usage?.tokens ?? null, contextWindow, theme);
475
+ const contextWindow = validCapacity(usage?.contextWindow)
476
+ ? usage.contextWindow : validCapacity(model?.contextWindow) ? model.contextWindow : Number.NaN;
477
+ const contexts = (["full", "capacity", "percentage"] as const).map((detail) =>
478
+ formatContextProgress(usage?.tokens ?? null, contextWindow, theme, autoCompactionEnabled === false, detail));
442
479
  const branch = footerData.getGitBranch();
443
480
  const signature = formatModel(model, theme, true, isCodexFastEnabled());
444
481
  const fullDirectory = colorDirectory(cwd);
445
482
  const focusedDirectory = colorDirectory(compactDirectory(cwd));
446
483
  const goal = formatGoalFooter(goalRuntime.state, theme);
447
- const primary = joinFooterParts([
448
- signature,
449
- level,
450
- context,
451
- ], theme);
452
- const primaryFocused = joinFooterParts([signature, context], theme);
484
+ const primary = contexts.map((context) => joinFooterParts([signature, level, context], theme));
485
+ const primaryFocused = contexts.map((context) => joinFooterParts([signature, context], theme));
453
486
  const session = joinFooterParts([
454
487
  theme.fg("dim", formatTime(Date.now() - sessionStart)),
455
488
  theme.fg("dim", formatCost(getSessionCost(ctx))),
456
489
  ], theme);
457
490
  const essentialModel = formatModel(model, theme, false, isCodexFastEnabled());
458
- const primaryRow = footerRowFits(primary, session, width)
459
- ? renderFooterRow(primary, session, width)
460
- : footerRowFits(primary, "", width)
461
- ? renderFooterRow(primary, "", width)
462
- : footerRowFits(primaryFocused, "", width)
463
- ? renderFooterRow(primaryFocused, "", width)
464
- : renderFooterRow(essentialModel, context, width);
491
+ const candidates = [
492
+ { left: primary[0] ?? "", right: session },
493
+ ...primary.map((left) => ({ left, right: "" })),
494
+ ...primaryFocused.map((left) => ({ left, right: "" })),
495
+ ...contexts.map((right) => ({ left: essentialModel, right })),
496
+ ];
497
+ const selected = candidates.find(({ left, right }) => footerRowFits(left, right, width))
498
+ ?? { left: essentialModel, right: contexts[2] ?? "" };
499
+ const primaryRow = renderFooterRow(selected.left, selected.right, width);
465
500
  const changes = gitFileChanges ? formatGitFileChanges(gitFileChanges, theme) : "";
466
501
  const branchLabel = branch
467
502
  ? `${theme.fg("dim", branch)}${changes ? `${theme.fg("dim", " · ")}${changes}` : ""}`
@@ -501,4 +536,5 @@ export function registerFooter(pi: ExtensionAPI, goalRuntime: GoalRuntime): void
501
536
  activeTui = undefined;
502
537
  goalRuntime.requestRender = undefined;
503
538
  });
539
+ return updatePreference;
504
540
  }
@@ -10,7 +10,6 @@ import {
10
10
  transitionGoalState,
11
11
  type GoalTransitionOptions,
12
12
  } from "./goal-state.ts";
13
- import { resolvePersonalInstructions } from "./personal-instructions.ts";
14
13
  import type { GoalRuntime, GoalState, GoalStatus } from "./runtime.ts";
15
14
  import { safeTerminalText } from "./safe-terminal-text.ts";
16
15
 
@@ -284,7 +283,7 @@ function sendGoalTurn(
284
283
  try {
285
284
  pi.sendMessage({
286
285
  customType: GOAL_CONTINUATION_TYPE,
287
- content: goalContinuationMessage(state, ctx),
286
+ content: goalContinuationMessage(state),
288
287
  display: false,
289
288
  }, { triggerTurn: true, deliverAs: "followUp" });
290
289
  return true;
@@ -396,13 +395,8 @@ function goalSystemPrompt(state: GoalState): string {
396
395
  return goalInstructions(state, "Active KillerOS goal");
397
396
  }
398
397
 
399
- function goalContinuationMessage(state: GoalState, ctx: ExtensionContext): string {
400
- const sections = [goalInstructions(state, "KillerOS long-running goal turn")];
401
- if (ctx.isProjectTrusted()) {
402
- const personal = resolvePersonalInstructions(ctx.cwd);
403
- if (personal) sections.push(personal);
404
- }
405
- return sections.join("\n\n");
398
+ function goalContinuationMessage(state: GoalState): string {
399
+ return goalInstructions(state, "KillerOS long-running goal turn");
406
400
  }
407
401
 
408
402
  export function isGoalModeSupported(ctx: ExtensionContext): boolean {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "killeros",
3
- "version": "3.0.5",
3
+ "version": "3.0.7",
4
4
  "description": "TUI, goals, and workflow automation for the Pi coding agent",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -45,21 +45,22 @@
45
45
  ]
46
46
  },
47
47
  "peerDependencies": {
48
- "@earendil-works/pi-ai": ">=1.0.4 <2",
49
- "@earendil-works/pi-coding-agent": ">=1.0.4 <2",
50
- "@earendil-works/pi-tui": ">=1.0.4 <2",
48
+ "@earendil-works/pi-ai": ">=1.2.0 <2",
49
+ "@earendil-works/pi-coding-agent": ">=1.2.0 <2",
50
+ "@earendil-works/pi-tui": ">=1.2.0 <2",
51
51
  "typebox": ">=1.1.38 <2"
52
52
  },
53
53
  "devDependencies": {
54
- "@earendil-works/pi-ai": "1.0.4",
55
- "@earendil-works/pi-coding-agent": "1.0.4",
56
- "@earendil-works/pi-server": "1.0.4",
57
- "@earendil-works/pi-tui": "1.0.4",
54
+ "@earendil-works/pi-ai": "1.2.0",
55
+ "@earendil-works/pi-coding-agent": "1.2.0",
56
+ "@earendil-works/pi-tui": "1.2.0",
58
57
  "@types/node": "24.12.4",
58
+ "@xterm/headless": "6.0.0",
59
59
  "eslint": "^10.9.1",
60
60
  "typebox": "1.3.20",
61
61
  "typescript": "5.9.3",
62
- "typescript-eslint": "^8.68.0"
62
+ "typescript-eslint": "^8.68.0",
63
+ "zigpty": "0.2.1"
63
64
  },
64
65
  "overrides": {
65
66
  "brace-expansion": "5.0.12"
@@ -1,5 +1,5 @@
1
1
  {
2
- "$schema": "https://raw.githubusercontent.com/earendil-works/pi/main/packages/coding-agent/src/modes/interactive/theme/theme-schema.json",
2
+ "$schema": "https://pi.dev/schemas/theme.schema.json",
3
3
  "name": "killeros",
4
4
  "vars": {
5
5
  "coral": "#d77757",