@ian-pascoe/pi-advisor 0.4.0 → 0.5.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 CHANGED
@@ -17,6 +17,7 @@ Advisor is **disabled by default**. Configuration precedence is session, trusted
17
17
  ## Commands
18
18
 
19
19
  ```text
20
+ /advisor
20
21
  /advisor status
21
22
  /advisor on|off [--global|--project]
22
23
  /advisor inherit [key] [--global|--project]
@@ -29,6 +30,12 @@ Advisor is **disabled by default**. Configuration precedence is session, trusted
29
30
 
30
31
  Argument autocomplete suggests command names, settings keys, and valid trailing scope flags.
31
32
 
33
+ In the interactive TUI, `/advisor` alone opens a settings menu built from Pi's native settings list, like `/settings`. A Scope row chooses where edits are written (session, trusted project, or global), and each setting shows its effective value with any non-default source. `enabled`, `includeSubagents`, and `thinkingLevel` cycle the selected scope's own value in place (`inherit → on → off`, or through each thinking level); while that scope has none, the row shows what it inherits, such as `inherit (on · project)`, and a value overridden by a higher-precedence scope is marked. Model, tools, numbers, and the prompt open submenus, and the prompt uses Pi's own editor component. Every edit is validated and applied immediately; a paused Advisor shows its reason and a Resume row. Closing the menu records one status entry listing the changes it applied, or nothing if none were. Without the TUI, `/advisor` records the status entry instead.
34
+
35
+ `/advisor status` and the other subcommands each record a status entry in the transcript. Collapsed, it shows state, model, backlog, usage and cost, unavailable tools, and the last error; expanded, it lists every setting with its source and each watched Child Agent. A mutating command's entry starts with the changes it applied. While enabled, the footer shows `advisor` when idle, `advisor: reviewing`/`consulting` with any `backlog N`, or `advisor: paused`, plus Child Agents that are reviewing or paused. `advisor_ask` renders its question and a Markdown answer preview, and Child Agent findings carry a `↳ <agentId>` label.
36
+
37
+ Interventions render with their severity and Advisor attribution. Long Nits collapse until expanded; Concerns and Blockers always show in full.
38
+
32
39
  ## On-demand consultations
33
40
 
34
41
  When Advisor is enabled, the main agent can call `advisor_ask({ message })` for analysis or a second opinion. The call waits for the current Review, then returns plain Markdown before any further Review Backlog is processed. Follow-up calls continue the same private Advisor Session.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ian-pascoe/pi-advisor",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "private": false,
5
5
  "description": "Background review and attributed corrective advice for Pi agent sessions.",
6
6
  "keywords": [
@@ -15,7 +15,7 @@ export function completeAdvisorCommandArguments(prefix: string): AutocompleteIte
15
15
  if (scopePrefix) {
16
16
  try {
17
17
  const command = parseAdvisorCommand(scopePrefix);
18
- if (command.action !== "status" && command.scope === "session")
18
+ if ("scope" in command && command.scope === "session")
19
19
  candidates.push(`${scopePrefix}--global`, `${scopePrefix}--project`);
20
20
  } catch {
21
21
  // Incomplete commands and JSON values cannot accept a scope yet.
@@ -27,7 +27,7 @@ export function completeAdvisorCommandArguments(prefix: string): AutocompleteIte
27
27
  }
28
28
 
29
29
  const usage =
30
- "Usage: /advisor [on|off|status|prompt|inherit [key]|set <key> <JSON>] [--global|--project]";
30
+ "Usage: /advisor [on|off|status|prompt|inherit [key]|set <key> <JSON>] [--global|--project]; /advisor alone opens settings";
31
31
 
32
32
  /** Parse one configuration change; validated patches cannot invent option keys. */
33
33
  export function parseAdvisorCommand(input: string) {
@@ -38,14 +38,19 @@ export function parseAdvisorCommand(input: string) {
38
38
  : flag?.[1] === "project"
39
39
  ? ("project" as const)
40
40
  : ("session" as const);
41
- const text = (flag ? input.trim().slice(0, flag.index) : input.trim()) || "status";
42
- if (text === "status") {
41
+ const text = flag ? input.trim().slice(0, flag.index) : input.trim();
42
+ if (text === "" || text === "status") {
43
43
  if (flag) throw new Error(usage);
44
- return { action: "status" as const };
44
+ return text === "" ? { action: "menu" as const } : { action: "status" as const };
45
45
  }
46
46
  if (text === "prompt") return { action: "prompt" as const, scope };
47
47
  if (text === "on" || text === "off") {
48
- return { action: "set" as const, scope, patch: { enabled: text === "on" } };
48
+ return {
49
+ action: "set" as const,
50
+ scope,
51
+ key: "enabled" as const,
52
+ patch: { enabled: text === "on" },
53
+ };
49
54
  }
50
55
  const inherit = /^inherit(?:\s+(\S+))?$/.exec(text);
51
56
  if (inherit) {
@@ -53,6 +58,7 @@ export function parseAdvisorCommand(input: string) {
53
58
  }
54
59
  const set = /^set\s+(\S+)\s+([\s\S]+)$/.exec(text);
55
60
  if (!set?.[1] || !set[2]) throw new Error(usage);
56
- const patch: AdvisorOptions = parseAdvisorOptions({ [set[1]]: JSON.parse(set[2]) }, scope);
57
- return { action: "set" as const, scope, patch };
61
+ const key = advisorOptionKey(set[1]);
62
+ const patch: AdvisorOptions = parseAdvisorOptions({ [key]: JSON.parse(set[2]) }, scope);
63
+ return { action: "set" as const, scope, key, patch };
58
64
  }
@@ -0,0 +1,33 @@
1
+ import { Type, type Static } from "typebox";
2
+
3
+ /** Intervention severities, in ascending urgency. */
4
+ export const advisorSeveritySchema = Type.Union([
5
+ Type.Literal("nit"),
6
+ Type.Literal("concern"),
7
+ Type.Literal("blocker"),
8
+ ]);
9
+ export type AdvisorSeverity = Static<typeof advisorSeveritySchema>;
10
+
11
+ /** One reported finding; also the journaled details of an Intervention message. */
12
+ export const advisorFindingSchema = Type.Object(
13
+ {
14
+ severity: advisorSeveritySchema,
15
+ message: Type.String({ minLength: 1, maxLength: 4000 }),
16
+ },
17
+ { additionalProperties: false },
18
+ );
19
+ export type AdvisorFinding = Static<typeof advisorFindingSchema>;
20
+
21
+ /** Observer lifecycle state reported by its `status`. */
22
+ export const advisorObserverStateSchema = Type.Union([
23
+ Type.Literal("disabled"),
24
+ Type.Literal("armed"),
25
+ Type.Literal("reviewing"),
26
+ Type.Literal("consulting"),
27
+ Type.Literal("paused"),
28
+ ]);
29
+ export type AdvisorObserverState = Static<typeof advisorObserverStateSchema>;
30
+
31
+ /** State recorded in status entries; private Advisor Sessions cannot host an Advisor. */
32
+ export const advisorStateSchema = Type.Union([advisorObserverStateSchema, Type.Literal("private")]);
33
+ export type AdvisorState = Static<typeof advisorStateSchema>;