@leing2021/super-pi 0.14.1 → 0.15.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 +174 -252
- package/extensions/ce-core/index.ts +41 -0
- package/extensions/ce-core/tools/bash-output-filter.ts +402 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
# Super Pi
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Turn your AI coding agent from "a tool that writes code" into "a reliable engineer."**
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
## Install
|
|
5
|
+
Install it, tell Pi what you want to build, then keep saying "continue" — it walks through the full loop: **think → plan → build → review → compound learnings.**
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
8
|
pi install npm:@leing2021/super-pi
|
|
@@ -12,333 +10,274 @@ pi install npm:@leing2021/super-pi
|
|
|
12
10
|
|
|
13
11
|
---
|
|
14
12
|
|
|
15
|
-
##
|
|
13
|
+
## Why Super Pi
|
|
16
14
|
|
|
17
|
-
|
|
15
|
+
Using a bare AI agent to write code has three common failure modes:
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
1. **Builds before thinking** — you finish and realize it's not what you wanted
|
|
18
|
+
2. **Loses context on interruption** — close the terminal, lose the progress
|
|
19
|
+
3. **Repeats the same mistakes** — every session starts from zero
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
You: I want to build a tool that helps indie devs find users
|
|
21
|
+
Super Pi's answers:
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
→ YC-style forcing questions one at a time: demand evidence? current alternatives? narrowest wedge?
|
|
28
|
-
→ Challenges premises, generates 2-3 approaches
|
|
29
|
-
→ Generates docs/brainstorms/2026-04-18-find-users-requirements.md
|
|
30
|
-
→ Recommends next step: 02-plan
|
|
23
|
+
- **Forces clarity before action** — not a form to fill out, but an AI that interrogates you like a YC partner demanding specific evidence
|
|
24
|
+
- **Auto-resume from checkpoints** — restart after interruption, skip completed work, continue from where you left off
|
|
25
|
+
- **Auto-compounds experience** — every solved problem becomes a searchable knowledge card; next time, the agent finds and reuses it
|
|
31
26
|
|
|
32
|
-
|
|
27
|
+
---
|
|
33
28
|
|
|
34
|
-
|
|
35
|
-
→ Reads requirements artifact
|
|
36
|
-
→ Generates docs/plans/2026-04-18-find-users-plan.md
|
|
37
|
-
→ Asks if you want CEO Review → you pick "CEO Review"
|
|
38
|
-
→ Challenges plan premises, dream-state mapping, forces alternatives, temporal interrogation
|
|
39
|
-
→ Recommends next step: 03-work
|
|
29
|
+
## The Five-Step Loop
|
|
40
30
|
|
|
41
|
-
|
|
31
|
+
```
|
|
32
|
+
01-brainstorm → 02-plan → 03-work → 04-review → 05-compound
|
|
33
|
+
think plan build review compound
|
|
34
|
+
```
|
|
42
35
|
|
|
43
|
-
|
|
44
|
-
→ Parallel execution, checkpoint resume
|
|
45
|
-
→ Recommends next step: 04-review
|
|
36
|
+
Each step has a dedicated skill + tool pair. Not just prompts — structured toolchains.
|
|
46
37
|
|
|
47
|
-
|
|
38
|
+
---
|
|
48
39
|
|
|
49
|
-
|
|
50
|
-
→ Structured code review + autofix
|
|
51
|
-
→ Asks if you want browser QA → you pick "Browser QA"
|
|
52
|
-
→ Opens your app with agent-browser, tests page by page
|
|
53
|
-
→ Fixes bugs found, screenshots as evidence
|
|
54
|
-
→ Recommends next step: 05-compound
|
|
40
|
+
## What Each Step Does
|
|
55
41
|
|
|
56
|
-
|
|
42
|
+
### 01-brainstorm: Think First
|
|
57
43
|
|
|
58
|
-
|
|
59
|
-
→ Extracts reusable patterns
|
|
60
|
-
→ Writes to docs/solutions/ for lasting knowledge
|
|
61
|
-
```
|
|
44
|
+
Not "describe your requirements." Three modes for three scenarios:
|
|
62
45
|
|
|
63
|
-
|
|
46
|
+
| Mode | For | What it does |
|
|
47
|
+
|------|-----|-------------|
|
|
48
|
+
| **Startup Diagnostic** | Startup ideas, new products | Six YC-style forcing questions, pushed until you produce specific evidence (not "people are interested" — "who would freak out if this disappeared tomorrow?") |
|
|
49
|
+
| **Builder Mode** | Side projects, hackathons | Only focused on building the coolest thing. If you accidentally mention revenue, it auto-upgrades to Startup Diagnostic |
|
|
50
|
+
| **CE Brainstorm** | Adding features to existing projects | Multi-round dialog to clarify scope boundaries, generates a structured requirements doc |
|
|
64
51
|
|
|
65
|
-
|
|
66
|
-
You: I want to make a fun music visualization side project
|
|
67
|
-
|
|
68
|
-
→ /skill:01-brainstorm
|
|
69
|
-
→ AI asks your goal → you pick "Side project / hackathon"
|
|
70
|
-
→ Enters Builder Mode
|
|
71
|
-
→ Asks: what's the coolest version? fastest path to something you can show?
|
|
72
|
-
→ No business validation, focused on building the coolest thing
|
|
73
|
-
→ If conversation shifts to revenue → auto-upgrades to Startup Diagnostic
|
|
74
|
-
```
|
|
52
|
+
All three modes run a **premise challenge** (are your assumptions valid?) and **alternatives generation** (at least one minimal + one ideal approach) before you're allowed to move on.
|
|
75
53
|
|
|
76
|
-
###
|
|
54
|
+
### 02-plan: Plan Well
|
|
77
55
|
|
|
78
|
-
|
|
79
|
-
You: I want to add user authentication to the project
|
|
56
|
+
Breaks requirements into implementation units, each following strict **RED → GREEN → REFACTOR** (no production code without a failing test first).
|
|
80
57
|
|
|
81
|
-
|
|
82
|
-
→ AI asks your goal → you pick "Adding a feature"
|
|
83
|
-
→ Enters CE Brainstorm (classic requirements discovery)
|
|
84
|
-
→ Multi-round dialog to clarify requirements (OAuth2? JWT? MFA?)
|
|
85
|
-
→ Generates docs/brainstorms/2026-04-18-auth-requirements.md
|
|
86
|
-
→ Recommends next step: 02-plan
|
|
87
|
-
```
|
|
58
|
+
**Incremental updates**: Requirements changed? `plan_diff` detects added/removed/modified units and patches the plan instead of rewriting it.
|
|
88
59
|
|
|
89
|
-
|
|
60
|
+
**CEO Review (optional)**: After planning, you can request a CEO Review. It challenges your plan using Bezos reversible-decision frameworks, Munger inversion, Jobs subtraction, forces alternative approaches, and draws error maps. Like having a demanding CTO review your proposal for free.
|
|
90
61
|
|
|
91
|
-
|
|
92
|
-
You: /skill:08-status
|
|
62
|
+
### 03-work: Build Right
|
|
93
63
|
|
|
94
|
-
|
|
95
|
-
→ Queries session_history for recent execution records
|
|
96
|
-
→ Shows current progress + recommends next step
|
|
97
|
-
```
|
|
64
|
+
**Parallel execution**: `task_splitter` uses a Union-Find algorithm to analyze file dependencies, feeds conflict-free units to `parallel_subagent` for concurrent execution.
|
|
98
65
|
|
|
99
|
-
|
|
66
|
+
**Checkpoint resume**: After each unit, a checkpoint is saved. Interrupted? Next startup auto-loads, skips completed work, continues from the breakpoint. Failed? `fail` records the error → `retry` suggests a recovery strategy (timeout? extend timeout. Permission issue? check permissions first. Code error? fix then retry).
|
|
100
67
|
|
|
101
|
-
|
|
102
|
-
You: Requirements changed, need to add SSO support
|
|
68
|
+
**Strict TDD**: Run failing test → write minimal implementation → test passes → refactor. Every step requires command output as evidence. No skipping.
|
|
103
69
|
|
|
104
|
-
|
|
105
|
-
→ plan_diff compare detects added/removed/modified units
|
|
106
|
-
→ plan_diff patch applies incremental changes
|
|
107
|
-
→ No need to rewrite the entire plan
|
|
108
|
-
```
|
|
70
|
+
### 04-review: Review Thoroughly
|
|
109
71
|
|
|
110
|
-
|
|
72
|
+
**Structured code review**: `review_router` auto-assigns reviewer personas based on diff metadata (changed payment code? bring in the security reviewer). Review discipline is technical evaluation, not theater — every finding must cite specific code, YAGNI checks, no performative agreement.
|
|
111
73
|
|
|
112
|
-
|
|
113
|
-
You: /skill:03-work docs/plans/auth-plan.md
|
|
74
|
+
**Browser QA (optional)**: Uses `agent-browser` to open your app, click through pages, screenshot bugs, fix by severity, up to 3 auto-fix iterations. Can auto-generate regression tests. Like having a QA engineer run acceptance tests.
|
|
114
75
|
|
|
115
|
-
|
|
116
|
-
→ Automatically skips completed parts, resumes from checkpoint
|
|
117
|
-
```
|
|
76
|
+
### 05-compound: Compound Learnings
|
|
118
77
|
|
|
119
|
-
|
|
78
|
+
`pattern_extractor` scans existing artifacts, extracts and categorizes patterns. Turns "the pitfall we hit this time" into a YAML-tagged solution card in `docs/solutions/`.
|
|
120
79
|
|
|
121
|
-
|
|
122
|
-
You: /skill:07-worktree
|
|
80
|
+
Two-level storage: project-specific → inside the project; cross-project → `~/.pi/agent/docs/solutions/` globally searchable.
|
|
123
81
|
|
|
124
|
-
|
|
125
|
-
→ Execute 03-work within the worktree
|
|
126
|
-
→ Merge back to main branch when done
|
|
127
|
-
```
|
|
82
|
+
Next time `02-plan` or `04-review` runs, a grep-first search strategy automatically retrieves relevant past experience.
|
|
128
83
|
|
|
129
84
|
---
|
|
130
85
|
|
|
131
|
-
##
|
|
86
|
+
## Technical Architecture
|
|
132
87
|
|
|
133
|
-
|
|
134
|
-
|-------|---------|-------------|-----------------|
|
|
135
|
-
| `01-brainstorm` | `/skill:01-brainstorm` | Requirements discovery, three modes: CE / Startup Diagnostic / Builder | `brainstorm_dialog` |
|
|
136
|
-
| `02-plan` | `/skill:02-plan` | Create/update plans, optional CEO Review | `plan_diff` |
|
|
137
|
-
| `03-work` | `/skill:03-work` | Execute plans (parallel + checkpoint + recovery) | `session_checkpoint`, `task_splitter`, `parallel_subagent` |
|
|
138
|
-
| `04-review` | `/skill:04-review` | Code review + optional browser QA | `review_router` |
|
|
139
|
-
| `05-compound` | `/skill:05-compound` | Knowledge compounding | `pattern_extractor` |
|
|
140
|
-
| `06-next` | `/skill:06-next` | Recommend next step | `workflow_state`, `session_history` |
|
|
141
|
-
| `07-worktree` | `/skill:07-worktree` | Worktree isolated development | `worktree_manager` |
|
|
142
|
-
| `08-status` | `/skill:08-status` | Check project status | `workflow_state`, `session_history` |
|
|
143
|
-
| `09-help` | `/skill:09-help` | Usage guide | — |
|
|
88
|
+
### 9 Skills (workflow nodes)
|
|
144
89
|
|
|
145
|
-
|
|
90
|
+
| Skill | One-liner | Core Tool |
|
|
91
|
+
|-------|-----------|-----------|
|
|
92
|
+
| `01-brainstorm` | Deep requirements mining in three modes | `brainstorm_dialog` |
|
|
93
|
+
| `02-plan` | Break into units, TDD gates, incremental updates | `plan_diff` |
|
|
94
|
+
| `03-work` | Parallel execution, checkpoint resume, error recovery | `session_checkpoint`, `task_splitter`, `parallel_subagent` |
|
|
95
|
+
| `04-review` | Persona-routed review + live browser testing | `review_router` |
|
|
96
|
+
| `05-compound` | Pattern extraction → knowledge card compounding | `pattern_extractor` |
|
|
97
|
+
| `06-next` | Not sure what to do next? Ask this | `workflow_state`, `session_history` |
|
|
98
|
+
| `07-worktree` | Git worktree isolated development | `worktree_manager` |
|
|
99
|
+
| `08-status` | Scan artifacts, report progress | `workflow_state`, `session_history` |
|
|
100
|
+
| `09-help` | Usage guide | — |
|
|
146
101
|
|
|
147
|
-
|
|
102
|
+
### 13 Tools (underlying capabilities)
|
|
148
103
|
|
|
149
|
-
| Tool |
|
|
104
|
+
| Tool | What it does |
|
|
150
105
|
|------|-------------|
|
|
151
|
-
| `
|
|
152
|
-
| `
|
|
106
|
+
| `task_splitter` | Union-Find algorithm analyzes file dependencies, auto-groups parallel-safe units |
|
|
107
|
+
| `session_checkpoint` | JSON-persisted checkpoints with save/load/fail/retry operations |
|
|
108
|
+
| `plan_diff` | Incremental plans: compare detects diffs, patch applies changes |
|
|
109
|
+
| `parallel_subagent` | `Promise.allSettled`-style parallel subagent execution |
|
|
110
|
+
| `review_router` | Auto-assign reviewer personas from diff metadata |
|
|
111
|
+
| `pattern_extractor` | Extract and categorize patterns from artifacts |
|
|
112
|
+
| `brainstorm_dialog` | Multi-round dialog state machine (start → refine × N → summarize) |
|
|
113
|
+
| `session_history` | Cross-session execution history recording and querying |
|
|
114
|
+
| `workflow_state` | Scan docs/ and .context/ to summarize workflow state |
|
|
115
|
+
| `worktree_manager` | Full git worktree lifecycle management |
|
|
116
|
+
| `artifact_helper` | Artifact path resolution and directory creation |
|
|
117
|
+
| `ask_user_question` | Structured user prompts (choices / free input) |
|
|
153
118
|
| `subagent` | Serial subagent chain |
|
|
154
|
-
| `parallel_subagent` | Parallel subagent execution (`Promise.allSettled`) |
|
|
155
|
-
| `workflow_state` | Workflow artifact state scanning |
|
|
156
|
-
| `worktree_manager` | Git worktree lifecycle (create/detect/merge/cleanup) |
|
|
157
|
-
| `review_router` | Diff analysis + reviewer persona routing (base + conditional) |
|
|
158
|
-
| `session_checkpoint` | Plan execution checkpoint + error recovery (save/load/fail/retry) |
|
|
159
|
-
| `task_splitter` | File-level dependency analysis + parallel grouping (union-find) |
|
|
160
|
-
| `brainstorm_dialog` | Multi-round brainstorm dialog management (start/refine/summarize) |
|
|
161
|
-
| `plan_diff` | Plan incremental comparison + patching (compare/patch) |
|
|
162
|
-
| `session_history` | Cross-session execution history (record/query/latest) |
|
|
163
|
-
| `pattern_extractor` | Artifact pattern extraction + categorization (extract/categorize) |
|
|
164
119
|
|
|
165
120
|
---
|
|
166
121
|
|
|
167
|
-
##
|
|
122
|
+
## Code Scale
|
|
168
123
|
|
|
169
|
-
|
|
170
|
-
01-brainstorm → 02-plan → 03-work → 04-review → 05-compound
|
|
171
|
-
Three modes CEO Review Parallel Browser QA Knowledge
|
|
172
|
-
(CE/Startup/ (optional) Checkpoint (optional) compounding
|
|
173
|
-
Builder) Resume
|
|
174
|
-
```
|
|
124
|
+
~2000 lines of TypeScript implementing 13 tools, 22 Markdown reference files driving 9 skills' behavioral strategies, 95 tests covering all tool logic.
|
|
175
125
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
---
|
|
179
|
-
|
|
180
|
-
## Generated File Structure
|
|
181
|
-
|
|
182
|
-
```
|
|
183
|
-
your-project/
|
|
184
|
-
├── docs/
|
|
185
|
-
│ ├── brainstorms/ # Generated by 01-brainstorm
|
|
186
|
-
│ │ └── 2026-04-18-auth-requirements.md
|
|
187
|
-
│ ├── plans/ # Generated by 02-plan
|
|
188
|
-
│ │ └── 2026-04-18-auth-plan.md
|
|
189
|
-
│ └── solutions/ # Generated by 05-compound
|
|
190
|
-
│ └── auth/
|
|
191
|
-
│ └── oauth2-solution.md
|
|
192
|
-
└── .context/
|
|
193
|
-
└── compound-engineering/
|
|
194
|
-
├── checkpoints/ # Generated by session_checkpoint
|
|
195
|
-
│ └── docs-plans-auth-plan.json
|
|
196
|
-
├── dialogs/ # Generated by brainstorm_dialog
|
|
197
|
-
│ └── docs-brainstorms-auth.json
|
|
198
|
-
└── history/ # Generated by session_history
|
|
199
|
-
└── 1745000000-01-brainstorm.json
|
|
200
|
-
```
|
|
126
|
+
Not a heavy framework. Each tool has a single responsibility, each skill works independently, and together they form a complete workflow.
|
|
201
127
|
|
|
202
128
|
---
|
|
203
129
|
|
|
204
|
-
##
|
|
205
|
-
|
|
206
|
-
| Tip | Description |
|
|
207
|
-
|-----|-------------|
|
|
208
|
-
| **Start with 01-brainstorm** | New ideas → Startup/Builder Mode, features → CE Mode |
|
|
209
|
-
| **Use 08-status to check progress** | Not sure where you are? Use 08-status |
|
|
210
|
-
| **Use 06-next for direction** | Don't know the next step? Use 06-next |
|
|
211
|
-
| **Commit artifacts to git** | Both `docs/` and `.context/` should be committed |
|
|
212
|
-
| **Use 07-worktree for big features** | Isolated development without affecting main branch |
|
|
213
|
-
| **Don't panic on interruption** | Next 03-work will auto-resume from checkpoint |
|
|
214
|
-
| **Use CEO Review to stress-test plans** | After planning, pick CEO Review to challenge premises and find blind spots |
|
|
215
|
-
| **Use Browser QA for acceptance** | After code review, pick Browser QA for real-app testing |
|
|
216
|
-
|
|
217
|
-
---
|
|
130
|
+
## Quick Start
|
|
218
131
|
|
|
219
|
-
|
|
132
|
+
### New idea
|
|
220
133
|
|
|
221
|
-
|
|
134
|
+
```
|
|
135
|
+
You: I want to build a tool that helps indie devs find users
|
|
222
136
|
|
|
223
|
-
|
|
137
|
+
→ Auto-enters 01-brainstorm, YC-style interrogation
|
|
138
|
+
→ Generates docs/brainstorms/2026-04-18-find-users-requirements.md
|
|
139
|
+
→ Recommends next: 02-plan
|
|
224
140
|
|
|
225
|
-
|
|
141
|
+
You: continue
|
|
226
142
|
|
|
227
|
-
|
|
143
|
+
→ 02-plan breaks into units, optional CEO Review
|
|
144
|
+
→ Generates docs/plans/2026-04-18-find-users-plan.md
|
|
228
145
|
|
|
229
|
-
|
|
230
|
-
- Five-step workflow architecture (brainstorm → plan → work → review → compound)
|
|
231
|
-
- Knowledge compounding: every engineering cycle produces a reusable solution artifact
|
|
232
|
-
- Artifact-driven state management (tracking progress through persistent docs under `docs/`)
|
|
233
|
-
- Structured review methodology (reviewer persona routing, findings schema)
|
|
146
|
+
You: continue
|
|
234
147
|
|
|
235
|
-
|
|
148
|
+
→ 03-work parallel execution, checkpoint resume
|
|
149
|
+
→ 04-review code review + optional browser QA
|
|
150
|
+
→ 05-compound knowledge compounding
|
|
151
|
+
```
|
|
236
152
|
|
|
237
|
-
|
|
153
|
+
### Adding a feature
|
|
238
154
|
|
|
239
|
-
|
|
155
|
+
```
|
|
156
|
+
You: I want to add user authentication to the project
|
|
240
157
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
- Review discipline: technical evaluation over performative agreement, YAGNI checks
|
|
245
|
-
- User approval gates: no advancing to the next step without explicit confirmation
|
|
246
|
-
- Subagent-driven development patterns
|
|
158
|
+
→ 01-brainstorm CE mode, multi-round dialog: OAuth2? JWT? MFA?
|
|
159
|
+
→ Requirements doc → 02-plan → 03-work → 04-review → 05-compound
|
|
160
|
+
```
|
|
247
161
|
|
|
248
|
-
###
|
|
162
|
+
### Resume after interruption
|
|
249
163
|
|
|
250
|
-
|
|
164
|
+
```
|
|
165
|
+
You: /skill:03-work docs/plans/auth-plan.md
|
|
251
166
|
|
|
252
|
-
|
|
167
|
+
→ Auto-loads checkpoint, skips completed units, resumes from breakpoint
|
|
168
|
+
```
|
|
253
169
|
|
|
254
|
-
|
|
255
|
-
- Session history management and continuous learning mechanisms
|
|
256
|
-
- Parallel subagent orchestration patterns (`Promise.allSettled` style)
|
|
257
|
-
- Checkpoint-based resume and error recovery strategies
|
|
258
|
-
- Systematic tool design: each skill paired with a dedicated tool
|
|
170
|
+
### Requirements changed
|
|
259
171
|
|
|
260
|
-
|
|
172
|
+
```
|
|
173
|
+
You: Requirements changed, need to add SSO support
|
|
261
174
|
|
|
262
|
-
|
|
175
|
+
→ 02-plan uses plan_diff to detect changes, patches incrementally
|
|
176
|
+
```
|
|
263
177
|
|
|
264
|
-
|
|
178
|
+
### Check progress anytime
|
|
265
179
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
- **plan-ceo-review → 02-plan CEO Review**: CEO cognitive frameworks (Bezos irreversible decisions, Munger inversion, Jobs subtraction), dream-state mapping, mandatory alternative generation, error maps and failure mode registries
|
|
269
|
-
- **qa → 04-review Browser QA**: Browser-driven QA testing workflow (using Pi-native `agent-browser` instead of gstack's `$B`), health scoring system, diff-aware test scoping, fix loops and regression test generation
|
|
180
|
+
```
|
|
181
|
+
You: /skill:08-status
|
|
270
182
|
|
|
271
|
-
|
|
183
|
+
→ Scans all artifacts, shows progress + recommends next step
|
|
184
|
+
```
|
|
272
185
|
|
|
273
186
|
---
|
|
274
187
|
|
|
275
|
-
##
|
|
276
|
-
|
|
277
|
-
### 0.14.0
|
|
188
|
+
## Generated File Structure
|
|
278
189
|
|
|
279
|
-
|
|
190
|
+
```
|
|
191
|
+
your-project/
|
|
192
|
+
├── docs/
|
|
193
|
+
│ ├── brainstorms/ # Requirements (01-brainstorm)
|
|
194
|
+
│ ├── plans/ # Execution plans (02-plan)
|
|
195
|
+
│ └── solutions/ # Knowledge cards (05-compound)
|
|
196
|
+
└── .context/
|
|
197
|
+
└── compound-engineering/
|
|
198
|
+
├── checkpoints/ # Breakpoint files (session_checkpoint)
|
|
199
|
+
├── dialogs/ # Dialog state (brainstorm_dialog)
|
|
200
|
+
└── history/ # Execution history (session_history)
|
|
201
|
+
```
|
|
280
202
|
|
|
281
|
-
|
|
282
|
-
- `02-plan` and `04-review`: grep-first search strategy (project-level + global-level `~/.pi/agent/docs/solutions/`)
|
|
283
|
-
- `05-compound`: mandatory frontmatter, two-level storage (project-specific vs cross-project global)
|
|
284
|
-
- Added `solution-search-strategy.md` reference
|
|
285
|
-
- 95 tests passing
|
|
203
|
+
**Recommendation: commit everything to git** — these files are the project's traceable memory.
|
|
286
204
|
|
|
287
|
-
|
|
205
|
+
---
|
|
288
206
|
|
|
289
|
-
|
|
290
|
-
- `01-brainstorm`: design checklist, stop conditions, user approval gate
|
|
291
|
-
- `02-plan`: strict TDD gates (RED→GREEN→REFACTOR), TDD violation rejection
|
|
292
|
-
- `03-work`: TDD execution discipline, structured completion report
|
|
293
|
-
- `04-review`: review discipline (technical evaluation), YAGNI check
|
|
207
|
+
## Design Philosophy
|
|
294
208
|
|
|
295
|
-
|
|
209
|
+
**80% planning and review, 20% execution.**
|
|
296
210
|
|
|
297
|
-
|
|
211
|
+
The goal isn't making AI write code faster — it's making AI think before writing, review after writing, and compound learnings after reviewing. Speed comes from fewer rewrites, not from skipping steps.
|
|
298
212
|
|
|
299
|
-
|
|
213
|
+
**The best ideas from four projects, reimplemented natively for Pi:**
|
|
300
214
|
|
|
301
|
-
-
|
|
215
|
+
- **Compound Engineering** (Every Inc.) → The five-step loop + knowledge compounding backbone
|
|
216
|
+
- **Superpowers** (Jesse Vincent) → Strict TDD gates + design checklists + review discipline
|
|
217
|
+
- **Everything Claude Code** (140K+ stars) → Parallel subagent orchestration + checkpoint resume + continuous learning
|
|
218
|
+
- **gstack** (Garry Tan / YC) → YC-style forcing questions + CEO Review cognitive frameworks + browser QA
|
|
302
219
|
|
|
303
|
-
|
|
220
|
+
Not a fork. Not a wrapper. The methodologies are extracted and rebuilt with Pi's native tool + skill system.
|
|
304
221
|
|
|
305
|
-
|
|
222
|
+
---
|
|
306
223
|
|
|
307
|
-
|
|
224
|
+
## Best Practices
|
|
308
225
|
|
|
309
|
-
|
|
226
|
+
| Tip | Why |
|
|
227
|
+
|-----|-----|
|
|
228
|
+
| Start with 01-brainstorm | Whatever the scenario, thinking first never hurts |
|
|
229
|
+
| Use 07-worktree for big features | Isolated dev, no impact on main branch |
|
|
230
|
+
| Use CEO Review on plans | Like having a demanding CTO review for free |
|
|
231
|
+
| Use browser QA for acceptance | Code review can't catch layout breaks and blank screens |
|
|
232
|
+
| Don't panic on interruption | Next 03-work auto-resumes from checkpoint |
|
|
233
|
+
| Use 08-status when lost | One glance shows where you are |
|
|
310
234
|
|
|
311
|
-
|
|
235
|
+
---
|
|
312
236
|
|
|
313
|
-
|
|
237
|
+
## Changelog
|
|
314
238
|
|
|
315
|
-
### 0.
|
|
239
|
+
### 0.14.0 — Structured solution retrieval
|
|
240
|
+
- YAML frontmatter tagging + grep-first two-level search
|
|
241
|
+
- 95 tests passing
|
|
316
242
|
|
|
317
|
-
|
|
243
|
+
### 0.13.0 — Superpowers engineering discipline
|
|
244
|
+
- Strict TDD gates, design checklists, YAGNI checks
|
|
318
245
|
|
|
319
|
-
### 0.
|
|
246
|
+
### 0.12.0 — Error recovery
|
|
247
|
+
- session_checkpoint fail/retry operations
|
|
320
248
|
|
|
321
|
-
|
|
249
|
+
### 0.11.0 — Pattern extraction
|
|
250
|
+
- New pattern_extractor tool
|
|
322
251
|
|
|
323
|
-
### 0.
|
|
252
|
+
### 0.10.0 — Continuous learning
|
|
253
|
+
- New session_history tool
|
|
324
254
|
|
|
325
|
-
|
|
255
|
+
### 0.9.0 — Incremental planning
|
|
256
|
+
- New plan_diff tool
|
|
326
257
|
|
|
327
|
-
### 0.
|
|
258
|
+
### 0.8.0 — Multi-round dialog
|
|
259
|
+
- New brainstorm_dialog tool
|
|
328
260
|
|
|
329
|
-
|
|
261
|
+
### 0.7.0 — Parallel grouping
|
|
262
|
+
- Union-Find based task_splitter
|
|
330
263
|
|
|
331
|
-
### 0.
|
|
264
|
+
### 0.6.0 — Checkpoint resume
|
|
265
|
+
- New session_checkpoint tool
|
|
332
266
|
|
|
333
|
-
|
|
267
|
+
### 0.5.0 — Parallel execution
|
|
268
|
+
- New parallel_subagent tool
|
|
334
269
|
|
|
335
|
-
### 0.
|
|
270
|
+
### 0.4.0 — Smart review
|
|
271
|
+
- New review_router tool
|
|
336
272
|
|
|
337
|
-
|
|
273
|
+
### 0.3.0 — Isolated development
|
|
274
|
+
- New worktree_manager + 07-worktree
|
|
338
275
|
|
|
339
|
-
### 0.
|
|
276
|
+
### 0.2.0 — State awareness
|
|
277
|
+
- New workflow_state + 06-next
|
|
340
278
|
|
|
341
|
-
|
|
279
|
+
### 0.1.0 — Initial release
|
|
280
|
+
- 7 skills, 3 tools
|
|
342
281
|
|
|
343
282
|
---
|
|
344
283
|
|
|
@@ -353,20 +292,3 @@ gstack is Y Combinator CEO Garry Tan's personal AI toolchain, representing the "
|
|
|
353
292
|
bun test
|
|
354
293
|
npm publish --dry-run
|
|
355
294
|
```
|
|
356
|
-
|
|
357
|
-
## CI/CD
|
|
358
|
-
|
|
359
|
-
- **Test**: runs `bun test` on every push/PR to main
|
|
360
|
-
- **Publish**: runs `bun test` + `npm publish` on version tags (`v*`)
|
|
361
|
-
|
|
362
|
-
### Release process
|
|
363
|
-
|
|
364
|
-
1. Bump version in `package.json`
|
|
365
|
-
2. Update README Changelog
|
|
366
|
-
3. `git commit -m "chore: bump to x.y.z"`
|
|
367
|
-
4. `git tag vx.y.z`
|
|
368
|
-
5. `git push origin main --tags`
|
|
369
|
-
|
|
370
|
-
### Required secrets
|
|
371
|
-
|
|
372
|
-
Set `NPM_TOKEN` in GitHub → Settings → Secrets → Actions.
|
|
@@ -13,6 +13,7 @@ import { createBrainstormDialogTool } from "./tools/brainstorm-dialog"
|
|
|
13
13
|
import { createPlanDiffTool } from "./tools/plan-diff"
|
|
14
14
|
import { createSessionHistoryTool } from "./tools/session-history"
|
|
15
15
|
import { createPatternExtractorTool } from "./tools/pattern-extractor"
|
|
16
|
+
import { filterBashOutput } from "./tools/bash-output-filter"
|
|
16
17
|
|
|
17
18
|
const artifactHelperParams = Type.Object({
|
|
18
19
|
repoRoot: Type.String({ description: "Repository root where workflow artifacts should be created" }),
|
|
@@ -504,8 +505,47 @@ export default function ceCoreExtension(pi: ExtensionAPI) {
|
|
|
504
505
|
}
|
|
505
506
|
},
|
|
506
507
|
})
|
|
508
|
+
|
|
509
|
+
// Bash output smart filter — reduces context waste from verbose command output
|
|
510
|
+
pi.on("tool_result", async (event, _ctx) => {
|
|
511
|
+
if (event.toolName !== "bash") return undefined
|
|
512
|
+
|
|
513
|
+
// Extract command from input
|
|
514
|
+
const command = (event.input as any)?.command ?? ""
|
|
515
|
+
if (!command) return undefined
|
|
516
|
+
|
|
517
|
+
// Extract text content from tool result
|
|
518
|
+
const textBlocks = (event.content as Array<any>)?.filter((b: any) => b.type === "text") ?? []
|
|
519
|
+
if (textBlocks.length === 0) return undefined
|
|
520
|
+
|
|
521
|
+
const output = textBlocks.map((b: any) => b.text).join("")
|
|
522
|
+
const fullOutputPath = (event.details as any)?.fullOutputPath
|
|
523
|
+
|
|
524
|
+
const result = filterBashOutput({
|
|
525
|
+
command,
|
|
526
|
+
output,
|
|
527
|
+
isError: event.isError ?? false,
|
|
528
|
+
fullOutputPath,
|
|
529
|
+
})
|
|
530
|
+
|
|
531
|
+
if (!result.filtered) return undefined
|
|
532
|
+
|
|
533
|
+
// Replace content with filtered version
|
|
534
|
+
return {
|
|
535
|
+
content: [{ type: "text", text: result.output }],
|
|
536
|
+
details: {
|
|
537
|
+
...event.details,
|
|
538
|
+
bashFilter: {
|
|
539
|
+
strategy: result.strategy,
|
|
540
|
+
originalBytes: result.originalBytes,
|
|
541
|
+
filteredBytes: result.filteredBytes,
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
}
|
|
545
|
+
})
|
|
507
546
|
}
|
|
508
547
|
|
|
548
|
+
|
|
509
549
|
export { createArtifactHelperTool } from "./tools/artifact-helper"
|
|
510
550
|
export { createAskUserQuestionTool } from "./tools/ask-user-question"
|
|
511
551
|
export { createSubagentTool } from "./tools/subagent"
|
|
@@ -526,3 +566,4 @@ export {
|
|
|
526
566
|
getRunArtifactPath,
|
|
527
567
|
} from "./utils/artifact-paths"
|
|
528
568
|
export { normalizeSlug } from "./utils/name-utils"
|
|
569
|
+
export { filterBashOutput } from "./tools/bash-output-filter"
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import { Type } from "@sinclair/typebox"
|
|
2
|
+
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Types
|
|
5
|
+
// ============================================================================
|
|
6
|
+
|
|
7
|
+
export interface BashOutputFilterInput {
|
|
8
|
+
/** The bash command that was run */
|
|
9
|
+
command: string
|
|
10
|
+
/** The raw output text */
|
|
11
|
+
output: string
|
|
12
|
+
/** Whether the command exited with an error */
|
|
13
|
+
isError: boolean
|
|
14
|
+
/** Original full output path (if truncated by pi) */
|
|
15
|
+
fullOutputPath?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface BashOutputFilterResult {
|
|
19
|
+
/** Filtered output text */
|
|
20
|
+
output: string
|
|
21
|
+
/** Whether filtering was applied */
|
|
22
|
+
filtered: boolean
|
|
23
|
+
/** Original size in bytes */
|
|
24
|
+
originalBytes: number
|
|
25
|
+
/** Filtered size in bytes */
|
|
26
|
+
filteredBytes: number
|
|
27
|
+
/** The filter strategy that was applied */
|
|
28
|
+
strategy: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// Command Classification
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
type CommandCategory = "install" | "test" | "build" | "search" | "git-diff" | "list" | "http" | "unknown"
|
|
36
|
+
|
|
37
|
+
interface CommandPattern {
|
|
38
|
+
pattern: RegExp
|
|
39
|
+
category: CommandCategory
|
|
40
|
+
/** Whether this is a "means" command (output is a side-effect) */
|
|
41
|
+
isMeans: boolean
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const COMMAND_PATTERNS: CommandPattern[] = [
|
|
45
|
+
// Install commands — output is a side-effect
|
|
46
|
+
{ pattern: /\b(npm|yarn|pnpm|bun)\s+(install|i|add|ci|update|upgrade)\b/, category: "install", isMeans: true },
|
|
47
|
+
{ pattern: /\b(pip|pip3|poetry|conda)\s+(install|add)\b/, category: "install", isMeans: true },
|
|
48
|
+
{ pattern: /\b(cargo)\s+(install|add|update)\b/, category: "install", isMeans: true },
|
|
49
|
+
{ pattern: /\b(bundle|gem)\s+(install)\b/, category: "install", isMeans: true },
|
|
50
|
+
{ pattern: /\b(go)\s+(get|install|mod\s+(tidy|download))\b/, category: "install", isMeans: true },
|
|
51
|
+
// Test commands — output is mostly noise (pass lines), failures are signal
|
|
52
|
+
{ pattern: /\b(npm|yarn|pnpm|bun)\s+(test|t|run\s+test)/, category: "test", isMeans: true },
|
|
53
|
+
{ pattern: /\b(vitest|jest|mocha|pytest|cargo\s+test|go\s+test|ruby|-Ilib)\b.*\b(test|spec)\b/, category: "test", isMeans: true },
|
|
54
|
+
{ pattern: /\b(jest|vitest|mocha|pytest|npx\s+(jest|vitest|mocha))\b/, category: "test", isMeans: true },
|
|
55
|
+
{ pattern: /\b(cargo)\s+test\b/, category: "test", isMeans: true },
|
|
56
|
+
{ pattern: /\b(go)\s+test\b/, category: "test", isMeans: true },
|
|
57
|
+
// Build/compile commands — warnings/errors are signal
|
|
58
|
+
{ pattern: /\b(tsc|typescript|tscl)\b/, category: "build", isMeans: true },
|
|
59
|
+
{ pattern: /\b(npm|yarn|pnpm|bun)\s+(run\s+)?(build|compile)\b/, category: "build", isMeans: true },
|
|
60
|
+
{ pattern: /\b(cargo)\s+(build|check|clippy)\b/, category: "build", isMeans: true },
|
|
61
|
+
{ pattern: /\b(make|cmake|gcc|g\+\+|clang)\b/, category: "build", isMeans: true },
|
|
62
|
+
{ pattern: /\b(dotnet)\s+(build|publish)\b/, category: "build", isMeans: true },
|
|
63
|
+
{ pattern: /\b(gradle|mvn|mvnw)\b/, category: "build", isMeans: true },
|
|
64
|
+
// Search commands — output IS the purpose, don't filter
|
|
65
|
+
{ pattern: /\b(grep|rg|ag|ack|git\s+grep)\b/, category: "search", isMeans: false },
|
|
66
|
+
{ pattern: /\b(rg|ripgrep)\b/, category: "search", isMeans: false },
|
|
67
|
+
// Git diff/log — output IS the purpose
|
|
68
|
+
{ pattern: /\bgit\s+(diff|log|show|range-diff)\b/, category: "git-diff", isMeans: false },
|
|
69
|
+
// File listing — moderate filtering ok
|
|
70
|
+
{ pattern: /\b(find|fd|ls\s+-R|tree|du|ncdu)\b/, category: "list", isMeans: false },
|
|
71
|
+
// HTTP requests — can be verbose
|
|
72
|
+
{ pattern: /\b(curl|wget|httpie|http\s+)\b/, category: "http", isMeans: false },
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
function classifyCommand(command: string): CommandPattern | null {
|
|
76
|
+
// Normalize command: remove leading whitespace, handle pipes
|
|
77
|
+
const cmd = command.trim()
|
|
78
|
+
|
|
79
|
+
// Check primary command (before any pipe)
|
|
80
|
+
const primaryCmd = cmd.split("|")[0].trim()
|
|
81
|
+
|
|
82
|
+
for (const pattern of COMMAND_PATTERNS) {
|
|
83
|
+
if (pattern.pattern.test(primaryCmd)) {
|
|
84
|
+
return pattern
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return null
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ============================================================================
|
|
92
|
+
// Output Filters
|
|
93
|
+
// ============================================================================
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Filter for install command output.
|
|
97
|
+
* Keep: error lines, warning lines, summary lines (added/removed/changed counts).
|
|
98
|
+
* Remove: progress bars, download lines, individual package install lines.
|
|
99
|
+
*/
|
|
100
|
+
function filterInstallOutput(output: string): string {
|
|
101
|
+
const lines = output.split("\n")
|
|
102
|
+
const kept: string[] = []
|
|
103
|
+
let removedCount = 0
|
|
104
|
+
|
|
105
|
+
for (const line of lines) {
|
|
106
|
+
// Always keep: empty lines (structure), errors, warnings, summary
|
|
107
|
+
if (
|
|
108
|
+
line.trim() === "" ||
|
|
109
|
+
/(error|ERR!|fatal|FAILED|failed|abort|timeout)/i.test(line) ||
|
|
110
|
+
/\b(warn|WARN|warning|deprecat)\b/i.test(line) ||
|
|
111
|
+
/\b(added|removed|changed|updated|audited)\s+\d+/i.test(line) ||
|
|
112
|
+
/\bpackages?\b.*\b(look|found|installed|removed)\b/i.test(line) ||
|
|
113
|
+
/\bvulnerabilities\b/i.test(line) ||
|
|
114
|
+
/^\s*$/.test(line) ||
|
|
115
|
+
line.startsWith("[") ||
|
|
116
|
+
/^\s*(\d+ packages?|up to date|already|nothing)/i.test(line)
|
|
117
|
+
) {
|
|
118
|
+
kept.push(line)
|
|
119
|
+
} else {
|
|
120
|
+
removedCount++
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Deduplicate consecutive empty lines
|
|
125
|
+
return deduplicateEmptyLines(kept.join("\n"))
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Filter for test command output.
|
|
130
|
+
* Keep: FAIL, ERROR, summary, test suite headers.
|
|
131
|
+
* Remove: PASS lines (✓, ✔, PASS, ✓), progress dots.
|
|
132
|
+
*/
|
|
133
|
+
function filterTestOutput(output: string): string {
|
|
134
|
+
const lines = output.split("\n")
|
|
135
|
+
const kept: string[] = []
|
|
136
|
+
let removedCount = 0
|
|
137
|
+
|
|
138
|
+
for (const line of lines) {
|
|
139
|
+
// Always keep: errors, failures, summary, suite names
|
|
140
|
+
if (
|
|
141
|
+
line.trim() === "" ||
|
|
142
|
+
/\b(FAIL|FAILING|FAILED|ERROR|✕|✗|×|✘|broken)\b/i.test(line) ||
|
|
143
|
+
/\b(fail|failure|error|timeout|crash)\b.*\d/i.test(line) ||
|
|
144
|
+
/\b(tests?|suites?|files?|passed|failed|skipped|pending)\s*[:=]?\s*\d/i.test(line) ||
|
|
145
|
+
/\b(RUN|RUNS)\s/i.test(line) || // vitest running indicator
|
|
146
|
+
/^\s*(FAIL|PASS|ERROR|SKIP|TODO)\s+(?:\s|\[)/i.test(line) ||
|
|
147
|
+
/^[\s│┌┐└┘├┤┬┴┼─]+/.test(line) || // Box drawing (test summaries)
|
|
148
|
+
/\b(synopsis|assert|expect|received|expected)\b/i.test(line) ||
|
|
149
|
+
/\s+(→|at)\s+.*\.\w+\s*$/i.test(line) || // Stack traces
|
|
150
|
+
/\d+\.\d+\s*(s|ms)\s*$/.test(line.trim()) || // Timing lines
|
|
151
|
+
line.includes("Test Suites:") ||
|
|
152
|
+
line.includes("Tests:") ||
|
|
153
|
+
line.includes("Snapshots:") ||
|
|
154
|
+
line.includes("Time:")
|
|
155
|
+
) {
|
|
156
|
+
kept.push(line)
|
|
157
|
+
} else if (
|
|
158
|
+
// Specifically skip PASS lines
|
|
159
|
+
/^\s*✓|✔|PASS|✅|·/.test(line) ||
|
|
160
|
+
/^\s*√/.test(line) ||
|
|
161
|
+
/^\s*ok\s+\d+/.test(line) ||
|
|
162
|
+
/^\s*\.\s*$/.test(line.trim()) // Progress dots
|
|
163
|
+
) {
|
|
164
|
+
removedCount++
|
|
165
|
+
} else {
|
|
166
|
+
// Keep lines that don't match pass patterns (context lines)
|
|
167
|
+
kept.push(line)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return deduplicateEmptyLines(kept.join("\n"))
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Filter for build/compile output.
|
|
176
|
+
* Keep: error lines, warning lines (deduplicated), summary.
|
|
177
|
+
* Remove: successful compilation lines, progress indicators.
|
|
178
|
+
*/
|
|
179
|
+
function filterBuildOutput(output: string): string {
|
|
180
|
+
const lines = output.split("\n")
|
|
181
|
+
const kept: string[] = []
|
|
182
|
+
const seenWarnings = new Set<string>()
|
|
183
|
+
let removedCount = 0
|
|
184
|
+
|
|
185
|
+
for (const line of lines) {
|
|
186
|
+
if (line.trim() === "") {
|
|
187
|
+
kept.push(line)
|
|
188
|
+
continue
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Always keep errors
|
|
192
|
+
if (/\b(error|Error:|fatal|FAILED|failed)\b/i.test(line)) {
|
|
193
|
+
kept.push(line)
|
|
194
|
+
continue
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Deduplicate warnings (keep first occurrence)
|
|
198
|
+
if (/\b(warning|warn)\b/i.test(line)) {
|
|
199
|
+
const normalized = line.replace(/:\d+:\d+/g, ":N:N").trim()
|
|
200
|
+
if (!seenWarnings.has(normalized)) {
|
|
201
|
+
seenWarnings.add(normalized)
|
|
202
|
+
kept.push(line)
|
|
203
|
+
} else {
|
|
204
|
+
kept.push(` ... (${countByPattern(lines, normalized)} similar warnings omitted)`)
|
|
205
|
+
}
|
|
206
|
+
continue
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Keep summary lines
|
|
210
|
+
if (
|
|
211
|
+
/\b(compiled|built|generated|error|warning)\s*\d/i.test(line) ||
|
|
212
|
+
/\b(Finished|Compiling|Building)\b/i.test(line) ||
|
|
213
|
+
/error\(s\)|warning\(s\)/i.test(line)
|
|
214
|
+
) {
|
|
215
|
+
kept.push(line)
|
|
216
|
+
continue
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Skip progress/success lines
|
|
220
|
+
if (
|
|
221
|
+
/^\s*(Compiling|Building|Generating)\s/i.test(line) ||
|
|
222
|
+
/^\s*\[.*\]\s/.test(line) // [1/10] style progress
|
|
223
|
+
) {
|
|
224
|
+
removedCount++
|
|
225
|
+
continue
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Keep everything else (might be important context)
|
|
229
|
+
kept.push(line)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return deduplicateEmptyLines(kept.join("\n"))
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* General filter for any large output.
|
|
237
|
+
* Applies safe, universal compression:
|
|
238
|
+
* - Collapse consecutive empty lines
|
|
239
|
+
* - Remove ANSI escape sequences
|
|
240
|
+
* - Trim trailing whitespace
|
|
241
|
+
*/
|
|
242
|
+
function filterGenericOutput(output: string): string {
|
|
243
|
+
// Remove ANSI escape sequences
|
|
244
|
+
const cleaned = output.replace(/\x1b\[[0-9;]*[a-zA-Z]/g, "")
|
|
245
|
+
// Deduplicate empty lines
|
|
246
|
+
return deduplicateEmptyLines(cleaned)
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ============================================================================
|
|
250
|
+
// Helpers
|
|
251
|
+
// ============================================================================
|
|
252
|
+
|
|
253
|
+
function deduplicateEmptyLines(text: string): string {
|
|
254
|
+
return text.replace(/\n{3,}/g, "\n\n")
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function countByPattern(lines: string[], normalizedPattern: string): number {
|
|
258
|
+
let count = 0
|
|
259
|
+
for (const line of lines) {
|
|
260
|
+
if (line.replace(/:\d+:\d+/g, ":N:N").trim() === normalizedPattern) {
|
|
261
|
+
count++
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return count
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function formatBytes(bytes: number): string {
|
|
268
|
+
if (bytes < 1024) return `${bytes}B`
|
|
269
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)}KB`
|
|
270
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)}MB`
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ============================================================================
|
|
274
|
+
// Main Filter Function
|
|
275
|
+
// ============================================================================
|
|
276
|
+
|
|
277
|
+
/** Minimum output size (bytes) to trigger any filtering */
|
|
278
|
+
const MIN_FILTER_THRESHOLD = 2048 // 2KB
|
|
279
|
+
|
|
280
|
+
/** Maximum filtered output size (bytes) — aggressive cutoff for very large outputs */
|
|
281
|
+
const MAX_FILTERED_SIZE = 30720 // 30KB
|
|
282
|
+
|
|
283
|
+
export function filterBashOutput(input: BashOutputFilterInput): BashOutputFilterResult {
|
|
284
|
+
const { command, output, isError, fullOutputPath } = input
|
|
285
|
+
const originalBytes = Buffer.byteLength(output, "utf-8")
|
|
286
|
+
|
|
287
|
+
// Don't filter small outputs
|
|
288
|
+
if (originalBytes < MIN_FILTER_THRESHOLD) {
|
|
289
|
+
return {
|
|
290
|
+
output,
|
|
291
|
+
filtered: false,
|
|
292
|
+
originalBytes,
|
|
293
|
+
filteredBytes: originalBytes,
|
|
294
|
+
strategy: "none (below threshold)",
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Don't filter error outputs — they need to be complete
|
|
299
|
+
if (isError) {
|
|
300
|
+
return {
|
|
301
|
+
output,
|
|
302
|
+
filtered: false,
|
|
303
|
+
originalBytes,
|
|
304
|
+
filteredBytes: originalBytes,
|
|
305
|
+
strategy: "none (error output)",
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const matched = classifyCommand(command)
|
|
310
|
+
|
|
311
|
+
// If no pattern matched, apply generic filtering for large outputs
|
|
312
|
+
if (!matched) {
|
|
313
|
+
if (originalBytes > MAX_FILTERED_SIZE) {
|
|
314
|
+
const filtered = filterGenericOutput(output)
|
|
315
|
+
const filteredBytes = Buffer.byteLength(filtered, "utf-8")
|
|
316
|
+
if (filteredBytes < originalBytes * 0.9) {
|
|
317
|
+
return {
|
|
318
|
+
output: appendFilterNotice(filtered, originalBytes, filteredBytes, "generic", fullOutputPath),
|
|
319
|
+
filtered: true,
|
|
320
|
+
originalBytes,
|
|
321
|
+
filteredBytes,
|
|
322
|
+
strategy: "generic (large unknown command)",
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return {
|
|
327
|
+
output,
|
|
328
|
+
filtered: false,
|
|
329
|
+
originalBytes,
|
|
330
|
+
filteredBytes: originalBytes,
|
|
331
|
+
strategy: "none (unknown command, below aggressive threshold)",
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// Don't filter "purpose" commands (search, git diff) unless extremely large
|
|
336
|
+
if (!matched.isMeans && originalBytes < MAX_FILTERED_SIZE) {
|
|
337
|
+
return {
|
|
338
|
+
output,
|
|
339
|
+
filtered: false,
|
|
340
|
+
originalBytes,
|
|
341
|
+
filteredBytes: originalBytes,
|
|
342
|
+
strategy: `none (${matched.category}: purpose command, within limit)`,
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
// Apply category-specific filter
|
|
347
|
+
let filtered: string
|
|
348
|
+
let strategyName: string
|
|
349
|
+
|
|
350
|
+
switch (matched.category) {
|
|
351
|
+
case "install":
|
|
352
|
+
filtered = filterInstallOutput(output)
|
|
353
|
+
strategyName = "install (errors + summary only)"
|
|
354
|
+
break
|
|
355
|
+
case "test":
|
|
356
|
+
filtered = filterTestOutput(output)
|
|
357
|
+
strategyName = "test (failures + summary only)"
|
|
358
|
+
break
|
|
359
|
+
case "build":
|
|
360
|
+
filtered = filterBuildOutput(output)
|
|
361
|
+
strategyName = "build (errors + deduplicated warnings)"
|
|
362
|
+
break
|
|
363
|
+
default:
|
|
364
|
+
// Purpose commands that are very large get generic filtering
|
|
365
|
+
filtered = filterGenericOutput(output)
|
|
366
|
+
strategyName = `generic (${matched.category}: large output)`
|
|
367
|
+
break
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
const filteredBytes = Buffer.byteLength(filtered, "utf-8")
|
|
371
|
+
|
|
372
|
+
// If filtering didn't help much (< 20% reduction), keep original
|
|
373
|
+
if (filteredBytes > originalBytes * 0.8) {
|
|
374
|
+
return {
|
|
375
|
+
output,
|
|
376
|
+
filtered: false,
|
|
377
|
+
originalBytes,
|
|
378
|
+
filteredBytes,
|
|
379
|
+
strategy: `none (${matched.category}: filtering insufficient, <20% reduction)`,
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return {
|
|
384
|
+
output: appendFilterNotice(filtered, originalBytes, filteredBytes, strategyName, fullOutputPath),
|
|
385
|
+
filtered: true,
|
|
386
|
+
originalBytes,
|
|
387
|
+
filteredBytes,
|
|
388
|
+
strategy: strategyName,
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function appendFilterNotice(
|
|
393
|
+
output: string,
|
|
394
|
+
originalBytes: number,
|
|
395
|
+
filteredBytes: number,
|
|
396
|
+
strategy: string,
|
|
397
|
+
fullOutputPath?: string,
|
|
398
|
+
): string {
|
|
399
|
+
const saved = formatBytes(originalBytes - filteredBytes)
|
|
400
|
+
const notice = `\n\n[Output filtered: ${formatBytes(originalBytes)} → ${formatBytes(filteredBytes)} (${saved} saved, strategy: ${strategy})${fullOutputPath ? `. Full output: ${fullOutputPath}` : ""}]`
|
|
401
|
+
return output + notice
|
|
402
|
+
}
|