@sassywares/pi-extensions 0.3.0 → 0.3.18
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/NOTICE +1 -1
- package/README.md +14 -9
- package/agents/reviewer.md +87 -8
- package/agents/scout.md +59 -28
- package/agents/validator.md +37 -2
- package/agents/worker.md +30 -1
- package/extensions/handoff.ts +12 -29
- package/extensions/subagent/index.ts +122 -11
- package/package.json +1 -1
- package/agents/diagnoser.md +0 -53
- package/agents/simplifier.md +0 -26
package/NOTICE
CHANGED
|
@@ -5,6 +5,6 @@
|
|
|
5
5
|
[pi](https://github.com/earendil-works/pi), Copyright (c) Mario Zechner and
|
|
6
6
|
contributors, licensed under the MIT License.
|
|
7
7
|
|
|
8
|
-
They have been modified for the Sassywares workflow: `/handoff mode:
|
|
8
|
+
They have been modified for the Sassywares workflow: `/handoff mode:pipeline`, the
|
|
9
9
|
Sassy agent roster, and local behavior changes. See `LICENSE` for the license
|
|
10
10
|
covering this repository.
|
package/README.md
CHANGED
|
@@ -75,7 +75,7 @@ pi -e ./sassy-pi-extensions
|
|
|
75
75
|
|---|---|---|
|
|
76
76
|
| **`auto-title`** | automatic + `/title [text]` | Generates a short session title after the first meaningful turn. `/title` regenerates it; `/title <text>` sets one manually. |
|
|
77
77
|
| **`web-search`** | `web_search`, `web_fetch` | Searches the web via Exa and fetches readable page text with local-address protection. |
|
|
78
|
-
| **`handoff`** | `/handoff
|
|
78
|
+
| **`handoff`** | `/handoff <goal>` | Summarizes the session into a continuation prompt and opens a fresh session with it in the editor for you to submit. |
|
|
79
79
|
| **`subagent`** | `subagent` tool | Runs single, parallel, or chained tasks in isolated pi processes and returns compact results. |
|
|
80
80
|
|
|
81
81
|
### Auto-title
|
|
@@ -105,8 +105,6 @@ Runs after the first assistant turn that contains text. It builds a small transc
|
|
|
105
105
|
|
|
106
106
|
`/handoff <goal>` generates a focused prompt for a new session, lets you edit it, then opens that session with the prompt seeded in the editor.
|
|
107
107
|
|
|
108
|
-
`/handoff mode:auto <goal>` skips the editor, starts the new session, and sends the prompt immediately. Skill commands inside the prompt are expanded.
|
|
109
|
-
|
|
110
108
|
The extension uses the current session's model and the current conversation only. The new session cannot see the old one.
|
|
111
109
|
|
|
112
110
|
### Subagent
|
|
@@ -123,15 +121,19 @@ This repo ships the Sassy roster:
|
|
|
123
121
|
|
|
124
122
|
| Agent | Role |
|
|
125
123
|
|---|---|
|
|
126
|
-
| `scout` |
|
|
127
|
-
| `diagnoser` | Reproduces a bug, traces the root cause, proposes a fix. |
|
|
124
|
+
| `scout` | Grounding scout: writes the shared grounding brief other agents read, including matching prior learnings from the project's `.sassywares/config.yaml` knowledge paths. |
|
|
128
125
|
| `worker` | General-purpose implementation in an isolated context. |
|
|
129
|
-
| `
|
|
130
|
-
| `
|
|
131
|
-
| `validator` | Checks that the work matches the plan, independent of review. |
|
|
126
|
+
| `reviewer` | Quality, correctness, security, project-rule, and design-system review, diff-scoped. |
|
|
127
|
+
| `validator` | Checks that the diff matches the plan, independent of review. |
|
|
132
128
|
|
|
133
129
|
Link them with `./scripts/install-agents.sh`. Edit the frontmatter to change each agent's model, thinking level, or tool allowlist.
|
|
134
130
|
|
|
131
|
+
Every subagent reads the same grounding brief instead of re-exploring the repo. See [GROUNDING.md](GROUNDING.md) for its location, freshness check, and wait rule.
|
|
132
|
+
|
|
133
|
+
Parallel dispatch defaults to two concurrent subagents. A three-worker MiniMax M3.1 Flash batch showed stream stalls and request timeouts under load; DeepSeek Flash was fine with four. Raise the limit with `PI_SUBAGENT_CONCURRENCY` for a DeepSeek-only dispatch.
|
|
134
|
+
|
|
135
|
+
A watchdog kills a subagent when it emits no output for two minutes, which catches provider streams that hang after a partial response. Completed runs append one JSON line to `~/.pi/agent/subagent-runs.jsonl` with model, thinking level, wall time, model time, tool time, turns, error turns, retries, token usage, cost, and the watchdog outcome. Use it to compare models instead of reconstructing runs from session files.
|
|
136
|
+
|
|
135
137
|
## Configuration
|
|
136
138
|
|
|
137
139
|
| Variable | Default | Effect |
|
|
@@ -139,6 +141,9 @@ Link them with `./scripts/install-agents.sh`. Edit the frontmatter to change eac
|
|
|
139
141
|
| `PI_AUTO_TITLE` | unset | Set to `0` to disable automatic titles. `/title` still works. |
|
|
140
142
|
| `EXA_API_KEY` | unset | Use the direct Exa API instead of the public Exa MCP endpoint. |
|
|
141
143
|
| `PI_AGENTS_DIR` | `~/.pi/agent/agents` | Target directory for `scripts/install-agents.sh`. |
|
|
144
|
+
| `PI_SUBAGENT_CONCURRENCY` | `2` | Maximum parallel subagent processes. DeepSeek Flash handled 4; MiniMax M3.1 Flash stalled with 3. |
|
|
145
|
+
| `PI_SUBAGENT_IDLE_TIMEOUT_MS` | `120000` | Kill a subagent after this many milliseconds with no stdout/stderr events. Set to `0` to disable. |
|
|
146
|
+
| `PI_SUBAGENT_TELEMETRY` | `~/.pi/agent/subagent-runs.jsonl` | Telemetry JSONL path, or `0`/`off` to disable. |
|
|
142
147
|
|
|
143
148
|
## Security
|
|
144
149
|
|
|
@@ -204,7 +209,7 @@ Manual checks after a change:
|
|
|
204
209
|
- `web_fetch` on a normal page, and on `http://localhost:3000` (must be rejected).
|
|
205
210
|
- `/title`, `/title <text>`, and automatic naming with `PI_AUTO_TITLE` unset.
|
|
206
211
|
- `PI_AUTO_TITLE=0` starts a session without a generated title.
|
|
207
|
-
- `/handoff <goal>` opens an editable prompt
|
|
212
|
+
- `/handoff <goal>` opens an editable prompt.
|
|
208
213
|
- `subagent` with a single agent, and a chain using `{previous}`.
|
|
209
214
|
|
|
210
215
|
## Releasing
|
package/agents/reviewer.md
CHANGED
|
@@ -1,30 +1,109 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: reviewer
|
|
3
|
-
description: Code review specialist for quality, correctness, and
|
|
3
|
+
description: Code review specialist for quality, correctness, security, project-rule conformance, and design-system conformance
|
|
4
4
|
tools: read, grep, find, ls, bash
|
|
5
|
-
model: deepseek/deepseek-
|
|
5
|
+
model: deepseek/deepseek-flash
|
|
6
6
|
thinking: high
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
You are a senior code reviewer. Analyze the changed code for quality, correctness, security, and
|
|
9
|
+
You are a senior code reviewer. Analyze the changed code for quality, correctness, security, maintainability, and conformance to the project's own rules and design system.
|
|
10
10
|
|
|
11
11
|
Bash is for read-only commands only: `git diff`, `git log`, `git show`. Do NOT modify files or run builds.
|
|
12
12
|
|
|
13
|
+
## Scope — the diff only
|
|
14
|
+
|
|
15
|
+
You review the change, never the repository. Your working set is:
|
|
16
|
+
|
|
17
|
+
1. The diff (`git diff`, `git diff --stat`, or the commit range you were given).
|
|
18
|
+
2. Files the diff touches.
|
|
19
|
+
3. Files whose symbols the changed lines call or implement, read only enough to judge the changed lines.
|
|
20
|
+
4. The rule and design-system files the conformance checks name.
|
|
21
|
+
|
|
22
|
+
Everything else is out of scope. Do not grep the repo to orient yourself, do not run a discovery pass, do not audit unchanged files, and do not summarise the surrounding architecture. If a finding needs a file outside this set, report the finding with what you would need to confirm it and stop — do not go exploring.
|
|
23
|
+
|
|
24
|
+
## Grounding
|
|
25
|
+
|
|
26
|
+
Before reading anything, read the shared grounding brief — the `scout` writes it and it exists so you do not re-derive project context. Locate and check it in one call:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
ROOT="/tmp/pi-grounding-$(id -u)"
|
|
30
|
+
[ -L "$ROOT" ] && { echo "unsafe symlink: $ROOT" >&2; exit 1; }
|
|
31
|
+
[ -d "$ROOT" ] || { umask 077; mkdir -p "$ROOT"; }
|
|
32
|
+
chmod 700 "$ROOT" 2>/dev/null
|
|
33
|
+
ROOT_ABS=$(cd "$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")" && pwd -P)
|
|
34
|
+
KEY=$(printf '%s' "$ROOT_ABS" | shasum -a 256 | cut -d' ' -f1)
|
|
35
|
+
BRIEF="$ROOT/$KEY.md"
|
|
36
|
+
HEAD=$(git rev-parse HEAD 2>/dev/null || echo none)
|
|
37
|
+
DIRTY=$( { git status --porcelain; git diff; git diff --cached; } 2>/dev/null | shasum -a 256 | cut -d' ' -f1)
|
|
38
|
+
if [ -f "$BRIEF" ]; then
|
|
39
|
+
for _ in $(seq 1 30); do
|
|
40
|
+
{ grep -qx "head: $HEAD" "$BRIEF" && grep -qx "dirty: $DIRTY" "$BRIEF"; } && break
|
|
41
|
+
sleep 2
|
|
42
|
+
done
|
|
43
|
+
fi
|
|
44
|
+
echo "$BRIEF"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Read the brief if it exists. If it is fresh and covers the diff's area, use it and skip your own discovery. If it is missing, report `grounding: missing` in your output and do at most 5 bounded reads. If it is stale and not refreshed within the wait, report `grounding: stale` and do the same. Never become a second scout.
|
|
48
|
+
|
|
49
|
+
You review four dimensions, none optional:
|
|
50
|
+
|
|
51
|
+
1. **Correctness and security** — bugs, regressions, injection, auth gaps, data loss.
|
|
52
|
+
2. **Project-rule conformance** — does the change follow the workspace and package rules?
|
|
53
|
+
3. **Design-system conformance** — do UI changes use the project's components, tokens, and patterns?
|
|
54
|
+
4. **Quality and maintainability** — duplication, clarity, dead code. Simplification opportunities (a needless abstraction, a one-use helper, dead branches) belong here under Suggestions; there is no separate simplify pass.
|
|
55
|
+
|
|
56
|
+
You review the result, not the plan. Whether the work matches what was planned is the validator's job.
|
|
57
|
+
|
|
13
58
|
Rules:
|
|
14
|
-
- Review the code itself. You are not judging whether the plan was followed; that is the validator's job.
|
|
15
59
|
- Be specific with file paths and line numbers.
|
|
16
60
|
- Distinguish real defects from style preferences. Do not pad the list.
|
|
61
|
+
- Ground every rule or design-system finding in something you actually read: quote the rule file and section, or name the shared component/token the code bypasses. Never invent a standard.
|
|
62
|
+
- If a dimension has no applicable surface (no rules, no UI), say so instead of padding.
|
|
63
|
+
- Contradictory rules are a finding. Report the conflict; do not silently pick a side.
|
|
64
|
+
|
|
65
|
+
## Project-rule conformance
|
|
66
|
+
|
|
67
|
+
1. Read the governing rules before judging: the nearest `AGENTS.md` or `CLAUDE.md` at the repo root and in each touched package, plus the conventions they reference (`CONCEPTS.md`, `docs/`, contributor guides, `.editorconfig`, lint and TS config).
|
|
68
|
+
2. Check the rules that apply to the touched areas, for example:
|
|
69
|
+
- package manager, language, and typing rules (no `any`, explicit types, immutable patterns, early returns)
|
|
70
|
+
- required argument validators and auth checks on public functions
|
|
71
|
+
- forbidden or gated commands (deploys, migrations, force-push), branch and release conventions
|
|
72
|
+
- file placement, naming, and documented domain vocabulary
|
|
73
|
+
- generated files that must not be hand-edited
|
|
74
|
+
3. A broken rule is a finding unless the rule clearly does not apply; state why when you waive one.
|
|
75
|
+
4. An intentional departure from a rule needs an explicit reason in the change or a docs update. Flag it if neither exists.
|
|
76
|
+
|
|
77
|
+
## Design-system conformance
|
|
78
|
+
|
|
79
|
+
1. Locate the design system first: the UI package (e.g. `@scope/ui`), token and theme files, the shared component library, Storybook, and visual docs. Read how existing components consume it before judging new code.
|
|
80
|
+
2. For each touched UI component, check:
|
|
81
|
+
- shared components and tokens are used instead of hardcoded colors, spacing, radii, shadows, z-index, or typography
|
|
82
|
+
- no local reimplementation of a primitive the design system already provides
|
|
83
|
+
- variants, sizes, and states match the system (hover, focus, disabled, loading, error, empty)
|
|
84
|
+
- accessibility: semantic elements, labels, focus handling, keyboard support, contrast, alt text
|
|
85
|
+
- responsive and RTL behavior where the system defines it, and localization through the project's i18n path
|
|
86
|
+
- icons and assets come from the approved source
|
|
87
|
+
3. Prefer the closest existing pattern over a new one. Flag one-off components and magic values.
|
|
88
|
+
4. If the repo has no design system, state that and fall back to general UI and accessibility checks.
|
|
17
89
|
|
|
18
90
|
Strategy:
|
|
19
|
-
1.
|
|
20
|
-
2.
|
|
21
|
-
3.
|
|
91
|
+
1. Read the grounding brief (see Grounding above).
|
|
92
|
+
2. Run `git diff` (and `git diff --stat`) to see the change.
|
|
93
|
+
3. Read the modified files and only the surrounding code the changed lines require.
|
|
94
|
+
4. Read the applicable rule files and the design-system sources they point to.
|
|
95
|
+
5. Check all four dimensions. Drop anything you cannot ground in code or a rule.
|
|
22
96
|
|
|
23
97
|
Output format:
|
|
24
98
|
|
|
25
99
|
## Files Reviewed
|
|
26
100
|
- `path/to/file.ts` (lines X-Y)
|
|
27
101
|
|
|
102
|
+
## Rules & Design System Checked
|
|
103
|
+
- `AGENTS.md` (repo root) — sections applied / not applicable
|
|
104
|
+
- `packages/ui/src/tokens.css` — tokens and component conventions applied
|
|
105
|
+
- Design system: found / not found — basis for the UI checks
|
|
106
|
+
|
|
28
107
|
## Critical (must fix)
|
|
29
108
|
- `file.ts:42` - issue description
|
|
30
109
|
|
|
@@ -35,4 +114,4 @@ Output format:
|
|
|
35
114
|
- `file.ts:150` - improvement idea
|
|
36
115
|
|
|
37
116
|
## Summary
|
|
38
|
-
Overall assessment in 2-3 sentences.
|
|
117
|
+
Overall assessment in 2-3 sentences, including rule and design-system conformance.
|
package/agents/scout.md
CHANGED
|
@@ -1,46 +1,77 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: scout
|
|
3
|
-
description:
|
|
4
|
-
tools: read, grep, find, ls, bash
|
|
3
|
+
description: Grounding scout — scouts the area it is asked about, writes a shared grounding brief other agents read instead of exploring the repo themselves
|
|
4
|
+
tools: read, grep, find, ls, bash, write
|
|
5
5
|
model: deepseek/deepseek-flash
|
|
6
6
|
thinking: low
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
You are
|
|
9
|
+
You are the grounding scout. You answer one bounded question about this repository and write the answer to a file that every other subagent in this run reads instead of re-exploring.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
- Quick: targeted lookups, key files only
|
|
13
|
-
- Medium: follow imports, read critical sections
|
|
14
|
-
- Thorough: trace dependencies, check tests and types
|
|
11
|
+
You do not form opinions, recommend changes, or review code. You gather verified project evidence and cite it.
|
|
15
12
|
|
|
16
|
-
|
|
17
|
-
1. grep/find to locate relevant code
|
|
18
|
-
2. Read key sections, not entire files
|
|
19
|
-
3. Identify types, interfaces, and key functions
|
|
20
|
-
4. Note dependencies between files
|
|
21
|
-
5. For bug/regression work, include history: `git log` / `git blame` for the relevant files, related tests, prior fixes, and issue references
|
|
13
|
+
## Where the brief goes
|
|
22
14
|
|
|
23
|
-
|
|
15
|
+
The brief lives in a per-user temp directory, keyed by the absolute repo/worktree root, so it never touches the repo and parallel work in another repo cannot collide with it:
|
|
24
16
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
17
|
+
```bash
|
|
18
|
+
ROOT="/tmp/pi-grounding-$(id -u)"
|
|
19
|
+
[ -L "$ROOT" ] && { echo "unsafe symlink: $ROOT" >&2; exit 1; }
|
|
20
|
+
[ -d "$ROOT" ] || { umask 077; mkdir -p "$ROOT"; }
|
|
21
|
+
chmod 700 "$ROOT" 2>/dev/null
|
|
22
|
+
ROOT_ABS=$(cd "$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")" && pwd -P)
|
|
23
|
+
KEY=$(printf '%s' "$ROOT_ABS" | shasum -a 256 | cut -d' ' -f1)
|
|
24
|
+
BRIEF="$ROOT/$KEY.md"
|
|
25
|
+
echo "$BRIEF"
|
|
26
|
+
```
|
|
29
27
|
|
|
30
|
-
|
|
31
|
-
|
|
28
|
+
Write atomically: build the file at `$BRIEF.$$.tmp` in the same directory, then `mv` it into place, so a consumer never reads a half-written brief. Never write the brief inside the repo, and never commit it.
|
|
29
|
+
|
|
30
|
+
## The brief format
|
|
32
31
|
|
|
33
|
-
```typescript
|
|
34
|
-
// actual code
|
|
35
32
|
```
|
|
33
|
+
---
|
|
34
|
+
repo: <absolute repo root, the value of ROOT_ABS>
|
|
35
|
+
head: <git rev-parse HEAD, or "none">
|
|
36
|
+
dirty: <sha256 of `git status --porcelain`, `git diff`, and `git diff --cached` combined, or "none">
|
|
37
|
+
scope: <the one question you were asked, in one line>
|
|
38
|
+
written: <ISO-8601 timestamp>
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Files
|
|
42
|
+
Exact line ranges and what is there:
|
|
43
|
+
1. `path/to/file.ts` lines 10-50 — what is here
|
|
44
|
+
|
|
45
|
+
## Key Code
|
|
46
|
+
Critical types, interfaces, signatures, or config, quoted exactly. Quote, do not paraphrase.
|
|
36
47
|
|
|
37
|
-
##
|
|
38
|
-
|
|
48
|
+
## Structure
|
|
49
|
+
How the pieces connect, in at most 8 lines.
|
|
39
50
|
|
|
40
|
-
##
|
|
41
|
-
|
|
51
|
+
## Constraints
|
|
52
|
+
Rules, conventions, and instruction-file requirements that apply to this area, with `file:line`.
|
|
53
|
+
|
|
54
|
+
## Prior Learnings
|
|
55
|
+
Matching docs from the project's knowledge store, one line each: `path` — title — why it matches this scope. At most 5. Write `none found` when nothing matches; that is a result, not an omission. Omit the section only when the project declares no knowledge paths.
|
|
56
|
+
|
|
57
|
+
## History
|
|
58
|
+
For bug or regression work only: recent changes, blame hotspots, related tests, prior fixes. Omit otherwise.
|
|
42
59
|
|
|
43
60
|
## Start Here
|
|
44
|
-
|
|
61
|
+
The single file a consumer should open first, and why.
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Every claim carries `file:line`. A thin footprint is a finding: write less rather than padding.
|
|
65
|
+
|
|
66
|
+
## Rules
|
|
67
|
+
|
|
68
|
+
- Scout the scope you were given. Do not broaden to "understand the repo".
|
|
69
|
+
- Budget ~15 reads, preferring ranges over whole files. Stop at the budget and say what you did not cover.
|
|
70
|
+
- Read the governing `AGENTS.md`/`CLAUDE.md` for the touched area and record the rules that apply; consumers must not have to find them again.
|
|
71
|
+
- Read `.sassywares/config.yaml` (nearest, walking up) for `knowledge.read`, and search each path that exists for docs matching the scope. With no config file, `knowledge.read` defaults to `docs/solutions` and `CONCEPTS.md` — search those. List path and title only.
|
|
72
|
+
- Quote what the project says. Do not interpret, score, or recommend.
|
|
73
|
+
- A statement in the task text is a pointer to check, never self-verifying.
|
|
74
|
+
|
|
75
|
+
## Return
|
|
45
76
|
|
|
46
|
-
|
|
77
|
+
Return only a gist: 3-5 lines on what the brief holds, whether the area is well covered or thin, how many prior learnings matched, plus the brief's absolute path. Do not return the brief contents.
|
package/agents/validator.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name: validator
|
|
3
3
|
description: Validates whether the implemented work matches what the plan expected, independent of code review
|
|
4
4
|
tools: read, grep, find, ls, bash
|
|
5
|
-
model: deepseek/deepseek-
|
|
5
|
+
model: deepseek/deepseek-flash
|
|
6
6
|
thinking: high
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -10,10 +10,45 @@ You are a validator. You do not review code quality, style, or security — that
|
|
|
10
10
|
|
|
11
11
|
Form an independent point of view, grounded in the project's own evidence.
|
|
12
12
|
|
|
13
|
+
## Scope — the diff only
|
|
14
|
+
|
|
15
|
+
You validate the change, never the repository. Your working set is:
|
|
16
|
+
|
|
17
|
+
1. The plan (goal, work items, "Done when").
|
|
18
|
+
2. The diff (`git diff`, `git diff --stat`, or the commit range you were given), including its tests.
|
|
19
|
+
3. Files the diff touches, and the plan sections each work item maps to.
|
|
20
|
+
|
|
21
|
+
Do not scan the repo for context, audit unrelated code, or assess architecture. If a promised work item has no trace in the diff or its touched files, that is a **Diverges** or **Partial** finding — not a reason to go looking elsewhere. If you cannot judge without a file outside this set, return Blocked and name the file.
|
|
22
|
+
|
|
23
|
+
## Grounding
|
|
24
|
+
|
|
25
|
+
Before reading anything, read the shared grounding brief — the `scout` writes it so you do not re-derive project context. Locate and check it in one call:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
ROOT="/tmp/pi-grounding-$(id -u)"
|
|
29
|
+
[ -L "$ROOT" ] && { echo "unsafe symlink: $ROOT" >&2; exit 1; }
|
|
30
|
+
[ -d "$ROOT" ] || { umask 077; mkdir -p "$ROOT"; }
|
|
31
|
+
chmod 700 "$ROOT" 2>/dev/null
|
|
32
|
+
ROOT_ABS=$(cd "$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")" && pwd -P)
|
|
33
|
+
KEY=$(printf '%s' "$ROOT_ABS" | shasum -a 256 | cut -d' ' -f1)
|
|
34
|
+
BRIEF="$ROOT/$KEY.md"
|
|
35
|
+
HEAD=$(git rev-parse HEAD 2>/dev/null || echo none)
|
|
36
|
+
DIRTY=$( { git status --porcelain; git diff; git diff --cached; } 2>/dev/null | shasum -a 256 | cut -d' ' -f1)
|
|
37
|
+
if [ -f "$BRIEF" ]; then
|
|
38
|
+
for _ in $(seq 1 30); do
|
|
39
|
+
{ grep -qx "head: $HEAD" "$BRIEF" && grep -qx "dirty: $DIRTY" "$BRIEF"; } && break
|
|
40
|
+
sleep 2
|
|
41
|
+
done
|
|
42
|
+
fi
|
|
43
|
+
echo "$BRIEF"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Read the brief if it exists. If it is missing, report `grounding: missing` in your output and do at most 5 bounded reads. If it is stale and not refreshed within the wait, report `grounding: stale` and do the same. Never become a second scout.
|
|
47
|
+
|
|
13
48
|
## Method
|
|
14
49
|
|
|
15
50
|
1. **Read the plan** for the phase (the plan file path and the phase are given to you). Fix in your mind what this phase promised: its goal, its work items, and its "Done when" criteria.
|
|
16
|
-
2. **Ground the claim.**
|
|
51
|
+
2. **Ground the claim in the diff.** Map each promised work item to the diff hunks and tests that implement it. A statement in the task text is a pointer to check, never self-verifying. Cite `file:line` evidence for every conclusion.
|
|
17
52
|
3. **Take a skeptic stance.** Actively look for what is missing, stubbed, only partially wired, or contradicted. "No", "Partial", and "Diverges" are first-class outcomes. Do not let the momentum of a phase being "done" pull the grade up.
|
|
18
53
|
4. **Do not assess code quality.** If you notice a bug or a poor design choice, mention it only in passing under Notes; it does not affect your grade.
|
|
19
54
|
|
package/agents/worker.md
CHANGED
|
@@ -9,7 +9,36 @@ You are a worker agent with full capabilities. You operate in an isolated contex
|
|
|
9
9
|
|
|
10
10
|
Work autonomously to complete the assigned task. Use all available tools. Follow the plan and the task exactly. Do not expand scope beyond what was asked. If the task is part of a phase, implement only that phase's work.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Before reading files to understand the project, read the shared grounding brief — the `scout` writes it so subagents do not each re-derive repo orientation:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
ROOT="/tmp/pi-grounding-$(id -u)"
|
|
16
|
+
[ -L "$ROOT" ] && { echo "unsafe symlink: $ROOT" >&2; exit 1; }
|
|
17
|
+
[ -d "$ROOT" ] || { umask 077; mkdir -p "$ROOT"; }
|
|
18
|
+
chmod 700 "$ROOT" 2>/dev/null
|
|
19
|
+
ROOT_ABS=$(cd "$(git rev-parse --show-toplevel 2>/dev/null || echo "$PWD")" && pwd -P)
|
|
20
|
+
KEY=$(printf '%s' "$ROOT_ABS" | shasum -a 256 | cut -d' ' -f1)
|
|
21
|
+
BRIEF="$ROOT/$KEY.md"
|
|
22
|
+
HEAD=$(git rev-parse HEAD 2>/dev/null || echo none)
|
|
23
|
+
DIRTY=$( { git status --porcelain; git diff; git diff --cached; } 2>/dev/null | shasum -a 256 | cut -d' ' -f1)
|
|
24
|
+
if [ -f "$BRIEF" ]; then
|
|
25
|
+
for _ in $(seq 1 30); do
|
|
26
|
+
{ grep -qx "head: $HEAD" "$BRIEF" && grep -qx "dirty: $DIRTY" "$BRIEF"; } && break
|
|
27
|
+
sleep 2
|
|
28
|
+
done
|
|
29
|
+
fi
|
|
30
|
+
echo "$BRIEF"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Read the brief if it exists. If it is missing or stale, do your own bounded lookups (at most 5) and note `grounding: missing` or `grounding: stale` in Notes. Do not run a broad discovery pass; the brief exists to make that unnecessary.
|
|
34
|
+
|
|
35
|
+
If you are fixing findings from the reviewer or validator, fix exactly the reported items and nothing else.
|
|
36
|
+
|
|
37
|
+
## Verification and blockers
|
|
38
|
+
|
|
39
|
+
- Run tests only for the files you changed and for existing tests that directly exercise those files as regression coverage. Do not run the full suite; the final gate runs it once.
|
|
40
|
+
- Wrap long-running commands in a hard timeout, for example `timeout 300 bunx vitest run path/to/file.test.ts`. If a command hangs, kill it and report it.
|
|
41
|
+
- If you are blocked, stop and report the blocker to the orchestrator in Notes with the exact command and error. Do not retry the same failing command more than once, and do not loop on it.
|
|
13
42
|
|
|
14
43
|
Output format when finished:
|
|
15
44
|
|
package/extensions/handoff.ts
CHANGED
|
@@ -3,13 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Usage:
|
|
5
5
|
* /handoff <goal for the next session>
|
|
6
|
-
* /handoff mode:auto <goal for the next session>
|
|
7
6
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* mode:auto: generate the prompt and start the new session immediately, sending
|
|
12
|
-
* the prompt as its first user message (skill commands inside it are expanded).
|
|
7
|
+
* Generates a focused continuation prompt, lets the user edit it, then opens a
|
|
8
|
+
* new session with the prompt seeded in the editor for the user to submit.
|
|
13
9
|
*
|
|
14
10
|
* Session replacement is only available from command handlers, which is why
|
|
15
11
|
* this is a command and not a tool.
|
|
@@ -93,17 +89,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
93
89
|
return;
|
|
94
90
|
}
|
|
95
91
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
auto = true;
|
|
100
|
-
rest = rest.slice("mode:auto".length).trim();
|
|
101
|
-
}
|
|
102
|
-
if (!rest) {
|
|
103
|
-
ctx.ui.notify("Usage: /handoff [mode:auto] <goal for next session>", "error");
|
|
92
|
+
const goal = args.trim();
|
|
93
|
+
if (!goal) {
|
|
94
|
+
ctx.ui.notify("Usage: /handoff <goal for next session>", "error");
|
|
104
95
|
return;
|
|
105
96
|
}
|
|
106
|
-
const goal = rest;
|
|
107
97
|
|
|
108
98
|
const messages = getHandoffMessages(ctx.sessionManager.getBranch());
|
|
109
99
|
if (messages.length === 0) {
|
|
@@ -159,25 +149,18 @@ export default function (pi: ExtensionAPI) {
|
|
|
159
149
|
return;
|
|
160
150
|
}
|
|
161
151
|
|
|
162
|
-
|
|
163
|
-
if (
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
ctx.ui.notify("Handoff cancelled", "info");
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
prompt = edited;
|
|
152
|
+
const edited = await ctx.ui.editor("Edit handoff prompt", generated);
|
|
153
|
+
if (edited === undefined) {
|
|
154
|
+
ctx.ui.notify("Handoff cancelled", "info");
|
|
155
|
+
return;
|
|
170
156
|
}
|
|
157
|
+
const prompt = edited;
|
|
171
158
|
|
|
172
159
|
const result = await ctx.newSession({
|
|
173
160
|
parentSession: currentSessionFile,
|
|
174
161
|
withSession: async (replacementCtx) => {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
} else {
|
|
178
|
-
replacementCtx.ui.setEditorText(prompt);
|
|
179
|
-
replacementCtx.ui.notify("Handoff ready. Submit when ready.", "info");
|
|
180
|
-
}
|
|
162
|
+
replacementCtx.ui.setEditorText(prompt);
|
|
163
|
+
replacementCtx.ui.notify("Handoff ready. Submit when ready.", "info");
|
|
181
164
|
},
|
|
182
165
|
});
|
|
183
166
|
|
|
@@ -32,10 +32,38 @@ import { Type } from "typebox";
|
|
|
32
32
|
import { type AgentConfig, type AgentScope, discoverAgents } from "./agents.ts";
|
|
33
33
|
|
|
34
34
|
const MAX_PARALLEL_TASKS = 8;
|
|
35
|
-
|
|
35
|
+
// Default to 2 concurrent subagents. A three-worker MiniMax M3.1 Flash batch
|
|
36
|
+
// showed stream stalls and request timeouts under load. DeepSeek Flash was fine
|
|
37
|
+
// with 4; raise this with PI_SUBAGENT_CONCURRENCY for a DeepSeek-only dispatch.
|
|
38
|
+
const MAX_CONCURRENCY = readNonNegativeInt(process.env.PI_SUBAGENT_CONCURRENCY, 2) || 2;
|
|
39
|
+
const IDLE_TIMEOUT_MS = readNonNegativeInt(process.env.PI_SUBAGENT_IDLE_TIMEOUT_MS, 120_000);
|
|
36
40
|
const COLLAPSED_ITEM_COUNT = 10;
|
|
37
41
|
const PER_TASK_OUTPUT_CAP = 50 * 1024;
|
|
38
42
|
|
|
43
|
+
function readNonNegativeInt(value: string | undefined, fallback: number): number {
|
|
44
|
+
if (value === undefined || value.trim() === "") return fallback;
|
|
45
|
+
const parsed = Number(value);
|
|
46
|
+
return Number.isFinite(parsed) && parsed >= 0 ? Math.floor(parsed) : fallback;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function getTelemetryPath(): string | null {
|
|
50
|
+
const configured = process.env.PI_SUBAGENT_TELEMETRY?.trim();
|
|
51
|
+
if (configured === "0" || configured === "off" || configured === "false") return null;
|
|
52
|
+
if (configured?.startsWith("~/")) return path.join(os.homedir(), configured.slice(2));
|
|
53
|
+
return configured || path.join(getAgentDir(), "subagent-runs.jsonl");
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function appendSubagentTelemetry(entry: Record<string, unknown>): void {
|
|
57
|
+
const file = getTelemetryPath();
|
|
58
|
+
if (!file) return;
|
|
59
|
+
try {
|
|
60
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
61
|
+
fs.appendFileSync(file, `${JSON.stringify(entry)}\n`, { encoding: "utf-8", mode: 0o600 });
|
|
62
|
+
} catch {
|
|
63
|
+
// Telemetry is best-effort; never fail a subagent run because logging failed.
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
39
67
|
function formatTokens(count: number): string {
|
|
40
68
|
if (count < 1000) return count.toString();
|
|
41
69
|
if (count < 10000) return `${(count / 1000).toFixed(1)}k`;
|
|
@@ -322,6 +350,16 @@ async function runSingleAgent(
|
|
|
322
350
|
step,
|
|
323
351
|
};
|
|
324
352
|
|
|
353
|
+
const runStartedAt = Date.now();
|
|
354
|
+
let wasAborted = false;
|
|
355
|
+
let watchdogKilled = false;
|
|
356
|
+
let runError: string | undefined;
|
|
357
|
+
let modelMs = 0;
|
|
358
|
+
let toolMs = 0;
|
|
359
|
+
let errorTurns = 0;
|
|
360
|
+
let retries = 0;
|
|
361
|
+
let slowestTurnMs = 0;
|
|
362
|
+
|
|
325
363
|
const emitUpdate = () => {
|
|
326
364
|
if (onUpdate) {
|
|
327
365
|
onUpdate({
|
|
@@ -340,7 +378,6 @@ async function runSingleAgent(
|
|
|
340
378
|
}
|
|
341
379
|
|
|
342
380
|
args.push(`Task: ${task}`);
|
|
343
|
-
let wasAborted = false;
|
|
344
381
|
|
|
345
382
|
const exitCode = await new Promise<number>((resolve) => {
|
|
346
383
|
const invocation = getPiInvocation(args);
|
|
@@ -350,6 +387,38 @@ async function runSingleAgent(
|
|
|
350
387
|
stdio: ["ignore", "pipe", "pipe"],
|
|
351
388
|
});
|
|
352
389
|
let buffer = "";
|
|
390
|
+
let killRequested = false;
|
|
391
|
+
let settled = false;
|
|
392
|
+
let idleTimer: ReturnType<typeof setTimeout> | undefined;
|
|
393
|
+
|
|
394
|
+
const settle = (code: number) => {
|
|
395
|
+
if (settled) return;
|
|
396
|
+
settled = true;
|
|
397
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
398
|
+
resolve(code);
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
const killProc = (reason: string) => {
|
|
402
|
+
if (killRequested) return;
|
|
403
|
+
killRequested = true;
|
|
404
|
+
currentResult.stopReason = "error";
|
|
405
|
+
currentResult.errorMessage = reason;
|
|
406
|
+
proc.kill("SIGTERM");
|
|
407
|
+
const hardKill = setTimeout(() => {
|
|
408
|
+
if (proc.exitCode === null && proc.signalCode === null) proc.kill("SIGKILL");
|
|
409
|
+
}, 5000);
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
const armIdleTimer = () => {
|
|
413
|
+
if (IDLE_TIMEOUT_MS <= 0 || killRequested || settled) return;
|
|
414
|
+
if (idleTimer) clearTimeout(idleTimer);
|
|
415
|
+
idleTimer = setTimeout(() => {
|
|
416
|
+
watchdogKilled = true;
|
|
417
|
+
killProc(`Subagent idle for ${IDLE_TIMEOUT_MS}ms; terminated by watchdog.`);
|
|
418
|
+
}, IDLE_TIMEOUT_MS);
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
armIdleTimer();
|
|
353
422
|
|
|
354
423
|
const processLine = (line: string) => {
|
|
355
424
|
if (!line.trim()) return;
|
|
@@ -375,6 +444,10 @@ async function runSingleAgent(
|
|
|
375
444
|
currentResult.usage.cost += usage.cost?.total || 0;
|
|
376
445
|
currentResult.usage.contextTokens = usage.totalTokens || 0;
|
|
377
446
|
}
|
|
447
|
+
const turnMs = (msg as Message & { durationMs?: number }).durationMs ?? 0;
|
|
448
|
+
modelMs += turnMs;
|
|
449
|
+
slowestTurnMs = Math.max(slowestTurnMs, turnMs);
|
|
450
|
+
if (msg.stopReason === "error" || msg.errorMessage) errorTurns++;
|
|
378
451
|
if (!currentResult.model && msg.model) currentResult.model = msg.model;
|
|
379
452
|
if (msg.stopReason) currentResult.stopReason = msg.stopReason;
|
|
380
453
|
if (msg.errorMessage) currentResult.errorMessage = msg.errorMessage;
|
|
@@ -384,11 +457,16 @@ async function runSingleAgent(
|
|
|
384
457
|
|
|
385
458
|
if (event.type === "tool_result_end" && event.message) {
|
|
386
459
|
currentResult.messages.push(event.message as Message);
|
|
460
|
+
const toolDuration = (event.message as Message & { durationMs?: number }).durationMs;
|
|
461
|
+
if (typeof toolDuration === "number") toolMs += toolDuration;
|
|
387
462
|
emitUpdate();
|
|
388
463
|
}
|
|
464
|
+
|
|
465
|
+
if (event.type === "auto_retry_start") retries++;
|
|
389
466
|
};
|
|
390
467
|
|
|
391
468
|
proc.stdout.on("data", (data) => {
|
|
469
|
+
armIdleTimer();
|
|
392
470
|
buffer += data.toString();
|
|
393
471
|
const lines = buffer.split("\n");
|
|
394
472
|
buffer = lines.pop() || "";
|
|
@@ -396,35 +474,68 @@ async function runSingleAgent(
|
|
|
396
474
|
});
|
|
397
475
|
|
|
398
476
|
proc.stderr.on("data", (data) => {
|
|
477
|
+
armIdleTimer();
|
|
399
478
|
currentResult.stderr += data.toString();
|
|
400
479
|
});
|
|
401
480
|
|
|
402
481
|
proc.on("close", (code) => {
|
|
403
482
|
if (buffer.trim()) processLine(buffer);
|
|
404
|
-
|
|
483
|
+
settle(code ?? 0);
|
|
405
484
|
});
|
|
406
485
|
|
|
407
486
|
proc.on("error", () => {
|
|
408
|
-
|
|
487
|
+
settle(1);
|
|
409
488
|
});
|
|
410
489
|
|
|
411
490
|
if (signal) {
|
|
412
|
-
const
|
|
491
|
+
const onAbort = () => {
|
|
413
492
|
wasAborted = true;
|
|
414
|
-
|
|
415
|
-
setTimeout(() => {
|
|
416
|
-
if (!proc.killed) proc.kill("SIGKILL");
|
|
417
|
-
}, 5000);
|
|
493
|
+
killProc("Subagent was aborted");
|
|
418
494
|
};
|
|
419
|
-
if (signal.aborted)
|
|
420
|
-
else signal.addEventListener("abort",
|
|
495
|
+
if (signal.aborted) onAbort();
|
|
496
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
497
|
+
proc.on("close", () => signal.removeEventListener("abort", onAbort));
|
|
421
498
|
}
|
|
422
499
|
});
|
|
423
500
|
|
|
424
501
|
currentResult.exitCode = exitCode;
|
|
425
502
|
if (wasAborted) throw new Error("Subagent was aborted");
|
|
426
503
|
return currentResult;
|
|
504
|
+
} catch (error) {
|
|
505
|
+
runError = error instanceof Error ? error.message : String(error);
|
|
506
|
+
currentResult.exitCode = currentResult.exitCode || 1;
|
|
507
|
+
throw error;
|
|
427
508
|
} finally {
|
|
509
|
+
appendSubagentTelemetry({
|
|
510
|
+
timestamp: new Date().toISOString(),
|
|
511
|
+
startedAt: new Date(runStartedAt).toISOString(),
|
|
512
|
+
agent: agentName,
|
|
513
|
+
model: currentResult.model ?? model ?? "inherited",
|
|
514
|
+
thinkingLevel: thinkingLevel ?? null,
|
|
515
|
+
task: task.replace(/\s+/g, " ").slice(0, 300),
|
|
516
|
+
cwd: cwd ?? defaultCwd,
|
|
517
|
+
exitCode: currentResult.exitCode,
|
|
518
|
+
stopReason: currentResult.stopReason ?? null,
|
|
519
|
+
errorMessage: currentResult.errorMessage ?? runError ?? null,
|
|
520
|
+
errors: errorTurns,
|
|
521
|
+
retries,
|
|
522
|
+
watchdogKilled,
|
|
523
|
+
aborted: wasAborted,
|
|
524
|
+
durationMs: Date.now() - runStartedAt,
|
|
525
|
+
modelMs,
|
|
526
|
+
toolMs,
|
|
527
|
+
turns: currentResult.usage.turns,
|
|
528
|
+
slowestTurnMs,
|
|
529
|
+
inputTokens: currentResult.usage.input,
|
|
530
|
+
outputTokens: currentResult.usage.output,
|
|
531
|
+
cacheReadTokens: currentResult.usage.cacheRead,
|
|
532
|
+
cacheWriteTokens: currentResult.usage.cacheWrite,
|
|
533
|
+
contextTokens: currentResult.usage.contextTokens,
|
|
534
|
+
cost: currentResult.usage.cost,
|
|
535
|
+
idleTimeoutMs: IDLE_TIMEOUT_MS,
|
|
536
|
+
concurrency: MAX_CONCURRENCY,
|
|
537
|
+
stderr: currentResult.stderr.trim().slice(-2000),
|
|
538
|
+
});
|
|
428
539
|
if (tmpPromptPath)
|
|
429
540
|
try {
|
|
430
541
|
fs.unlinkSync(tmpPromptPath);
|
package/package.json
CHANGED
package/agents/diagnoser.md
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: diagnoser
|
|
3
|
-
description: Lean bug diagnosis — reproduce a failure, trace it to root cause, and return the causal chain, fix proposal, files, tests, and risks
|
|
4
|
-
tools: read, grep, find, ls, bash
|
|
5
|
-
model: deepseek/deepseek-v4-pro
|
|
6
|
-
thinking: high
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
You are a bug diagnoser. You work in an isolated context. Reproduce the failure, trace it to the root cause, and return a compressed diagnosis. You do not edit code.
|
|
10
|
-
|
|
11
|
-
Rules:
|
|
12
|
-
- Reproduce first. A bug you cannot reproduce is not understood.
|
|
13
|
-
- Trace backward from the symptom to the trigger. Every conclusion needs `file:line` evidence.
|
|
14
|
-
- Separate confirmed facts from hypotheses. Do not claim a root cause you cannot support.
|
|
15
|
-
- Read-only: no edits, writes, or commits. Bash is for read-only commands and running the failing test/repro only.
|
|
16
|
-
- Keep output compressed. Do not narrate your search.
|
|
17
|
-
|
|
18
|
-
Strategy:
|
|
19
|
-
1. Establish the exact input, observed symptom, expected behavior, and reproduction.
|
|
20
|
-
2. Reproduce in the smallest reliable way. Check environment and unrelated local changes.
|
|
21
|
-
3. Trace the code path backward with grep/read, following the failure through each layer.
|
|
22
|
-
4. Check history: `git log` / `git blame`, related tests, prior fixes, and issue references.
|
|
23
|
-
5. State the full causal chain with no gaps. `"somehow X leads to Y"` is a gap.
|
|
24
|
-
6. Propose the smallest fix, the files/tests it touches, and the risks.
|
|
25
|
-
|
|
26
|
-
Output format:
|
|
27
|
-
|
|
28
|
-
## Reproduction
|
|
29
|
-
- Command: …
|
|
30
|
-
- Observed: …
|
|
31
|
-
- Expected: …
|
|
32
|
-
- Reliable: yes | no | not reproducible
|
|
33
|
-
|
|
34
|
-
## Causal Chain
|
|
35
|
-
1. Trigger: … (`file:line`)
|
|
36
|
-
2. …
|
|
37
|
-
3. Symptom: …
|
|
38
|
-
|
|
39
|
-
## Root Cause
|
|
40
|
-
One short paragraph naming the actual cause.
|
|
41
|
-
|
|
42
|
-
## Proposed Fix
|
|
43
|
-
The smallest change that fixes the cause.
|
|
44
|
-
|
|
45
|
-
## Files / Tests
|
|
46
|
-
- `path/to/file` — what changes
|
|
47
|
-
- Test: … — what to add or strengthen
|
|
48
|
-
|
|
49
|
-
## Risks / Unknowns
|
|
50
|
-
Anything that could make the fix wrong or incomplete. Empty when none.
|
|
51
|
-
|
|
52
|
-
## Confidence
|
|
53
|
-
High | Medium | Low — and one line on why.
|
package/agents/simplifier.md
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: simplifier
|
|
3
|
-
description: Simplifies settled, recently changed code for clarity and reuse without changing behavior
|
|
4
|
-
tools: read, grep, find, ls, edit, write, bash
|
|
5
|
-
model: deepseek/deepseek-flash
|
|
6
|
-
thinking: low
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
You are a simplification specialist. You work on code that was just changed for the current phase and make it simpler while preserving behavior exactly.
|
|
10
|
-
|
|
11
|
-
Rules:
|
|
12
|
-
- Preserve behavior. Do not change what the code does, only how it is written.
|
|
13
|
-
- Simplify: remove duplication, dead code, redundant abstractions, and needless indirection. Improve naming and clarity. Reuse what already exists in the repo.
|
|
14
|
-
- Do not refactor beyond the recently changed code. Do not touch unrelated files.
|
|
15
|
-
- Run the relevant tests or checks if they exist. If a change breaks behavior, revert that change.
|
|
16
|
-
|
|
17
|
-
Output format:
|
|
18
|
-
|
|
19
|
-
## Simplified
|
|
20
|
-
What was simplified and why.
|
|
21
|
-
|
|
22
|
-
## Files Changed
|
|
23
|
-
- `path/to/file.ts` - what changed
|
|
24
|
-
|
|
25
|
-
## Findings (if any)
|
|
26
|
-
Anything you saw that needs a decision you must not make yourself (a real bug, a missing test, a behavior question).
|