@hk_net/pi-advisor 0.1.7 → 0.1.9

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/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.9 - 2026-08-23
4
+
5
+ ### Changed
6
+
7
+ - Add the copyright holder and EUPL licensing notice to the package license and canonical TypeScript source.
8
+ - Identify KAPPER NETWORK-COMMUNICATIONS GmbH and kapper.net in the npm author metadata.
9
+
10
+ ## 0.1.8 - 2026-08-23
11
+
12
+ ### Changed
13
+
14
+ - Mark Pi's host-provided SDK packages as optional wildcard peers so npm does not install a redundant Pi SDK dependency tree.
15
+ - Verify development and tests against Pi SDK 0.84.2 while retaining runtime compatibility with Pi 0.84.1 and newer.
16
+ - Refine advisor tool guidance so non-trivial tasks require a review at an evidence-backed checkpoint before the final answer without encouraging an immediate first-action call.
17
+ - Make reviewer responses checkpoint-aware, evidence-grounded, and limited to a verdict plus three prioritized actions.
18
+ - Prevent `/advisor` from writing project-scoped configuration until Pi trusts the project.
19
+ - Run `onDone` review only after Pi fully settles retries, compaction recovery, and queued continuations.
20
+ - Write advisor configuration atomically with owner-only file permissions where supported.
21
+ - Document that the individual package and GitHub bundle must not be installed together.
22
+
3
23
  ## 0.1.7 - 2026-08-13
4
24
 
5
25
  ### Changed
package/LICENSE CHANGED
@@ -1,3 +1,6 @@
1
+ Copyright © 2026 kapper.net - KAPPER NETWORK-COMMUNICATIONS GmbH
2
+ Licensed under the EUPL
3
+
1
4
  EUROPEAN UNION PUBLIC LICENCE v. 1.2
2
5
  EUPL © the European Union 2007, 2016
3
6
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # pi-advisor
2
2
 
