@sebastianandreasson/pi-autonomous-agents 0.2.0 → 0.3.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 +12 -1
- package/SETUP.md +6 -0
- package/docs/PI_SUPERVISOR.md +7 -9
- package/package.json +3 -3
- package/pi.config.json +1 -0
- package/src/pi-client.mjs +37 -0
- package/src/pi-config.mjs +48 -17
- package/src/pi-history.mjs +2 -0
- package/src/pi-preflight.mjs +48 -17
- package/src/pi-prompts.mjs +292 -103
- package/src/pi-repo.mjs +6 -3
- package/src/pi-rpc-adapter.mjs +31 -0
- package/src/pi-supervisor.mjs +408 -26
- package/src/pi-telemetry.mjs +14 -1
- package/templates/pi.config.example.json +2 -1
package/README.md
CHANGED
|
@@ -91,12 +91,17 @@ PI_CONFIG_FILE=pi.config.json pi-harness clear-history
|
|
|
91
91
|
|
|
92
92
|
The command removes configured harness history/runtime files and verifies that no configured history paths remain afterward.
|
|
93
93
|
|
|
94
|
+
For prompt debugging, the harness also writes the exact assembled prompt for the current role to `.pi-last-prompt.txt` by default.
|
|
95
|
+
For flow debugging, it also writes a machine-readable `.pi-last-iteration.json` summary with the selected task, tester verdict, commit-plan state, and terminal reason.
|
|
96
|
+
|
|
94
97
|
## Generic Contracts
|
|
95
98
|
|
|
96
99
|
- `taskFile`: usually `TODOS.md`
|
|
97
100
|
- `developerInstructionsFile`: per-project developer instructions
|
|
98
101
|
- `testerInstructionsFile`: per-project tester instructions
|
|
99
102
|
- `roleModels`: optional per-role model overrides
|
|
103
|
+
- `commitMode`: `agent` by default, `plan` only for legacy harness-managed commit parsing
|
|
104
|
+
- `promptMode`: `compact` by default
|
|
100
105
|
- `testCommand`: fast verification command
|
|
101
106
|
- `visualCaptureCommand`: project-defined screenshot capture command
|
|
102
107
|
- `visualFeedbackFile`: latest visual-review handoff
|
|
@@ -104,8 +109,14 @@ The command removes configured harness history/runtime files and verifies that n
|
|
|
104
109
|
|
|
105
110
|
For unattended loops, keep `testCommand` fast and bounded, such as a smoke suite. Long real-time Playwright happy-path specs belong in an explicit nightly or post-run lane, not the default developer/tester inner loop.
|
|
106
111
|
|
|
112
|
+
Keep TODO items extremely small and implementation-shaped when using weaker local models. Broad tasks tend to produce much longer turns, more retries, and more tester drift than narrow one-step tasks.
|
|
113
|
+
|
|
107
114
|
The adapter heartbeat is PI-RPC-event based. Streaming shell output does not count as progress on its own, so long-running tools should rely on the tool-aware watchdog thresholds rather than terminal streaming.
|
|
108
115
|
|
|
109
|
-
`piModel` remains the default text model, but you can override specific roles with `roleModels` such as `developer`, `developerRetry`, `developerFix`, `tester`, `testerCommit
|
|
116
|
+
`piModel` remains the default text model, but you can override specific roles with `roleModels` such as `developer`, `developerRetry`, `developerFix`, `tester`, and `visualReview`. `testerCommit` is only relevant if you opt back into `commitMode: "plan"`.
|
|
117
|
+
|
|
118
|
+
By default, successful tester passes should stage and create the commit directly in the same PI turn. The old commit-plan parsing flow is still available as `commitMode: "plan"`, but it is now a compatibility mode rather than the default.
|
|
119
|
+
|
|
120
|
+
Prompt/context handoff is compact by default. The harness now caps prior feedback excerpts, changed-file lists, verification excerpts, and prompt note handoff. If needed, tune `maxPromptChangedFiles`, `maxVisualFeedbackLines`, `maxTesterFeedbackLines`, `maxPromptNotesLines`, and `maxVerificationExcerptLines`.
|
|
110
121
|
|
|
111
122
|
The harness expects screenshot capture to produce a `manifest.json` plus image files under the configured visual capture directory.
|
package/SETUP.md
CHANGED
|
@@ -46,6 +46,7 @@ If the repo uses another package manager already, use the repo-native equivalent
|
|
|
46
46
|
- `taskFile`: usually `TODOS.md`
|
|
47
47
|
- `developerInstructionsFile`: `pi/DEVELOPER.md`
|
|
48
48
|
- `testerInstructionsFile`: `pi/TESTER.md`
|
|
49
|
+
- `commitMode`: normally `agent`
|
|
49
50
|
- `testCommand`: a fast bounded verification command for this repo
|
|
50
51
|
- `visualCaptureCommand`: only if this repo has a real screenshot capture flow
|
|
51
52
|
- `models` / `piModel` / `visualReviewModel` / `roleModels`: configure the models actually available in this environment
|
|
@@ -123,6 +124,7 @@ Recommended pattern:
|
|
|
123
124
|
- local model for `developerFix`
|
|
124
125
|
- local or slightly stronger model for `tester`
|
|
125
126
|
- stronger frontier model for `visualReview` only if available
|
|
127
|
+
- keep `commitMode` as `agent` unless the repo explicitly needs legacy harness-managed commit-plan parsing
|
|
126
128
|
|
|
127
129
|
Example shape:
|
|
128
130
|
|
|
@@ -179,6 +181,9 @@ The harness should fail fast if:
|
|
|
179
181
|
- a configured provider endpoint is unreachable
|
|
180
182
|
- a configured provider does not serve the configured model id
|
|
181
183
|
|
|
184
|
+
For prompt debugging, inspect `.pi-last-prompt.txt` after a run. It contains the exact assembled prompt that was sent for the active role.
|
|
185
|
+
For flow debugging, inspect `.pi-last-iteration.json` after a run. It summarizes the selected task, repo-change outcome, tester verdict, commit-plan state, and terminal reason.
|
|
186
|
+
|
|
182
187
|
## Agent Rules
|
|
183
188
|
|
|
184
189
|
- Reuse existing repo conventions where possible.
|
|
@@ -186,6 +191,7 @@ The harness should fail fast if:
|
|
|
186
191
|
- Do not invent fake test commands or model endpoints.
|
|
187
192
|
- Do not enable visual review unless the repo actually has a usable capture command and model config.
|
|
188
193
|
- Keep changes minimal and local to harness setup.
|
|
194
|
+
- Prefer very small, implementation-shaped TODO items for local models. Broad tasks tend to create long turns, retries, and weak tester behavior.
|
|
189
195
|
|
|
190
196
|
## What To Report Back
|
|
191
197
|
|
package/docs/PI_SUPERVISOR.md
CHANGED
|
@@ -18,7 +18,7 @@ Each real iteration follows this sequence:
|
|
|
18
18
|
2. A fast local verification command runs immediately after the developer round.
|
|
19
19
|
3. If verification passes, `tester` reviews the change independently from a skeptical user-facing perspective.
|
|
20
20
|
4. If tester or verification finds a real issue, the supervisor gives the findings back to `developer` for one focused repair pass.
|
|
21
|
-
5. If tester reaches `PASS`, tester
|
|
21
|
+
5. If tester reaches `PASS`, tester creates the commit directly in the same turn by default.
|
|
22
22
|
6. Optionally, every `N` successful iterations, the harness runs a read-only visual review over screenshots and persists the feedback for later runs.
|
|
23
23
|
7. If that visual review returns `FAIL`, `BLOCKED`, or times out, the iteration is not counted as a success and the feedback is carried into later prompts.
|
|
24
24
|
|
|
@@ -69,6 +69,7 @@ Projects typically provide their own `pi.config.json` with fields such as:
|
|
|
69
69
|
- `models`
|
|
70
70
|
- `piModel`
|
|
71
71
|
- `visualReviewModel`
|
|
72
|
+
- `commitMode`
|
|
72
73
|
|
|
73
74
|
Model entries may carry their own OpenAI-compatible endpoint settings, so the PI text loop and the multimodal visual reviewer can point at different backends without changing code.
|
|
74
75
|
|
|
@@ -83,7 +84,6 @@ Model entries may carry their own OpenAI-compatible endpoint settings, so the PI
|
|
|
83
84
|
"developerRetry": "local/dev-model",
|
|
84
85
|
"developerFix": "local/dev-model",
|
|
85
86
|
"tester": "local/tester-model",
|
|
86
|
-
"testerCommit": "local/tester-model",
|
|
87
87
|
"visualReview": "cloud/vision-model"
|
|
88
88
|
}
|
|
89
89
|
}
|
|
@@ -162,15 +162,13 @@ Allowed response `status` values:
|
|
|
162
162
|
|
|
163
163
|
## Git Finalization
|
|
164
164
|
|
|
165
|
-
The
|
|
165
|
+
The default flow keeps commit ownership with the active agent:
|
|
166
166
|
|
|
167
167
|
1. `developer` should leave a clean, reviewable diff and should not commit.
|
|
168
|
-
2. `tester` should review functionality and, on `PASS`,
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
3. The harness stages only those requested files and performs the commit itself.
|
|
173
|
-
4. If the requested plan cannot be isolated safely, the iteration is blocked or failed instead of committing unrelated work.
|
|
168
|
+
2. `tester` should review functionality and, on `PASS`, stage only the task-related files and create the commit directly.
|
|
169
|
+
3. If the working tree is too messy to isolate safely, tester should return `VERDICT: BLOCKED` instead of guessing.
|
|
170
|
+
|
|
171
|
+
If a repo explicitly needs the older harness-managed commit-plan flow, set `commitMode` to `plan`. In that mode, `testerCommit` and parsed commit plans are used as a compatibility path rather than the default.
|
|
174
172
|
|
|
175
173
|
## Persistent Handoffs
|
|
176
174
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sebastianandreasson/pi-autonomous-agents",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.3.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Portable unattended PI harness for developer/tester/visual-review loops.",
|
|
7
7
|
"license": "MIT",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
"pi-harness": "./src/cli.mjs"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
|
-
"check": "node --check src/cli.mjs && node --check src/pi-clear-history.mjs && node --check src/pi-client.mjs && node --check src/pi-config.mjs && node --check src/pi-flow.mjs && node --check src/pi-heartbeat.mjs && node --check src/pi-history.mjs && node --check src/pi-preflight.mjs && node --check src/pi-prompts.mjs && node --check src/pi-repo.mjs && node --check src/pi-report.mjs && node --check src/pi-rpc-adapter.mjs && node --check src/pi-supervisor.mjs && node --check src/pi-telemetry.mjs && node --check src/pi-visual-once.mjs && node --check src/pi-visual-review.mjs && node --check src/index.mjs && node --check test/pi-heartbeat.test.mjs && node --check test/pi-role-models.test.mjs && node --check test/pi-flow.test.mjs && node --check test/pi-history.test.mjs && node --check test/pi-prompts.test.mjs && node --check test/pi-preflight.test.mjs",
|
|
20
|
-
"test": "node --test test/pi-heartbeat.test.mjs test/pi-role-models.test.mjs test/pi-flow.test.mjs test/pi-history.test.mjs test/pi-prompts.test.mjs test/pi-preflight.test.mjs"
|
|
19
|
+
"check": "node --check src/cli.mjs && node --check src/pi-clear-history.mjs && node --check src/pi-client.mjs && node --check src/pi-config.mjs && node --check src/pi-flow.mjs && node --check src/pi-heartbeat.mjs && node --check src/pi-history.mjs && node --check src/pi-preflight.mjs && node --check src/pi-prompts.mjs && node --check src/pi-repo.mjs && node --check src/pi-report.mjs && node --check src/pi-rpc-adapter.mjs && node --check src/pi-supervisor.mjs && node --check src/pi-telemetry.mjs && node --check src/pi-visual-once.mjs && node --check src/pi-visual-review.mjs && node --check src/index.mjs && node --check test/pi-heartbeat.test.mjs && node --check test/pi-role-models.test.mjs && node --check test/pi-flow.test.mjs && node --check test/pi-history.test.mjs && node --check test/pi-prompts.test.mjs && node --check test/pi-preflight.test.mjs && node --check test/pi-repo.test.mjs && node --check test/pi-telemetry.test.mjs",
|
|
20
|
+
"test": "node --test test/pi-heartbeat.test.mjs test/pi-role-models.test.mjs test/pi-flow.test.mjs test/pi-history.test.mjs test/pi-prompts.test.mjs test/pi-preflight.test.mjs test/pi-repo.test.mjs test/pi-telemetry.test.mjs"
|
|
21
21
|
},
|
|
22
22
|
"files": [
|
|
23
23
|
"src",
|
package/pi.config.json
CHANGED
package/src/pi-client.mjs
CHANGED
|
@@ -18,6 +18,7 @@ function formatLastAgentOutput(response) {
|
|
|
18
18
|
`status: ${String(response.status ?? '')}`,
|
|
19
19
|
`sessionId: ${String(response.sessionId ?? '')}`,
|
|
20
20
|
`sessionFile: ${String(response.sessionFile ?? '')}`,
|
|
21
|
+
`terminalReason: ${String(response.terminalReason ?? '')}`,
|
|
21
22
|
`notes: ${String(response.notes ?? '').trim()}`,
|
|
22
23
|
]
|
|
23
24
|
|
|
@@ -58,6 +59,15 @@ async function runMockTurn({ config, sessionId, sessionFile, prompt, reason }) {
|
|
|
58
59
|
durationSeconds: 0,
|
|
59
60
|
output,
|
|
60
61
|
notes: 'Mock transport completed without repo edits.',
|
|
62
|
+
role: '',
|
|
63
|
+
model: '',
|
|
64
|
+
toolCalls: 0,
|
|
65
|
+
toolErrors: 0,
|
|
66
|
+
messageUpdates: 0,
|
|
67
|
+
stopReason: '',
|
|
68
|
+
loopDetected: false,
|
|
69
|
+
loopSignature: '',
|
|
70
|
+
terminalReason: 'mock_completed',
|
|
61
71
|
}
|
|
62
72
|
}
|
|
63
73
|
|
|
@@ -142,6 +152,15 @@ async function runAdapterTurn({ config, model, sessionId, sessionFile, prompt, i
|
|
|
142
152
|
durationSeconds: result.durationSeconds,
|
|
143
153
|
output: result.combinedOutput,
|
|
144
154
|
notes: 'Adapter process exceeded the configured timeout.',
|
|
155
|
+
role: '',
|
|
156
|
+
model: model ?? config.piModel,
|
|
157
|
+
toolCalls: 0,
|
|
158
|
+
toolErrors: 0,
|
|
159
|
+
messageUpdates: 0,
|
|
160
|
+
stopReason: '',
|
|
161
|
+
loopDetected: false,
|
|
162
|
+
loopSignature: '',
|
|
163
|
+
terminalReason: 'agent_timeout',
|
|
145
164
|
}
|
|
146
165
|
}
|
|
147
166
|
|
|
@@ -157,6 +176,15 @@ async function runAdapterTurn({ config, model, sessionId, sessionFile, prompt, i
|
|
|
157
176
|
durationSeconds: result.durationSeconds,
|
|
158
177
|
output: result.combinedOutput,
|
|
159
178
|
notes: truncateForNotes(result.combinedOutput) || 'Adapter exited non-zero.',
|
|
179
|
+
role: '',
|
|
180
|
+
model: model ?? config.piModel,
|
|
181
|
+
toolCalls: 0,
|
|
182
|
+
toolErrors: 0,
|
|
183
|
+
messageUpdates: 0,
|
|
184
|
+
stopReason: '',
|
|
185
|
+
loopDetected: false,
|
|
186
|
+
loopSignature: '',
|
|
187
|
+
terminalReason: 'adapter_failed',
|
|
160
188
|
}
|
|
161
189
|
}
|
|
162
190
|
|
|
@@ -179,6 +207,15 @@ async function runAdapterTurn({ config, model, sessionId, sessionFile, prompt, i
|
|
|
179
207
|
durationSeconds: result.durationSeconds,
|
|
180
208
|
output,
|
|
181
209
|
notes,
|
|
210
|
+
role: String(response.role ?? ''),
|
|
211
|
+
model: String(response.model ?? model ?? config.piModel ?? ''),
|
|
212
|
+
toolCalls: Number.isFinite(Number(response.toolCalls)) ? Number(response.toolCalls) : 0,
|
|
213
|
+
toolErrors: Number.isFinite(Number(response.toolErrors)) ? Number(response.toolErrors) : 0,
|
|
214
|
+
messageUpdates: Number.isFinite(Number(response.messageUpdates)) ? Number(response.messageUpdates) : 0,
|
|
215
|
+
stopReason: String(response.stopReason ?? ''),
|
|
216
|
+
loopDetected: response.loopDetected === true,
|
|
217
|
+
loopSignature: String(response.loopSignature ?? ''),
|
|
218
|
+
terminalReason: String(response.terminalReason ?? ''),
|
|
182
219
|
}
|
|
183
220
|
}
|
|
184
221
|
|
package/src/pi-config.mjs
CHANGED
|
@@ -130,6 +130,22 @@ function normalizeRoleModels(raw) {
|
|
|
130
130
|
return normalized
|
|
131
131
|
}
|
|
132
132
|
|
|
133
|
+
function normalizeCommitMode(raw) {
|
|
134
|
+
const value = normalizeString(raw, 'agent').trim().toLowerCase()
|
|
135
|
+
if (value === 'agent' || value === 'plan') {
|
|
136
|
+
return value
|
|
137
|
+
}
|
|
138
|
+
throw new Error(`Expected commitMode to be "agent" or "plan", received "${raw}"`)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function normalizePromptMode(raw) {
|
|
142
|
+
const value = normalizeString(raw, 'compact').trim().toLowerCase()
|
|
143
|
+
if (value === 'compact' || value === 'full') {
|
|
144
|
+
return value
|
|
145
|
+
}
|
|
146
|
+
throw new Error(`Expected promptMode to be "compact" or "full", received "${raw}"`)
|
|
147
|
+
}
|
|
148
|
+
|
|
133
149
|
function resolveModelProfile(modelProfiles, modelName) {
|
|
134
150
|
if (!modelName || typeof modelName !== 'string') {
|
|
135
151
|
return null
|
|
@@ -181,12 +197,30 @@ export function loadConfig(mode = 'once') {
|
|
|
181
197
|
const repoConfig = readRepoConfig(cwd)
|
|
182
198
|
const file = repoConfig.values
|
|
183
199
|
const bundledAdapterCommand = 'pi-harness adapter'
|
|
200
|
+
const bundledDeveloperInstructionsFile = path.join(packageRoot, 'templates', 'DEVELOPER.md')
|
|
201
|
+
const bundledTesterInstructionsFile = path.join(packageRoot, 'templates', 'TESTER.md')
|
|
184
202
|
const modelProfiles = readObject('models', file.models, {})
|
|
185
203
|
const roleModels = normalizeRoleModels(file.roleModels)
|
|
186
204
|
const piModel = readString('PI_MODEL', file.piModel, '')
|
|
187
205
|
const visualReviewModel = readString('PI_VISUAL_REVIEW_MODEL', file.visualReviewModel, '')
|
|
188
206
|
const resolvedPiModel = resolveModelProfile(modelProfiles, piModel)
|
|
189
207
|
const resolvedVisualReviewModel = resolveModelProfile(modelProfiles, visualReviewModel)
|
|
208
|
+
const developerInstructionsFile = resolveInstructionsFile(
|
|
209
|
+
cwd,
|
|
210
|
+
'PI_DEVELOPER_INSTRUCTIONS_FILE',
|
|
211
|
+
file.developerInstructionsFile,
|
|
212
|
+
hasValue(file.instructionsFile)
|
|
213
|
+
? String(file.instructionsFile)
|
|
214
|
+
: bundledDeveloperInstructionsFile
|
|
215
|
+
)
|
|
216
|
+
const testerInstructionsFile = resolveInstructionsFile(
|
|
217
|
+
cwd,
|
|
218
|
+
'PI_TESTER_INSTRUCTIONS_FILE',
|
|
219
|
+
file.testerInstructionsFile,
|
|
220
|
+
hasValue(file.instructionsFile)
|
|
221
|
+
? String(file.instructionsFile)
|
|
222
|
+
: bundledTesterInstructionsFile
|
|
223
|
+
)
|
|
190
224
|
|
|
191
225
|
return {
|
|
192
226
|
cwd,
|
|
@@ -196,23 +230,11 @@ export function loadConfig(mode = 'once') {
|
|
|
196
230
|
agentName: readString('PI_AGENT_NAME', file.agentName, 'PI'),
|
|
197
231
|
adapterCommand: readString('PI_ADAPTER_COMMAND', file.adapterCommand, bundledAdapterCommand),
|
|
198
232
|
taskFile: resolveFromCwd(cwd, 'PI_TASK_FILE', file.taskFile, 'TODOS.md'),
|
|
199
|
-
instructionsFile: resolveInstructionsFile(cwd, 'PI_INSTRUCTIONS_FILE', file.instructionsFile,
|
|
200
|
-
developerInstructionsFile
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
hasValue(file.instructionsFile)
|
|
205
|
-
? String(file.instructionsFile)
|
|
206
|
-
: path.join(packageRoot, 'templates', 'DEVELOPER.md')
|
|
207
|
-
),
|
|
208
|
-
testerInstructionsFile: resolveInstructionsFile(
|
|
209
|
-
cwd,
|
|
210
|
-
'PI_TESTER_INSTRUCTIONS_FILE',
|
|
211
|
-
file.testerInstructionsFile,
|
|
212
|
-
hasValue(file.instructionsFile)
|
|
213
|
-
? String(file.instructionsFile)
|
|
214
|
-
: path.join(packageRoot, 'templates', 'TESTER.md')
|
|
215
|
-
),
|
|
233
|
+
instructionsFile: resolveInstructionsFile(cwd, 'PI_INSTRUCTIONS_FILE', file.instructionsFile, bundledDeveloperInstructionsFile),
|
|
234
|
+
developerInstructionsFile,
|
|
235
|
+
testerInstructionsFile,
|
|
236
|
+
usingBundledDeveloperInstructions: developerInstructionsFile === bundledDeveloperInstructionsFile,
|
|
237
|
+
usingBundledTesterInstructions: testerInstructionsFile === bundledTesterInstructionsFile,
|
|
216
238
|
logFile: resolveFromCwd(cwd, 'PI_LOG_FILE', file.logFile, 'pi.log'),
|
|
217
239
|
telemetryJsonl: resolveFromCwd(cwd, 'PI_TELEMETRY_JSONL', file.telemetryJsonl, 'pi_telemetry.jsonl'),
|
|
218
240
|
telemetryCsv: resolveFromCwd(cwd, 'PI_TELEMETRY_CSV', file.telemetryCsv, 'pi_telemetry.csv'),
|
|
@@ -221,12 +243,21 @@ export function loadConfig(mode = 'once') {
|
|
|
221
243
|
lastAgentOutputFile: resolveFromCwd(cwd, 'PI_LAST_AGENT_OUTPUT_FILE', file.lastAgentOutputFile, '.pi-last-output.txt'),
|
|
222
244
|
lastVerificationOutputFile: resolveFromCwd(cwd, 'PI_LAST_VERIFICATION_OUTPUT_FILE', file.lastVerificationOutputFile, '.pi-last-verification.txt'),
|
|
223
245
|
changedFilesFile: resolveFromCwd(cwd, 'PI_CHANGED_FILES_FILE', file.changedFilesFile, '.pi-changed-files.txt'),
|
|
246
|
+
lastPromptFile: resolveFromCwd(cwd, 'PI_LAST_PROMPT_FILE', file.lastPromptFile, '.pi-last-prompt.txt'),
|
|
247
|
+
lastIterationSummaryFile: resolveFromCwd(cwd, 'PI_LAST_ITERATION_SUMMARY_FILE', file.lastIterationSummaryFile, '.pi-last-iteration.json'),
|
|
224
248
|
piRuntimeDir: resolveFromCwd(cwd, 'PI_RUNTIME_DIR', file.piRuntimeDir, '.pi-runtime'),
|
|
225
249
|
piCli: readString('PI_CLI', file.piCli, 'pi'),
|
|
226
250
|
piModel,
|
|
227
251
|
piModelProfile: resolvedPiModel,
|
|
228
252
|
modelProfiles,
|
|
229
253
|
roleModels,
|
|
254
|
+
commitMode: normalizeCommitMode(readString('PI_COMMIT_MODE', file.commitMode, 'agent')),
|
|
255
|
+
promptMode: normalizePromptMode(readString('PI_PROMPT_MODE', file.promptMode, 'compact')),
|
|
256
|
+
maxPromptChangedFiles: readInt('PI_MAX_PROMPT_CHANGED_FILES', file.maxPromptChangedFiles, 10),
|
|
257
|
+
maxVisualFeedbackLines: readInt('PI_MAX_VISUAL_FEEDBACK_LINES', file.maxVisualFeedbackLines, 20),
|
|
258
|
+
maxTesterFeedbackLines: readInt('PI_MAX_TESTER_FEEDBACK_LINES', file.maxTesterFeedbackLines, 32),
|
|
259
|
+
maxPromptNotesLines: readInt('PI_MAX_PROMPT_NOTES_LINES', file.maxPromptNotesLines, 16),
|
|
260
|
+
maxVerificationExcerptLines: readInt('PI_MAX_VERIFICATION_EXCERPT_LINES', file.maxVerificationExcerptLines, 40),
|
|
230
261
|
piTools: readString('PI_TOOLS', file.piTools, 'read,bash,edit,write,grep,find,ls'),
|
|
231
262
|
piThinking: readString('PI_THINKING', file.piThinking, ''),
|
|
232
263
|
piNoExtensions: readBool('PI_NO_EXTENSIONS', file.piNoExtensions, false),
|
package/src/pi-history.mjs
CHANGED
|
@@ -20,6 +20,8 @@ export function collectHistoryTargets(config) {
|
|
|
20
20
|
config.lastAgentOutputFile,
|
|
21
21
|
config.lastVerificationOutputFile,
|
|
22
22
|
config.changedFilesFile,
|
|
23
|
+
config.lastPromptFile,
|
|
24
|
+
config.lastIterationSummaryFile,
|
|
23
25
|
config.piRuntimeDir,
|
|
24
26
|
config.visualFeedbackFile,
|
|
25
27
|
config.testerFeedbackFile,
|
package/src/pi-preflight.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs/promises'
|
|
2
|
-
import {
|
|
2
|
+
import { spawnSync } from 'node:child_process'
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import process from 'node:process'
|
|
5
5
|
|
|
@@ -34,20 +34,42 @@ export function parsePiListModelsOutput(output) {
|
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
const ids = []
|
|
37
|
+
let modelColumnIndex = -1
|
|
37
38
|
for (const rawLine of text.split('\n')) {
|
|
38
39
|
const line = rawLine.trim()
|
|
40
|
+
const stripped = line.replace(/^[-*]\s+/, '').trim()
|
|
41
|
+
const columns = stripped.split(/\s+/).filter(Boolean)
|
|
42
|
+
const normalizedColumns = columns.map((value) => value.toLowerCase())
|
|
43
|
+
|
|
44
|
+
if (
|
|
45
|
+
modelColumnIndex === -1
|
|
46
|
+
&& normalizedColumns.includes('model')
|
|
47
|
+
&& normalizedColumns.some((value) => value === 'provider' || value === 'id' || value === 'name')
|
|
48
|
+
) {
|
|
49
|
+
modelColumnIndex = normalizedColumns.indexOf('model')
|
|
50
|
+
continue
|
|
51
|
+
}
|
|
52
|
+
|
|
39
53
|
if (
|
|
40
54
|
line === ''
|
|
41
55
|
|| /^available models:?$/i.test(line)
|
|
42
56
|
|| /^models:?$/i.test(line)
|
|
43
57
|
|| /^id\s+/i.test(line)
|
|
44
58
|
|| /^name\s+/i.test(line)
|
|
59
|
+
|| /^[-=\s]+$/.test(line)
|
|
45
60
|
) {
|
|
46
61
|
continue
|
|
47
62
|
}
|
|
48
63
|
|
|
49
|
-
|
|
50
|
-
|
|
64
|
+
if (modelColumnIndex >= 0) {
|
|
65
|
+
const modelToken = columns[modelColumnIndex]?.trim() ?? ''
|
|
66
|
+
if (modelToken !== '') {
|
|
67
|
+
ids.push(modelToken)
|
|
68
|
+
}
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const firstToken = columns[0]?.trim() ?? ''
|
|
51
73
|
if (firstToken !== '') {
|
|
52
74
|
ids.push(firstToken)
|
|
53
75
|
}
|
|
@@ -106,24 +128,33 @@ async function ensurePiHomeModelsConfig() {
|
|
|
106
128
|
}
|
|
107
129
|
|
|
108
130
|
function listPiModels(config) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
131
|
+
const result = spawnSync(config.piCli, ['--list-models'], {
|
|
132
|
+
cwd: config.cwd,
|
|
133
|
+
env: process.env,
|
|
134
|
+
encoding: 'utf8',
|
|
135
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
136
|
+
})
|
|
137
|
+
const stdout = String(result.stdout ?? '').trim()
|
|
138
|
+
const stderr = String(result.stderr ?? '').trim()
|
|
139
|
+
const combinedOutput = [stdout, stderr].filter(Boolean).join('\n').trim()
|
|
140
|
+
|
|
141
|
+
if (result.error) {
|
|
142
|
+
throw new Error(
|
|
143
|
+
combinedOutput === ''
|
|
144
|
+
? `Failed to list PI models via "${config.piCli} --list-models".`
|
|
145
|
+
: `Failed to list PI models via "${config.piCli} --list-models".\n${combinedOutput}`
|
|
146
|
+
)
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (result.status !== 0) {
|
|
121
150
|
throw new Error(
|
|
122
|
-
|
|
151
|
+
combinedOutput === ''
|
|
123
152
|
? `Failed to list PI models via "${config.piCli} --list-models".`
|
|
124
|
-
: `Failed to list PI models via "${config.piCli} --list-models".\n${
|
|
153
|
+
: `Failed to list PI models via "${config.piCli} --list-models".\n${combinedOutput}`
|
|
125
154
|
)
|
|
126
155
|
}
|
|
156
|
+
|
|
157
|
+
return parsePiListModelsOutput(combinedOutput)
|
|
127
158
|
}
|
|
128
159
|
|
|
129
160
|
function getConfiguredTextModels(config) {
|