@juicesharp/rpiv-advisor 0.1.3 → 0.6.1

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 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
- ![Advisor model selector](https://raw.githubusercontent.com/juicesharp/rpiv-advisor/main/docs/advisor.jpg)
8
+ ![Advisor model selector](https://raw.githubusercontent.com/juicesharp/rpiv-mono/main/packages/rpiv-advisor/docs/advisor.jpg)
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 { Container, SelectList, Spacer, Text, type SelectItem } from "@mariozechner/pi-tui";
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
- items,
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
+ serializeConversation,
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) + "\n", "utf-8");
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
  }
@@ -216,9 +213,7 @@ function buildErrorResult(
216
213
  const effort = getAdvisorEffort();
217
214
  return {
218
215
  content: [{ type: "text", text: userText }],
219
- details: advisorLabel
220
- ? { advisorModel: advisorLabel, effort, errorMessage }
221
- : { effort, errorMessage },
216
+ details: advisorLabel ? { advisorModel: advisorLabel, effort, errorMessage } : { effort, errorMessage },
222
217
  };
223
218
  }
224
219
 
@@ -239,11 +234,7 @@ async function executeAdvisor(
239
234
  return buildErrorResult(advisorLabel, errMisconfigured(advisorLabel, auth.error), auth.error);
240
235
  }
241
236
  if (!auth.apiKey) {
242
- return buildErrorResult(
243
- advisorLabel,
244
- errNoApiKey(advisorLabel),
245
- errNoApiKeyDetail(advisor.provider),
246
- );
237
+ return buildErrorResult(advisorLabel, errNoApiKey(advisorLabel), errNoApiKeyDetail(advisor.provider));
247
238
  }
248
239
 
249
240
  const branch = ctx.sessionManager.getBranch();
@@ -435,9 +426,7 @@ export function registerAdvisorCommand(pi: ExtensionAPI): void {
435
426
  setAdvisorEffort(undefined);
436
427
  saveAdvisorConfig(undefined, undefined);
437
428
  if (activeHas) {
438
- pi.setActiveTools(
439
- activeTools.filter((n) => n !== ADVISOR_TOOL_NAME),
440
- );
429
+ pi.setActiveTools(activeTools.filter((n) => n !== ADVISOR_TOOL_NAME));
441
430
  }
442
431
  ctx.ui.notify(MSG_ADVISOR_DISABLED, "info");
443
432
  return;
@@ -452,9 +441,7 @@ export function registerAdvisorCommand(pi: ExtensionAPI): void {
452
441
  // Effort picker — only for reasoning-capable models
453
442
  let effortChoice: ThinkingLevel | undefined;
454
443
  if (picked.reasoning) {
455
- const levels = supportsXhigh(picked)
456
- ? [...BASE_EFFORT_LEVELS, XHIGH_EFFORT_LEVEL]
457
- : BASE_EFFORT_LEVELS;
444
+ const levels = supportsXhigh(picked) ? [...BASE_EFFORT_LEVELS, XHIGH_EFFORT_LEVEL] : BASE_EFFORT_LEVELS;
458
445
 
459
446
  const effortItems: SelectItem[] = [
460
447
  { value: OFF_VALUE, label: "off" },
@@ -464,12 +451,7 @@ export function registerAdvisorCommand(pi: ExtensionAPI): void {
464
451
  })),
465
452
  ];
466
453
 
467
- const effortResult = await showEffortPicker(
468
- ctx,
469
- effortItems,
470
- getAdvisorEffort(),
471
- DEFAULT_EFFORT,
472
- );
454
+ const effortResult = await showEffortPicker(ctx, effortItems, getAdvisorEffort(), DEFAULT_EFFORT);
473
455
  if (!effortResult) {
474
456
  return;
475
457
  }
package/index.ts CHANGED
@@ -11,18 +11,18 @@
11
11
 
12
12
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
13
13
  import {
14
- registerAdvisorTool,
15
- registerAdvisorCommand,
16
- registerAdvisorBeforeAgentStart,
17
- restoreAdvisorState,
14
+ registerAdvisorBeforeAgentStart,
15
+ registerAdvisorCommand,
16
+ registerAdvisorTool,
17
+ restoreAdvisorState,
18
18
  } from "./advisor.js";
19
19
 
20
20
  export default function (pi: ExtensionAPI) {
21
- registerAdvisorTool(pi);
22
- registerAdvisorCommand(pi);
23
- registerAdvisorBeforeAgentStart(pi);
21
+ registerAdvisorTool(pi);
22
+ registerAdvisorCommand(pi);
23
+ registerAdvisorBeforeAgentStart(pi);
24
24
 
25
- pi.on("session_start", async (_event, ctx) => {
26
- restoreAdvisorState(ctx, pi);
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.1.3",
3
+ "version": "0.6.1",
4
4
  "description": "Pi extension: advisor-strategy pattern — escalate to a stronger reviewer model",
5
- "keywords": ["pi-package", "pi-extension", "rpiv", "advisor"],
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-advisor.git"
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-advisor/issues"
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": ["./index.ts"]
35
+ "extensions": [
36
+ "./index.ts"
37
+ ]
29
38
  },
30
39
  "peerDependencies": {
31
40
  "@mariozechner/pi-ai": "*",