@juicesharp/rpiv-advisor 0.1.3 → 0.7.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/LICENSE +21 -0
- package/README.md +1 -1
- package/advisor-ui.ts +9 -34
- package/advisor.ts +116 -45
- package/index.ts +10 -10
- package/package.json +16 -7
- package/prompts/advisor-system.txt +1 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 juicesharp
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ implementing the advisor-strategy pattern: the executor model can escalate
|
|
|
5
5
|
decisions to a stronger reviewer model (e.g. Opus), receive guidance, and
|
|
6
6
|
resume.
|
|
7
7
|
|
|
8
|
-

|
|
9
9
|
|
|
10
10
|
## Installation
|
|
11
11
|
|
package/advisor-ui.ts
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* SelectList theme wiring.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { DynamicBorder, type ExtensionContext, type Theme } from "@mariozechner/pi-coding-agent";
|
|
10
9
|
import type { ThinkingLevel } from "@mariozechner/pi-ai";
|
|
11
|
-
import {
|
|
10
|
+
import { DynamicBorder, type ExtensionContext, type Theme } from "@mariozechner/pi-coding-agent";
|
|
11
|
+
import { Container, type SelectItem, SelectList, Spacer, Text } from "@mariozechner/pi-tui";
|
|
12
12
|
|
|
13
13
|
const MAX_VISIBLE_ROWS = 10;
|
|
14
14
|
const NAV_HINT = "↑↓ navigate • enter select • esc cancel";
|
|
@@ -39,12 +39,7 @@ function selectListTheme(theme: Theme) {
|
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
function buildSelectPanel(
|
|
43
|
-
theme: Theme,
|
|
44
|
-
title: string,
|
|
45
|
-
proseLines: string[],
|
|
46
|
-
selectList: SelectList,
|
|
47
|
-
): Container {
|
|
42
|
+
function buildSelectPanel(theme: Theme, title: string, proseLines: string[], selectList: SelectList): Container {
|
|
48
43
|
const container = new Container();
|
|
49
44
|
const border = () => new DynamicBorder((s: string) => theme.fg("accent", s));
|
|
50
45
|
|
|
@@ -64,16 +59,9 @@ function buildSelectPanel(
|
|
|
64
59
|
return container;
|
|
65
60
|
}
|
|
66
61
|
|
|
67
|
-
export async function showAdvisorPicker(
|
|
68
|
-
ctx: ExtensionContext,
|
|
69
|
-
items: SelectItem[],
|
|
70
|
-
): Promise<string | null> {
|
|
62
|
+
export async function showAdvisorPicker(ctx: ExtensionContext, items: SelectItem[]): Promise<string | null> {
|
|
71
63
|
return ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
|
|
72
|
-
const selectList = new SelectList(
|
|
73
|
-
items,
|
|
74
|
-
Math.min(items.length, MAX_VISIBLE_ROWS),
|
|
75
|
-
selectListTheme(theme),
|
|
76
|
-
);
|
|
64
|
+
const selectList = new SelectList(items, Math.min(items.length, MAX_VISIBLE_ROWS), selectListTheme(theme));
|
|
77
65
|
selectList.onSelect = (item) => done(item.value);
|
|
78
66
|
selectList.onCancel = () => done(null);
|
|
79
67
|
|
|
@@ -102,28 +90,15 @@ export async function showEffortPicker(
|
|
|
102
90
|
defaultEffort: ThinkingLevel,
|
|
103
91
|
): Promise<string | null> {
|
|
104
92
|
return ctx.ui.custom<string | null>((tui, theme, _kb, done) => {
|
|
105
|
-
const selectList = new SelectList(
|
|
106
|
-
|
|
107
|
-
Math.min(items.length, MAX_VISIBLE_ROWS),
|
|
108
|
-
selectListTheme(theme),
|
|
109
|
-
);
|
|
110
|
-
const preferredIdx = currentEffort
|
|
111
|
-
? items.findIndex((item) => item.value === currentEffort)
|
|
112
|
-
: -1;
|
|
93
|
+
const selectList = new SelectList(items, Math.min(items.length, MAX_VISIBLE_ROWS), selectListTheme(theme));
|
|
94
|
+
const preferredIdx = currentEffort ? items.findIndex((item) => item.value === currentEffort) : -1;
|
|
113
95
|
selectList.setSelectedIndex(
|
|
114
|
-
preferredIdx >= 0
|
|
115
|
-
? preferredIdx
|
|
116
|
-
: items.findIndex((item) => item.value === defaultEffort),
|
|
96
|
+
preferredIdx >= 0 ? preferredIdx : items.findIndex((item) => item.value === defaultEffort),
|
|
117
97
|
);
|
|
118
98
|
selectList.onSelect = (item) => done(item.value);
|
|
119
99
|
selectList.onCancel = () => done(null);
|
|
120
100
|
|
|
121
|
-
const container = buildSelectPanel(
|
|
122
|
-
theme,
|
|
123
|
-
EFFORT_HEADER_TITLE,
|
|
124
|
-
[EFFORT_HEADER_PROSE],
|
|
125
|
-
selectList,
|
|
126
|
-
);
|
|
101
|
+
const container = buildSelectPanel(theme, EFFORT_HEADER_TITLE, [EFFORT_HEADER_PROSE], selectList);
|
|
127
102
|
|
|
128
103
|
return {
|
|
129
104
|
render: (w) => container.render(w),
|
package/advisor.ts
CHANGED
|
@@ -14,19 +14,19 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
17
|
-
import { dirname, join } from "node:path";
|
|
18
17
|
import { homedir } from "node:os";
|
|
18
|
+
import { dirname, join } from "node:path";
|
|
19
19
|
import { fileURLToPath } from "node:url";
|
|
20
|
-
import { completeSimple, supportsXhigh, type Message, type ThinkingLevel } from "@mariozechner/pi-ai";
|
|
21
20
|
import type { Api, Model, StopReason, Usage } from "@mariozechner/pi-ai";
|
|
21
|
+
import { completeSimple, type Message, supportsXhigh, type ThinkingLevel } from "@mariozechner/pi-ai";
|
|
22
22
|
import {
|
|
23
|
-
convertToLlm,
|
|
24
|
-
serializeConversation,
|
|
25
23
|
type AgentToolResult,
|
|
26
24
|
type AgentToolUpdateCallback,
|
|
25
|
+
convertToLlm,
|
|
27
26
|
type ExtensionAPI,
|
|
28
27
|
type ExtensionContext,
|
|
29
28
|
type SessionEntry,
|
|
29
|
+
type ToolInfo,
|
|
30
30
|
} from "@mariozechner/pi-coding-agent";
|
|
31
31
|
import type { SelectItem } from "@mariozechner/pi-tui";
|
|
32
32
|
import { Type } from "@sinclair/typebox";
|
|
@@ -63,8 +63,7 @@ const MSG_ADVISOR_DISABLED = "Advisor disabled";
|
|
|
63
63
|
const MSG_REQUIRES_INTERACTIVE = "/advisor requires interactive mode";
|
|
64
64
|
|
|
65
65
|
// Errors (static)
|
|
66
|
-
const ERR_NO_MODEL =
|
|
67
|
-
"No advisor model is configured. The user can enable one with the /advisor command.";
|
|
66
|
+
const ERR_NO_MODEL = "No advisor model is configured. The user can enable one with the /advisor command.";
|
|
68
67
|
const ERR_CALL_ABORTED = "Advisor call was cancelled before it completed.";
|
|
69
68
|
const ERR_EMPTY_RESPONSE = "Advisor returned no text content.";
|
|
70
69
|
const ERR_NO_MODEL_SELECTED = "no advisor model selected";
|
|
@@ -73,15 +72,13 @@ const ERR_ABORTED_DETAIL = "aborted";
|
|
|
73
72
|
const ERR_UNKNOWN = "unknown error";
|
|
74
73
|
|
|
75
74
|
// Errors/messages (parameterized)
|
|
76
|
-
const errMisconfigured = (label: string, err: string) =>
|
|
77
|
-
`Advisor (${label}) is misconfigured: ${err}`;
|
|
75
|
+
const errMisconfigured = (label: string, err: string) => `Advisor (${label}) is misconfigured: ${err}`;
|
|
78
76
|
const errNoApiKey = (label: string) => `Advisor (${label}) has no API key available.`;
|
|
79
77
|
const errNoApiKeyDetail = (provider: string) => `no API key for ${provider}`;
|
|
80
78
|
const errCallFailed = (err: string | undefined) => `Advisor call failed: ${err ?? ERR_UNKNOWN}`;
|
|
81
79
|
const errCallThrew = (msg: string) => `Advisor call threw: ${msg}`;
|
|
82
80
|
const errSelectionNotFound = (choice: string) => `Advisor selection not found: ${choice}`;
|
|
83
|
-
const errModelUnavailable = (key: string) =>
|
|
84
|
-
`Previously configured advisor model ${key} is no longer available`;
|
|
81
|
+
const errModelUnavailable = (key: string) => `Previously configured advisor model ${key} is no longer available`;
|
|
85
82
|
const msgAdvisorEnabled = (label: string, effort: ThinkingLevel | undefined) =>
|
|
86
83
|
`Advisor: ${label}${effort ? `, ${effort}` : ""}`;
|
|
87
84
|
const msgAdvisorRestored = (label: string, effort: ThinkingLevel | undefined) =>
|
|
@@ -113,7 +110,7 @@ function saveAdvisorConfig(key: string | undefined, effort: ThinkingLevel | unde
|
|
|
113
110
|
if (effort) config.effort = effort;
|
|
114
111
|
try {
|
|
115
112
|
mkdirSync(dirname(ADVISOR_CONFIG_PATH), { recursive: true });
|
|
116
|
-
writeFileSync(ADVISOR_CONFIG_PATH, JSON.stringify(config, null, 2)
|
|
113
|
+
writeFileSync(ADVISOR_CONFIG_PATH, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
|
|
117
114
|
} catch {
|
|
118
115
|
// write may fail on disk-full or permission errors — best effort only
|
|
119
116
|
}
|
|
@@ -139,6 +136,97 @@ export const ADVISOR_SYSTEM_PROMPT = readFileSync(
|
|
|
139
136
|
"utf-8",
|
|
140
137
|
).trimEnd();
|
|
141
138
|
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
// Inventory state + serializer — stable tool-inventory Message for cache parity
|
|
141
|
+
//
|
|
142
|
+
// globalThis-keyed to survive module re-import on /new, /fork, /resume (mirrors
|
|
143
|
+
// rpiv-btw/btw.ts:37, 87-98). Single-slot cache — the Pi tool registry is
|
|
144
|
+
// process-scoped, so per-session keying would be redundant. Cache invalidates
|
|
145
|
+
// only when the set of registered tool names changes.
|
|
146
|
+
// ---------------------------------------------------------------------------
|
|
147
|
+
|
|
148
|
+
const ADVISOR_STATE_KEY = Symbol.for("rpiv-advisor");
|
|
149
|
+
|
|
150
|
+
interface AdvisorState {
|
|
151
|
+
inventorySignature?: string;
|
|
152
|
+
inventoryMessage?: Message;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function getAdvisorRuntimeState(): AdvisorState {
|
|
156
|
+
const g = globalThis as unknown as { [k: symbol]: AdvisorState | undefined };
|
|
157
|
+
let state = g[ADVISOR_STATE_KEY];
|
|
158
|
+
if (!state) {
|
|
159
|
+
state = {};
|
|
160
|
+
g[ADVISOR_STATE_KEY] = state;
|
|
161
|
+
}
|
|
162
|
+
return state;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Recursive key-sorted JSON serializer — matches JSON.stringify semantics
|
|
166
|
+
// (drops `undefined` in objects, emits `null` for `undefined` in arrays) but
|
|
167
|
+
// guarantees stable key ordering across V8 insertion-order variation. Required
|
|
168
|
+
// because nested TypeBox schemas may be authored in any order, and prompt
|
|
169
|
+
// caching is byte-sensitive.
|
|
170
|
+
function stableStringify(value: unknown): string {
|
|
171
|
+
if (value === null || typeof value !== "object") {
|
|
172
|
+
return JSON.stringify(value);
|
|
173
|
+
}
|
|
174
|
+
if (Array.isArray(value)) {
|
|
175
|
+
return `[${value.map((v) => (v === undefined ? "null" : stableStringify(v))).join(",")}]`;
|
|
176
|
+
}
|
|
177
|
+
const obj = value as Record<string, unknown>;
|
|
178
|
+
const entries: string[] = [];
|
|
179
|
+
for (const k of Object.keys(obj).sort()) {
|
|
180
|
+
const v = obj[k];
|
|
181
|
+
if (v === undefined) continue;
|
|
182
|
+
entries.push(`${JSON.stringify(k)}:${stableStringify(v)}`);
|
|
183
|
+
}
|
|
184
|
+
return `{${entries.join(",")}}`;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function buildInventoryBlock(tools: ToolInfo[]): string {
|
|
188
|
+
// Omit `sourceInfo` — its `path` field is install-location-dependent and
|
|
189
|
+
// would bust cache parity across machines/reinstalls.
|
|
190
|
+
return tools
|
|
191
|
+
.map((t) => `### ${t.name}\n${t.description}\n\nParameters: ${stableStringify(t.parameters)}`)
|
|
192
|
+
.join("\n\n---\n\n");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Strip the executor's in-flight advisor() toolCall from the tail assistant
|
|
196
|
+
// message. That call is what invoked *us* — there is no matching toolResult
|
|
197
|
+
// yet, and providers (Anthropic, GLM/zai, OpenAI) reject payloads with orphan
|
|
198
|
+
// toolCalls. Name-targeted to leave any other trailing toolCalls visible.
|
|
199
|
+
function stripInflightAdvisorCall(messages: Message[]): Message[] {
|
|
200
|
+
if (messages.length === 0) return messages;
|
|
201
|
+
const last = messages[messages.length - 1];
|
|
202
|
+
if (last.role !== "assistant") return messages;
|
|
203
|
+
const filtered = last.content.filter((c) => !(c.type === "toolCall" && c.name === ADVISOR_TOOL_NAME));
|
|
204
|
+
if (filtered.length === last.content.length) return messages;
|
|
205
|
+
if (filtered.length === 0) return messages.slice(0, -1);
|
|
206
|
+
return [...messages.slice(0, -1), { ...last, content: filtered }];
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
// Returns `undefined` when the registry is empty (no extensions loaded) so
|
|
210
|
+
// callers can skip prepending an empty block that would still cost a cache unit.
|
|
211
|
+
export function getInventoryMessage(tools: ToolInfo[]): Message | undefined {
|
|
212
|
+
if (tools.length === 0) return undefined;
|
|
213
|
+
const sorted = [...tools].sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
|
|
214
|
+
const signature = sorted.map((t) => t.name).join("|");
|
|
215
|
+
const state = getAdvisorRuntimeState();
|
|
216
|
+
if (state.inventorySignature === signature && state.inventoryMessage) {
|
|
217
|
+
return state.inventoryMessage;
|
|
218
|
+
}
|
|
219
|
+
const text = `## Available Executor Tools\n\n${buildInventoryBlock(sorted)}`;
|
|
220
|
+
const message: Message = {
|
|
221
|
+
role: "user",
|
|
222
|
+
content: [{ type: "text", text }],
|
|
223
|
+
timestamp: Date.now(),
|
|
224
|
+
};
|
|
225
|
+
state.inventorySignature = signature;
|
|
226
|
+
state.inventoryMessage = message;
|
|
227
|
+
return message;
|
|
228
|
+
}
|
|
229
|
+
|
|
142
230
|
// ---------------------------------------------------------------------------
|
|
143
231
|
// Module state — in-memory, resets each session
|
|
144
232
|
// ---------------------------------------------------------------------------
|
|
@@ -216,14 +304,13 @@ function buildErrorResult(
|
|
|
216
304
|
const effort = getAdvisorEffort();
|
|
217
305
|
return {
|
|
218
306
|
content: [{ type: "text", text: userText }],
|
|
219
|
-
details: advisorLabel
|
|
220
|
-
? { advisorModel: advisorLabel, effort, errorMessage }
|
|
221
|
-
: { effort, errorMessage },
|
|
307
|
+
details: advisorLabel ? { advisorModel: advisorLabel, effort, errorMessage } : { effort, errorMessage },
|
|
222
308
|
};
|
|
223
309
|
}
|
|
224
310
|
|
|
225
311
|
async function executeAdvisor(
|
|
226
312
|
ctx: ExtensionContext,
|
|
313
|
+
pi: ExtensionAPI,
|
|
227
314
|
signal: AbortSignal | undefined,
|
|
228
315
|
onUpdate: AgentToolUpdateCallback<AdvisorDetails> | undefined,
|
|
229
316
|
): Promise<AgentToolResult<AdvisorDetails>> {
|
|
@@ -239,29 +326,20 @@ async function executeAdvisor(
|
|
|
239
326
|
return buildErrorResult(advisorLabel, errMisconfigured(advisorLabel, auth.error), auth.error);
|
|
240
327
|
}
|
|
241
328
|
if (!auth.apiKey) {
|
|
242
|
-
return buildErrorResult(
|
|
243
|
-
advisorLabel,
|
|
244
|
-
errNoApiKey(advisorLabel),
|
|
245
|
-
errNoApiKeyDetail(advisor.provider),
|
|
246
|
-
);
|
|
329
|
+
return buildErrorResult(advisorLabel, errNoApiKey(advisorLabel), errNoApiKeyDetail(advisor.provider));
|
|
247
330
|
}
|
|
248
331
|
|
|
332
|
+
// Live-read every call — advisor runs mid-turn so any message_end snapshot
|
|
333
|
+
// is always one turn stale. convertToLlm is pass-through for user/assistant/
|
|
334
|
+
// toolResult (messages.js:111-114), so element refs are stable across calls
|
|
335
|
+
// via the session store — content-stable output without a snapshot layer.
|
|
249
336
|
const branch = ctx.sessionManager.getBranch();
|
|
250
337
|
const agentMessages = branch
|
|
251
338
|
.filter((e): e is SessionEntry & { type: "message" } => e.type === "message")
|
|
252
339
|
.map((e) => e.message);
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
const
|
|
256
|
-
role: "user",
|
|
257
|
-
content: [
|
|
258
|
-
{
|
|
259
|
-
type: "text",
|
|
260
|
-
text: `## Conversation So Far\n\n${conversationText}`,
|
|
261
|
-
},
|
|
262
|
-
],
|
|
263
|
-
timestamp: Date.now(),
|
|
264
|
-
};
|
|
340
|
+
const branchMessages = stripInflightAdvisorCall(convertToLlm(agentMessages));
|
|
341
|
+
const inventoryMessage = getInventoryMessage(pi.getAllTools());
|
|
342
|
+
const messages: Message[] = inventoryMessage ? [inventoryMessage, ...branchMessages] : branchMessages;
|
|
265
343
|
|
|
266
344
|
onUpdate?.({
|
|
267
345
|
content: [{ type: "text", text: msgConsulting(advisorLabel, effort) }],
|
|
@@ -271,7 +349,9 @@ async function executeAdvisor(
|
|
|
271
349
|
try {
|
|
272
350
|
const response = await completeSimple(
|
|
273
351
|
advisor,
|
|
274
|
-
|
|
352
|
+
// `tools: []` reaffirms the "never calls tools" contract even when
|
|
353
|
+
// `messages` contains prior toolCall/toolResult blocks (btw.ts:235).
|
|
354
|
+
{ systemPrompt: ADVISOR_SYSTEM_PROMPT, messages, tools: [] },
|
|
275
355
|
{ apiKey: auth.apiKey, headers: auth.headers, signal, reasoning: effort },
|
|
276
356
|
);
|
|
277
357
|
|
|
@@ -371,7 +451,7 @@ export function registerAdvisorTool(pi: ExtensionAPI): void {
|
|
|
371
451
|
parameters: AdvisorParams,
|
|
372
452
|
|
|
373
453
|
async execute(_toolCallId, _params, signal, onUpdate, ctx) {
|
|
374
|
-
return executeAdvisor(ctx, signal, onUpdate);
|
|
454
|
+
return executeAdvisor(ctx, pi, signal, onUpdate);
|
|
375
455
|
},
|
|
376
456
|
});
|
|
377
457
|
}
|
|
@@ -435,9 +515,7 @@ export function registerAdvisorCommand(pi: ExtensionAPI): void {
|
|
|
435
515
|
setAdvisorEffort(undefined);
|
|
436
516
|
saveAdvisorConfig(undefined, undefined);
|
|
437
517
|
if (activeHas) {
|
|
438
|
-
pi.setActiveTools(
|
|
439
|
-
activeTools.filter((n) => n !== ADVISOR_TOOL_NAME),
|
|
440
|
-
);
|
|
518
|
+
pi.setActiveTools(activeTools.filter((n) => n !== ADVISOR_TOOL_NAME));
|
|
441
519
|
}
|
|
442
520
|
ctx.ui.notify(MSG_ADVISOR_DISABLED, "info");
|
|
443
521
|
return;
|
|
@@ -452,9 +530,7 @@ export function registerAdvisorCommand(pi: ExtensionAPI): void {
|
|
|
452
530
|
// Effort picker — only for reasoning-capable models
|
|
453
531
|
let effortChoice: ThinkingLevel | undefined;
|
|
454
532
|
if (picked.reasoning) {
|
|
455
|
-
const levels = supportsXhigh(picked)
|
|
456
|
-
? [...BASE_EFFORT_LEVELS, XHIGH_EFFORT_LEVEL]
|
|
457
|
-
: BASE_EFFORT_LEVELS;
|
|
533
|
+
const levels = supportsXhigh(picked) ? [...BASE_EFFORT_LEVELS, XHIGH_EFFORT_LEVEL] : BASE_EFFORT_LEVELS;
|
|
458
534
|
|
|
459
535
|
const effortItems: SelectItem[] = [
|
|
460
536
|
{ value: OFF_VALUE, label: "off" },
|
|
@@ -464,12 +540,7 @@ export function registerAdvisorCommand(pi: ExtensionAPI): void {
|
|
|
464
540
|
})),
|
|
465
541
|
];
|
|
466
542
|
|
|
467
|
-
const effortResult = await showEffortPicker(
|
|
468
|
-
ctx,
|
|
469
|
-
effortItems,
|
|
470
|
-
getAdvisorEffort(),
|
|
471
|
-
DEFAULT_EFFORT,
|
|
472
|
-
);
|
|
543
|
+
const effortResult = await showEffortPicker(ctx, effortItems, getAdvisorEffort(), DEFAULT_EFFORT);
|
|
473
544
|
if (!effortResult) {
|
|
474
545
|
return;
|
|
475
546
|
}
|
package/index.ts
CHANGED
|
@@ -11,18 +11,18 @@
|
|
|
11
11
|
|
|
12
12
|
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
13
13
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
registerAdvisorBeforeAgentStart,
|
|
15
|
+
registerAdvisorCommand,
|
|
16
|
+
registerAdvisorTool,
|
|
17
|
+
restoreAdvisorState,
|
|
18
18
|
} from "./advisor.js";
|
|
19
19
|
|
|
20
20
|
export default function (pi: ExtensionAPI) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
registerAdvisorTool(pi);
|
|
22
|
+
registerAdvisorCommand(pi);
|
|
23
|
+
registerAdvisorBeforeAgentStart(pi);
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
26
|
+
restoreAdvisorState(ctx, pi);
|
|
27
|
+
});
|
|
28
28
|
}
|
package/package.json
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@juicesharp/rpiv-advisor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Pi extension: advisor-strategy pattern — escalate to a stronger reviewer model",
|
|
5
|
-
"keywords": [
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pi-package",
|
|
7
|
+
"pi-extension",
|
|
8
|
+
"rpiv",
|
|
9
|
+
"advisor"
|
|
10
|
+
],
|
|
6
11
|
"type": "module",
|
|
7
12
|
"license": "MIT",
|
|
8
13
|
"author": "juicesharp",
|
|
9
14
|
"repository": {
|
|
10
15
|
"type": "git",
|
|
11
|
-
"url": "git+https://github.com/juicesharp/rpiv-
|
|
16
|
+
"url": "git+https://github.com/juicesharp/rpiv-mono.git",
|
|
17
|
+
"directory": "packages/rpiv-advisor"
|
|
12
18
|
},
|
|
13
|
-
"homepage": "https://github.com/juicesharp/rpiv-advisor#readme",
|
|
19
|
+
"homepage": "https://github.com/juicesharp/rpiv-mono/tree/main/packages/rpiv-advisor#readme",
|
|
14
20
|
"bugs": {
|
|
15
|
-
"url": "https://github.com/juicesharp/rpiv-
|
|
21
|
+
"url": "https://github.com/juicesharp/rpiv-mono/issues"
|
|
16
22
|
},
|
|
17
23
|
"publishConfig": {
|
|
18
24
|
"access": "public"
|
|
@@ -22,10 +28,13 @@
|
|
|
22
28
|
"advisor.ts",
|
|
23
29
|
"advisor-ui.ts",
|
|
24
30
|
"prompts/",
|
|
25
|
-
"README.md"
|
|
31
|
+
"README.md",
|
|
32
|
+
"LICENSE"
|
|
26
33
|
],
|
|
27
34
|
"pi": {
|
|
28
|
-
"extensions": [
|
|
35
|
+
"extensions": [
|
|
36
|
+
"./index.ts"
|
|
37
|
+
]
|
|
29
38
|
},
|
|
30
39
|
"peerDependencies": {
|
|
31
40
|
"@mariozechner/pi-ai": "*",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
You are an advisor model in an advisor-strategy pattern. An executor model is running a task end-to-end — calling tools, reading results, iterating toward a solution. When the executor hits a decision it cannot reasonably solve alone, it consults you for guidance.
|
|
1
|
+
You are an advisor model in an advisor-strategy pattern. An executor model is running a task end-to-end — calling tools, reading results, iterating toward a solution. When the executor hits a decision it cannot reasonably solve alone, it consults you for guidance. The executor's full tool inventory is prepended before the conversation so you can judge tool-choice correctness.
|
|
2
2
|
|
|
3
3
|
You read the shared conversation context and return ONE of:
|
|
4
4
|
- a plan (concrete next steps the executor should take),
|