3
- A [pi](https://www.npmjs.com/package/@earendil-works/pi-coding-agent) extension for pi `>=0.84.1` that offers a
3
+ A [Pi](https://www.npmjs.com/package/@earendil-works/pi-coding-agent) extension for Pi 0.84.1 or newer (tested with the current Pi 0.84.2 release) that offers a
4
4
  parameterless `advisor` tool — inspired by Claude Code's advisor, but expanded with additional
5
5
  nudges and a manual review procedure. The regular model calls `advisor` to get a second opinion
6
6
  from an explicitly configured **stronger reviewer model** that sees the *entire* conversation transcript. Beyond the
@@ -11,24 +11,33 @@ or finishes a task, and the human can invoke a manual review via `/advise`.
11
11
 
12
12
  When the model calls `advisor` (no arguments), the extension serializes the full active branch —
13
13
  user/assistant text, assistant **reasoning**, every **tool call (with args)** and its **result** —
14
- and forwards it to a stronger reviewer model with a reviewer system prompt. The reviewer returns
15
- direct, actionable advice (flag wrong assumptions, name what's likely to bite, scrutinize "I'm
16
- done" claims). Individual tool-call arguments are truncated at 800 characters and tool results at
14
+ and forwards it to a stronger reviewer model with a reviewer system prompt. The reviewer infers whether it is assessing a plan,
15
+ a concrete failure, or claimed completion; checks the work against user and repository instructions
16
+ and primary-source evidence; and returns a verdict plus at most three prioritized actions. A
17
+ premature consultation asks for the specific missing evidence instead of inventing a verdict.
18
+ Individual tool-call arguments are truncated at 800 characters and tool results at
17
19
  2,000 characters so huge outputs do not dominate the review; after that, the transcript is truncated
18
20
  oldest-first only if it would overflow the reviewer's context window.
19
21
 
20
22
  Beyond the callable tool, the extension provides two additional ways to get advice:
21
23
 
22
- - **Automatic nudges** — the tool's `promptGuidelines` encourage the model to call advisor
23
- *before substantive work*, *when stuck*, and *when it believes the task is complete*. Two
24
- opt-in deterministic triggers (`onDone`, `whenStuck`) go further: they auto-consult the
25
- reviewer and inject the feedback directly into the conversation so the agent sees it.
24
+ - **Automatic nudges** — the tool's `promptGuidelines` tell the model to call advisor at a
25
+ useful checkpoint in every non-trivial task: after enough investigation to form an
26
+ evidence-backed approach and before a consequential edit, when stuck, or after implementation
27
+ and verification before claiming completion. A mandatory pre-final check tells the model not to
28
+ finish non-trivial work unless it has called advisor at least once after gathering evidence.
29
+ The guidance explicitly prohibits an advisor call as the first action or while there is no
30
+ substantive evidence or work to review. Two opt-in
31
+ deterministic triggers (`onDone`, `whenStuck`) go further: they auto-consult the reviewer and
32
+ inject the feedback directly into the conversation so the agent sees it.
26
33
  - **Manual review via `/advise`** — the human can invoke a one-off review at any time. Feedback
27
34
  can be shown UI-only (informal, for the human), piped into the chat as a user message, or
28
35
  injected as a steering message so the running agent sees and acts on it.
29
36
 
30
37
  ## Files & install
31
38
 
39
+ > **Avoid duplicate installation.** Install this npm package or the GitHub bundle, not both. Loading both copies can duplicate tools, commands, and automatic event handlers.
40
+
32
41
  - `advisor.ts` — canonical source (this repo).
33
42
  - Package-installed copy: loaded from this package's `pi.extensions` manifest after `pi install`.
34
43
 
@@ -118,8 +127,9 @@ Autocomplete completes `/adviso...` to `/advisor` without Pi's trailing-space in
118
127
  Default **off** — out of the box the regular model decides when to call `advisor`, nudged by the
119
128
  tool's `promptGuidelines`. Two opt-in deterministic triggers, configurable per project and global:
120
129
 
121
- - **`onDone`** — on `agent_end`, auto-review and steer one follow-up so the agent addresses any
122
- issues before truly stopping (guarded to at most once per user prompt).
130
+ - **`onDone`** — on `agent_settled`, after automatic retries, compaction recovery, and queued
131
+ continuations finish, auto-review and steer one follow-up so the agent addresses any issues
132
+ before truly stopping (guarded to at most once per user prompt).
123
133
  - **`whenStuck: N`** — after N consecutive tool errors **or** N repeated identical tool calls
124
134
  (same tool name + same arguments), auto-consult the reviewer and inject the advice as a
125
135
  steering message to get unstuck.
@@ -131,7 +141,8 @@ tool's `promptGuidelines`. Two opt-in deterministic triggers, configurable per p
131
141
  another model.
132
142
  - **Project config follows pi project trust.** A global install ignores `<cwd>/.pi/advisor.json`
133
143
  while the current project is untrusted, so an untrusted checkout cannot silently choose a reviewer
134
- model or enable auto-triggers.
144
+ model or enable auto-triggers. `/advisor` also withholds the project scope from its configuration
145
+ picker until the project is trusted; global configuration remains available.
135
146
  - **Auto-triggers are off by default.** The `onDone` and `whenStuck` features must be explicitly
136
147
  enabled in configuration, and they do nothing unless a reviewer model is configured.
137
148
  - **Data sent to the reviewer model.** When `advisor` is called, the extension sends the full
package/advisor.ts CHANGED
@@ -1,3 +1,6 @@
1
+ // Copyright © 2026 kapper.net - KAPPER NETWORK-COMMUNICATIONS GmbH
2
+ // SPDX-License-Identifier: EUPL-1.2
3
+
1
4
  /**
2
5
  * advisor.ts — a pi extension inspired by Claude Code's `advisor` tool, expanded
3
6
  * with automatic triggers and a human-invoked manual review command.
@@ -44,7 +47,7 @@ import { getSupportedThinkingLevels, Type } from "@earendil-works/pi-ai";
44
47
  import type { Api, Model, ModelThinkingLevel, ProviderHeaders } from "@earendil-works/pi-ai";
45
48
  import { complete } from "@earendil-works/pi-ai/compat";
46
49
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
47
- import { getMarkdownTheme } from "@earendil-works/pi-coding-agent";
50
+ import { CONFIG_DIR_NAME, getMarkdownTheme } from "@earendil-works/pi-coding-agent";
48
51
  import { Box, Markdown, SelectList, truncateToWidth } from "@earendil-works/pi-tui";
49
52
  import type { AutocompleteItem, Component } from "@earendil-works/pi-tui";
50
53
  import * as fs from "node:fs";
@@ -193,19 +196,28 @@ tool outputs, file contents, command output, or other transcript excerpts unless
193
196
  are directly relevant to reviewing the coding agent's work. Do not quote secrets or
194
197
  credentials unless strictly necessary to identify a concrete issue.
195
198
 
199
+ First infer the checkpoint: evidence gathering or plan review, recovery from a
200
+ concrete failure, or completion review. Judge the work appropriate to that checkpoint.
201
+ If the transcript is too early to support a useful review, say that plainly and name
202
+ the specific evidence the agent should gather next; do not invent a verdict.
203
+
196
204
  Give direct, high-signal advice. Specifically:
205
+ - Check the user's actual goal, repository instructions, primary-source API evidence,
206
+ and existing project patterns before endorsing the approach.
197
207
  - If the agent is about to build on a wrong assumption, a misread of a file, or a
198
208
  flawed interpretation of the request, say so plainly and point at the evidence.
199
209
  - If the approach is sound, confirm it and name the one or two things most likely to
200
210
  bite — edge cases, missed requirements, or unverified claims.
201
- - If the agent thinks it is done, scrutinize that: is there a requirement left unmet,
202
- a claim asserted but not verified, a test that doesn't actually test the change?
211
+ - If the agent thinks it is done, scrutinize the actual changes and verification: is
212
+ a requirement left unmet, a claim unverified, or a test not exercising the behavior?
213
+ Do not manufacture unrelated scope or demand changes unsupported by the task.
203
214
  - Prefer concrete next actions over generic best-practice lectures. Cite specific
204
- files, functions, or transcript moments.
215
+ files, functions, commands, or transcript moments.
205
216
 
206
- Be concise and decisive. You are the more capable model in the room — act like it.
207
- Do not restate the transcript back; the agent already has it. Lead with your verdict.
208
- Always return your advice as visible assistant text. Do not return reasoning-only output.`;
217
+ Be concise and decisive. Lead with a one-sentence verdict, followed by at most three
218
+ prioritized actions. If no corrective action is needed, say the work is ready and name
219
+ only any final verification still warranted. Do not restate the transcript back; the
220
+ agent already has it. Always return visible assistant text, never reasoning-only output.`;
209
221
 
210
222
  // ── Config files ────────────────────────────────────────────────────────────
211
223
 
@@ -218,7 +230,14 @@ type AdvisorConfig = {
218
230
  };
219
231
 
220
232
  const globalConfigPath = () => path.join(os.homedir(), ".pi", "agent", "advisor.json");
221
- const projectConfigPath = (cwd: string) => path.join(cwd, ".pi", "advisor.json");
233
+ const projectConfigPath = (cwd: string) => path.join(cwd, CONFIG_DIR_NAME, "advisor.json");
234
+
235
+ const PROJECT_SCOPE = "This folder (project)";
236
+ const GLOBAL_SCOPE = "Global (all projects)";
237
+
238
+ export function getAdvisorScopeChoices(projectTrusted: boolean): string[] {
239
+ return projectTrusted ? [PROJECT_SCOPE, GLOBAL_SCOPE] : [GLOBAL_SCOPE];
240
+ }
222
241
 
223
242
  export function validateAdvisorConfig(raw: unknown, source = "advisor config"): AdvisorConfig {
224
243
  const warn = (message: string) => console.warn(`[pi-advisor] Ignoring invalid ${source}: ${message}`);
@@ -282,7 +301,14 @@ function writeConfig(file: string, cfg: AdvisorConfig): void {
282
301
  if (cfg.onDone !== undefined) clean.onDone = cfg.onDone;
283
302
  if (cfg.whenStuck !== undefined) clean.whenStuck = cfg.whenStuck;
284
303
  if (cfg.timeoutMs !== undefined) clean.timeoutMs = cfg.timeoutMs;
285
- fs.writeFileSync(file, JSON.stringify(clean, null, 2) + "\n", "utf-8");
304
+
305
+ const temporaryFile = `${file}.tmp-${process.pid}-${Date.now()}`;
306
+ try {
307
+ fs.writeFileSync(temporaryFile, JSON.stringify(clean, null, 2) + "\n", { encoding: "utf-8", mode: 0o600 });
308
+ fs.renameSync(temporaryFile, file);
309
+ } finally {
310
+ fs.rmSync(temporaryFile, { force: true });
311
+ }
286
312
  }
287
313
 
288
314
  // ── Resolution ──────────────────────────────────────────────────────────────
@@ -797,14 +823,16 @@ export default function advisorExtension(pi: ExtensionAPI) {
797
823
  label: "Advisor",
798
824
  description:
799
825
  "Consult a configured stronger reviewer model that sees your full conversation transcript. " +
800
- "Takes NO parameters if a reviewer model is configured, the entire active conversation " +
801
- "(your task, reasoning, every tool call and result) is forwarded automatically. " +
802
- "If no reviewer model is configured, this sends nothing and returns setup guidance. " +
803
- "Returns direct, actionable advice.",
804
- promptSnippet: "Consult a configured stronger reviewer model on the full transcript before/after substantive work",
826
+ "Use it only after the transcript contains substantive investigation, an evidence-backed plan, " +
827
+ "implementation work, or a concrete failure to review; do not call it as the first action. " +
828
+ "Takes NO parameters if configured, the entire active conversation (task, reasoning, tool calls, " +
829
+ "and results) is forwarded automatically. If unconfigured, it sends nothing and returns setup guidance.",
830
+ promptSnippet: "Mandatory evidence-based review checkpoint for non-trivial work; never the first action",
805
831
  promptGuidelines: [
806
- "Call advisor before substantive work (before writing, before committing to an interpretation or assumption), when stuck (errors recurring, approach not converging), and when you believe the task is complete.",
807
- "advisor takes no arguments; if no reviewer model is configured, it sends nothing and returns setup guidance. Otherwise it forwards the whole conversation. Give its advice serious weight, but if a concrete step it suggests fails empirically or contradicts primary-source evidence you hold, adapt rather than follow blindly.",
832
+ "For every non-trivial task, you MUST call advisor at a useful checkpoint: after investigating enough context to form an evidence-backed approach and before the first consequential edit, when concrete failures recur or the approach stops converging, or after implementation and verification before claiming completion.",
833
+ "Before giving a final answer on any non-trivial task, verify that you called advisor at least once after gathering substantive evidence. If you have not, call advisor now and incorporate its result; do not finish the task without this review.",
834
+ "Never call advisor as the first action, merely to restate the user's request, or while the transcript contains no substantive evidence or work for a reviewer to assess. Trivial questions and mechanical edits do not require advisor.",
835
+ "advisor takes no arguments; if no reviewer model is configured, it sends nothing and returns setup guidance. Otherwise it forwards the whole conversation. Give its advice serious weight, but adapt when a suggestion fails empirically or contradicts primary-source evidence.",
808
836
  ],
809
837
  parameters: Type.Object({}),
810
838
  async execute(_toolCallId, _params, signal, onUpdate, ctx) {
@@ -871,8 +899,9 @@ export default function advisorExtension(pi: ExtensionAPI) {
871
899
  }
872
900
  });
873
901
 
874
- // "On done": when the agent finishes, review and (if enabled) steer one follow-up.
875
- pi.on("agent_end", async (_event, ctx) => {
902
+ // "On done": after retries, compaction recovery, and queued continuations have
903
+ // fully settled, review and (if enabled) steer one follow-up.
904
+ pi.on("agent_settled", async (_event, ctx) => {
876
905
  const projectTrusted = contextProjectTrusted(ctx);
877
906
  const { onDone } = effectiveTriggers(ctx.cwd, projectTrusted);
878
907
  if (!onDone || autoReviewedThisRound || autoRunning || isDisabled(ctx.cwd, projectTrusted) || isUnconfigured(ctx.cwd, projectTrusted)) return;
@@ -996,11 +1025,13 @@ export default function advisorExtension(pi: ExtensionAPI) {
996
1025
  }
997
1026
 
998
1027
  const pickScope = async (): Promise<string | undefined> => {
999
- const PROJECT_OPT = "This folder (project)";
1000
- const GLOBAL_OPT = "Global (all projects)";
1001
- const scope = await ctx.ui.select("Apply to", [PROJECT_OPT, GLOBAL_OPT]);
1028
+ const projectTrusted = contextProjectTrusted(ctx);
1029
+ if (!projectTrusted) {
1030
+ ctx.ui.notify("Project advisor settings cannot be changed until this project is trusted; only global scope is available.", "warning");
1031
+ }
1032
+ const scope = await ctx.ui.select("Apply to", getAdvisorScopeChoices(projectTrusted));
1002
1033
  if (scope === undefined) return undefined;
1003
- return scope === PROJECT_OPT ? projectConfigPath(cwd) : globalConfigPath();
1034
+ return scope === PROJECT_SCOPE ? projectConfigPath(cwd) : globalConfigPath();
1004
1035
  };
1005
1036
  const persist = (file: string, patch: AdvisorConfig) => {
1006
1037
  writeConfig(file, { ...readConfig(file), ...patch });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hk_net/pi-advisor",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Pi advisor extension: consult a configured reviewer model on the full conversation transcript",
5
5
  "type": "module",
6
6
  "private": false,
@@ -11,7 +11,10 @@
11
11
  "advisor"
12
12
  ],
13
13
  "license": "EUPL-1.2",
14
- "author": "hknet",
14
+ "author": {
15
+ "name": "KAPPER NETWORK-COMMUNICATIONS GmbH",
16
+ "url": "https://kapper.net"
17
+ },
15
18
  "repository": {
16
19
  "type": "git",
17
20
  "url": "git+https://github.com/hknet/pi-extensions.git",
@@ -26,10 +29,24 @@
26
29
  "README.md",
27
30
  "CHANGELOG.md"
28
31
  ],
32
+ "engines": {
33
+ "node": ">=22.19.0"
34
+ },
29
35
  "peerDependencies": {
30
- "@earendil-works/pi-ai": ">=0.84.1",
31
- "@earendil-works/pi-coding-agent": ">=0.84.1",
32
- "@earendil-works/pi-tui": ">=0.84.1"
36
+ "@earendil-works/pi-ai": "*",
37
+ "@earendil-works/pi-coding-agent": "*",
38
+ "@earendil-works/pi-tui": "*"
39
+ },
40
+ "peerDependenciesMeta": {
41
+ "@earendil-works/pi-ai": {
42
+ "optional": true
43
+ },
44
+ "@earendil-works/pi-coding-agent": {
45
+ "optional": true
46
+ },
47
+ "@earendil-works/pi-tui": {
48
+ "optional": true
49
+ }
33
50
  },
34
51
  "pi": {
35
52
  "extensions": [