@clapecho233/pi-smart-fold 0.1.0 → 0.2.0
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/README.md +104 -27
- package/index.ts +1185 -61
- package/lib/config.ts +55 -7
- package/lib/fold.ts +291 -9
- package/lib/thinking.ts +194 -0
- package/package.json +11 -2
- package/scripts/link-pi.mjs +75 -0
- package/scripts/load-check.mjs +44 -0
- package/test/click-sim.mjs +414 -0
- package/test/fold.test.mjs +483 -6
- package/tsconfig.json +14 -0
package/README.md
CHANGED
|
@@ -1,64 +1,141 @@
|
|
|
1
1
|
# pi-smart-fold
|
|
2
2
|
|
|
3
|
-
[pi](https://github.com/earendil-works/pi-mono) coding-agent
|
|
3
|
+
A [pi](https://github.com/earendil-works/pi-mono) coding-agent extension that keeps the session transcript compact — without losing key information.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
- 折叠仅影响 TUI 显示,不改动会话文件与发送给模型的上下文。
|
|
5
|
+
| Without smart-fold | With smart-fold |
|
|
6
|
+
| --- | --- |
|
|
7
|
+
| Long thinking walls, verbose tool output, full file previews | One-line `Thought for 12.4s` summaries, folded tool output, `write src/index.ts +12 -3` headers |
|
|
9
8
|
|
|
10
|
-
##
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
### 1. Thinking fold + live timer (`smart` mode, default)
|
|
12
|
+
|
|
13
|
+
**While the model thinks**, the block shows a bold **`Thinking… (8s)`** header line, followed by the scrolling *tail* of the thinking text (like `tail -f`). **One click toggles directly to the full text so far** (the ticking **`Thinking… (8s)`** line stays pinned at the bottom and keeps updating); **one more click toggles back** to the scrolling tail. There is no intermediate label state — a single click always switches between `scrolling tail ↔ full view`:
|
|
11
14
|
|
|
12
15
|
```text
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
Scrolling tail (default) Full view (after one click)
|
|
17
|
+
Thinking… (8s) …earlier thinking text…
|
|
18
|
+
…latest line of thinking …thinking still streaming…
|
|
19
|
+
Thinking… (9s) ← pinned at the bottom, live-updating
|
|
16
20
|
```
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
**After the thinking run finishes**, the block collapses to a single bold **`Thought for 12.4s`** line (measured duration). If the block was in full view at that moment, it transitions smoothly to the pinned duration footer first.
|
|
23
|
+
|
|
24
|
+
**Single-click expand/collapse per block**: finished thinking starts hidden (seeded by a prototype patch). **One click → that block expands to its full text** (with the bold duration line kept at the bottom); **one more click → collapses back**. No intermediate states, and only the clicked block is affected — other thinking runs in the same message are untouched. The same one-click toggle works for a run that already finished *while its message is still streaming* (e.g. thinking done, answer text or a tool call still coming): the folded `Thought for …` line expands directly — never through pi's bare `Thought…` label middle state.
|
|
25
|
+
|
|
26
|
+
- **Fully takes over native folding**: pi's built-in hidden-thinking label (`Thinking...`) is replaced with per-message `Thought for …` labels generated by this extension. What you see after clicks or `ctrl+t` is always our text, never the native one. (In `smart` mode `ctrl+t` has no extra effect — use clicks or `/fold expand` instead.)
|
|
27
|
+
- **Exact click detection**: an idempotent prototype patch on pi's publicly exported `AssistantMessageComponent` seeds the hidden state of finished thinking runs and observes writes to the component's internal visibility map (distinguishing `ctrl+t`'s clear operation). Clicks on runs of a still-streaming message — the live run as well as already-finished ones — are redirected so the native two-state toggle's hidden middle state never appears. Global re-renders — theme changes, window resizes, layout redraws — are never mistaken for clicks. If a future pi version can't be patched, the extension degrades gracefully to pi's native display (with `/fold expand` as a fallback).
|
|
28
|
+
- **Persistent durations**: each thinking run's duration is recorded in the session file keyed by content hash (a `smart-fold-thinking` custom entry, never sent to the LLM context), so durations still show after `/resume`. Multiple thinking runs within one message (between tool calls) are timed individually.
|
|
29
|
+
- **Code-block safe**: while a code fence streams through the thinking text, structural fence lines are skipped in the collapsed tail — the block never flashes fully open for a frame. In the expanded view an unclosed code fence is closed automatically so the `Thinking…/Thought for …` footer renders below it as bold text, never swallowed into the code block as literal `**asterisks**`.
|
|
30
|
+
- Truncation is measured in **terminal display columns** (CJK characters / emoji count as 2), keeping the line ending with an `…` prefix when over-wide.
|
|
31
|
+
- Folding is **display-only**: the session file and the context sent to the model are never modified.
|
|
32
|
+
|
|
33
|
+
Other modes: `tail` (always one line — the text tail with a bold duration prefix), `full` (show full text after the run ends, with the duration footer), `off` (disable and restore pi's default thinking display).
|
|
34
|
+
|
|
35
|
+
Fallback: `/fold expand on|off` (or "Expand all thinking" in the settings panel) temporarily expands/collapses *all* finished thinking blocks. Daily usage only needs single clicks on individual blocks; new sessions always start fully folded.
|
|
36
|
+
|
|
37
|
+
### 2. Tool output folding
|
|
38
|
+
|
|
39
|
+
On every `session_start` (startup, `/reload`, `/new`, `/resume`, `/fork`) the extension calls `ctx.ui.setToolsExpanded(false)` so tool output stays collapsed; press `ctrl+o` to expand manually.
|
|
40
|
+
|
|
41
|
+
### 3. Tool call line truncation + write/edit diff stats
|
|
42
|
+
|
|
43
|
+
- **Long call lines are truncated**: `bash` / `read` / `grep` / `find` / `ls` call lines collapse to a single line, cut to the terminal width with a trailing `…` (pi's ANSI-aware `truncateToWidth`). An extra ` …` marker is appended when the command has more lines. Click or `ctrl+o` to expand and see everything.
|
|
44
|
+
- **write / edit line-diff stats**:
|
|
45
|
+
- `write`: reads the original file content *before* the write executes and computes a line-level diff (common prefix/suffix trimming + LCS). The header line gains a **green `+added` / red `-removed`** suffix, e.g. `write src/index.ts +12 -3`; new files show `+N -0`. Files larger than 8 MB are skipped. Stats are **persisted with the tool result in the session file**, so they still show in restored sessions (writes made before this version have no record and show nothing).
|
|
46
|
+
- `edit`: diffs each `edits[]` entry's `oldText → newText` directly and sums the result, e.g. `edit src/app.ts +2 -1` (updates live while arguments stream in).
|
|
47
|
+
- **write/edit collapse to a header-only line by default** (`writeCollapsed: header`): collapsed rows show just the one-line header — no code content or diff preview (not natively possible in pi; implemented by wrapping both `renderCall` and `renderResult` — the result area renders an empty component while collapsed, and error messages remain visible). Click / `ctrl+o` expands to the full diff / syntax-highlighted content. The **`+N -M` stat stays in the header line in both states** (edit's native full-width background bar is preserved; the stat is injected into the trailing padding without changing the line width). Switch to `preview` in settings to restore pi's native preview.
|
|
48
|
+
- Implemented as rendering wrappers around pi's built-in tools (execution logic is fully reused from each `create*ToolDefinition`).
|
|
49
|
+
|
|
50
|
+
## Settings
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
/fold Open a /config-style interactive settings list (Enter/Space to change, Esc to close)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Or change values directly (with autocompletion):
|
|
57
|
+
|
|
58
|
+
```text
|
|
59
|
+
/fold thinking smart|tail|full|off # on=smart, off=off accepted for compatibility
|
|
60
|
+
/fold tools on|off # fold tool output at session start
|
|
61
|
+
/fold writestat on|off # write/edit diff stats toggle
|
|
62
|
+
/fold writecollapsed header|preview # write rows: header-only / keep preview when collapsed
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Config file `smart-fold.config.json` (next to the extension entry; the legacy boolean `thinkingFold` is migrated automatically):
|
|
19
66
|
|
|
20
67
|
```json
|
|
21
68
|
{
|
|
22
69
|
"toolsFold": true,
|
|
23
|
-
"
|
|
70
|
+
"thinking": "smart",
|
|
71
|
+
"writeStat": true,
|
|
72
|
+
"writeCollapsed": "header"
|
|
24
73
|
}
|
|
25
74
|
```
|
|
26
75
|
|
|
27
|
-
##
|
|
76
|
+
## Installation
|
|
28
77
|
|
|
29
|
-
|
|
78
|
+
Any one of these:
|
|
30
79
|
|
|
31
80
|
```bash
|
|
32
|
-
#
|
|
81
|
+
# Option A: clone into pi's global extension auto-discovery directory
|
|
33
82
|
git clone <this-repo> ~/.pi/agent/extensions/smart-fold
|
|
34
83
|
|
|
35
|
-
#
|
|
84
|
+
# Option B: install via the pi package manager
|
|
36
85
|
pi install git:<repo-url>
|
|
37
86
|
|
|
38
|
-
#
|
|
87
|
+
# Option C: add to settings.json
|
|
39
88
|
# ~/.pi/agent/settings.json → { "extensions": ["/path/to/pi-smart-fold"] }
|
|
40
89
|
|
|
41
|
-
#
|
|
90
|
+
# Try it temporarily
|
|
42
91
|
pi -e /path/to/pi-smart-fold/index.ts
|
|
43
92
|
```
|
|
44
93
|
|
|
45
|
-
>
|
|
94
|
+
> The extension has no npm dependencies — pi loads the TypeScript directly via jiti.
|
|
46
95
|
|
|
47
|
-
##
|
|
96
|
+
## Development
|
|
48
97
|
|
|
49
98
|
```bash
|
|
50
|
-
npm
|
|
99
|
+
npm install # dev deps + relink node_modules/@earendil-works to the installed pi (postinstall)
|
|
100
|
+
npm run link:pi # re-point the type/runtime links after a pi upgrade — always current version
|
|
101
|
+
npm run typecheck # strict tsc against the installed pi's .d.ts (no emit)
|
|
102
|
+
npm run check # link + typecheck + jiti load-check + unit tests + click simulation
|
|
103
|
+
npm test # unit tests for the pure functions (native TS type stripping, Node ≥ 22.18, no build step)
|
|
104
|
+
npm run test:sim # click-cycle simulation against the real pi AssistantMessageComponent
|
|
51
105
|
```
|
|
52
106
|
|
|
53
|
-
|
|
107
|
+
The extension itself has no npm dependencies — pi loads the TypeScript directly via jiti.
|
|
108
|
+
`typescript` / `@types/node` are dev-only, and `node_modules/@earendil-works/*` are symlinks into
|
|
109
|
+
the pi installation that loads the extension (created by `scripts/link-pi.mjs`), so typecheck
|
|
110
|
+
always runs against the exact pi version installed on the machine.
|
|
111
|
+
|
|
112
|
+
Project layout:
|
|
54
113
|
|
|
55
114
|
```
|
|
56
|
-
index.ts
|
|
57
|
-
lib/fold.ts
|
|
58
|
-
lib/
|
|
59
|
-
|
|
115
|
+
index.ts Extension entry (transformer / events / write render wrappers / /fold settings UI)
|
|
116
|
+
lib/fold.ts Pure functions: display width, tail truncation, duration formatting, line diff
|
|
117
|
+
lib/thinking.ts Thinking timer state machine (per-run durations keyed by content hash)
|
|
118
|
+
lib/config.ts Config load/save (with legacy migration; falls back to defaults when missing/corrupt)
|
|
119
|
+
scripts/link-pi.mjs Symlink the installed pi runtime into node_modules (version-following)
|
|
120
|
+
scripts/load-check.mjs Smoke test: load the extension via pi's jiti and exercise registration
|
|
121
|
+
test/fold.test.mjs Unit tests
|
|
122
|
+
test/click-sim.mjs Click-cycle simulation (real pi component + simulated clicks, verifies tail ↔ full toggling)
|
|
60
123
|
```
|
|
61
124
|
|
|
62
|
-
##
|
|
125
|
+
## Known limitations
|
|
126
|
+
|
|
127
|
+
- Single-click toggling of thinking runs in a *live* message depends on the prototype patch's redirect; without the patch it degrades to pi's native two-state toggle (two clicks to expand).
|
|
128
|
+
- Expand state is runtime-only: new sessions/reloads start folded again. Window resizes never collapse blocks you've expanded.
|
|
129
|
+
- write diff stats only apply to writes executed in the current session (restored older sessions have no pre-execution snapshot to diff against).
|
|
130
|
+
- When stacked with pi's native "Hide thinking blocks" (`ctrl+t` toggle): if thinking is hidden that way, the live scrolling line won't show — keep the native display at its default and let this extension handle folding.
|
|
131
|
+
|
|
132
|
+
## Compatibility
|
|
133
|
+
|
|
134
|
+
Built and verified against pi **`0.86.1`** public extension APIs: `registerMarkdownTransformer`,
|
|
135
|
+
`ctx.ui.setToolsExpanded` / `setHiddenThinkingLabel`, `create*ToolDefinition` (and friends),
|
|
136
|
+
`registerTool` (`renderCall` / `renderResult` + `ToolRenderContext`), `registerCommand`, `appendEntry`,
|
|
137
|
+
`SettingsList`, and the `AssistantMessageComponent` click-internals patch (`updateContent`,
|
|
138
|
+
`thinkingVisibilityOverrides`, `hiddenThinkingLabel`).
|
|
63
139
|
|
|
64
|
-
|
|
140
|
+
After upgrading pi, run `npm run link:pi && npm run check` — the links follow the newly installed
|
|
141
|
+
version automatically, and the check verifies types, jiti loading, and behavior against it.
|