@d3ara1n/pi-subagent 3.3.1 → 3.4.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
@@ -64,7 +64,7 @@ Observations on how main models behave with this plugin, one family per subsecti
64
64
 
65
65
  | Command | Description |
66
66
  |---------|-------------|
67
- | `/subagent:view` | Open the live view: a tabbed overlay with a per-run activity feed and a brief detail page (inputs, files, stats), plus modal steer input for the focused run — doubles as the session's run archive |
67
+ | `/subagent:view` | Open the live view: a tabbed overlay with a per-run activity feed and a brief detail page (inputs, files, stats), plus modal steer input for the focused run — doubles as the session's run archive. Also available as the native palette entry **Subagent: Activity View** (Ctrl+Shift+P via [`pi-command-palette`](../pi-command-palette)), which opens the view directly without needing an empty editor |
68
68
  | `/subagent:doctor` | Diagnose pi invocation, model-role resolution, configuration, and role references |
69
69
  | `/subagent:status` | List background runs and their current state |
70
70
  | `/subagent:cancel <id\|all> [reason]` | Cancel a live background run (or every live run); the optional reason is recorded with the run |
@@ -82,6 +82,7 @@ Steer input is modal so keys never conflict with typing: in browse mode `s` open
82
82
  ## Dependencies
83
83
 
84
84
  - [`@d3ara1n/pi-model-roles`](../pi-model-roles) — model role resolution
85
+ - [`@d3ara1n/pi-command-palette-core`](../pi-command-palette-core) — native command-palette registry (pure npm library, installed automatically; the palette entry appears when [`pi-command-palette`](../pi-command-palette) is installed)
85
86
 
86
87
  ## Installation
87
88
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@d3ara1n/pi-subagent",
3
- "version": "3.3.1",
3
+ "version": "3.4.0",
4
4
  "type": "module",
5
5
  "description": "Role-based subagent orchestration for pi — delegates tasks to specialized pi child processes with configurable model roles",
6
6
  "main": "src/index.ts",
@@ -27,6 +27,7 @@
27
27
  }
28
28
  },
29
29
  "dependencies": {
30
+ "@d3ara1n/pi-command-palette-core": "*",
30
31
  "@d3ara1n/pi-model-roles": "*"
31
32
  },
32
33
  "pi": {
@@ -42,11 +42,10 @@ test("queued items share the visible limit and consumption changes only the mark
42
42
  assert.equal(buildDisplayItems(log).length, 7);
43
43
  });
44
44
 
45
- test("queued rendering uses status for every display type and steers retain accent", () => {
45
+ test("queued rendering uses status for every display type and steer color follows delivery status", () => {
46
46
  const color = (name: string, text: string) => `<${name}>${text}</${name}>`;
47
- for (const status of ["queued", "done"] as const) {
48
- assert.match(formatDisplayItem({ type: "steer", status, text: "input" }, color), /^<accent>/);
49
- }
47
+ assert.match(formatDisplayItem({ type: "steer", status: "queued", text: "input" }, color), /^<accent>/);
48
+ assert.match(formatDisplayItem({ type: "steer", status: "done", text: "input" }, color), /^<dim>/);
50
49
  assert.equal(formatDisplayItem({ type: "steer", status: "done", text: "queued" }, plain), "\u21a9 steer: queued");
51
50
  assert.match(formatDisplayItem({ type: "thinking", status: "queued" }, plain), /\(queued\)/);
52
51
  assert.match(formatDisplayItem({ type: "toolCall", name: "read", args: {}, status: "queued" }, plain), /\(queued\)/);
package/src/index.ts CHANGED
@@ -13,9 +13,10 @@
13
13
  * - Accurate, concise output for the main model
14
14
  */
15
15
 
16
- import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
16
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
17
17
  import { Type } from "typebox";
18
18
  import { getModelRolesAPI } from "@d3ara1n/pi-model-roles";
19
+ import { paletteCommandRegistry } from "@d3ara1n/pi-command-palette-core";
19
20
  import type { SubagentConfig, SubagentResult, SubagentRole } from "./types.ts";
20
21
  import { DEFAULT_CONFIG } from "./types.ts";
21
22
  import { loadSubagentConfig } from "./config.ts";
@@ -713,8 +714,7 @@ export default function subagentExtension(pi: ExtensionAPI) {
713
714
  parameters: Type.Object({
714
715
  id: Type.String({ description: "Run id returned by a background delegate call" }),
715
716
  message: Type.String({
716
- description:
717
- "The concrete correction or updated requirement for the child to follow.",
717
+ description: "The concrete correction or updated requirement for the child to follow.",
718
718
  }),
719
719
  }),
720
720
 
@@ -809,38 +809,50 @@ export default function subagentExtension(pi: ExtensionAPI) {
809
809
  renderResult: renderCancelResult,
810
810
  });
811
811
 
812
- pi.registerCommand("subagent:view", {
813
- description: "Open the subagent activity view (watch runs, steer, browse the session's archive)",
814
- handler: async (_args, ctx) => {
815
- // Union of every known run: the background registry — append-only for
816
- // the whole session, the view doubles as the run archive and derives
817
- // nothing from delivery state plus live in-flight runs (foreground
818
- // delegate calls included, visible only while in flight). Dedupe by
819
- // id background runs appear in both.
820
- const runsProvider = () => {
821
- const seen = new Set<string>();
822
- const out: RunHandle[] = [];
823
- for (const r of [...backgroundRuns.values(), ...liveRuns]) {
824
- if (!seen.has(r.id)) {
825
- seen.add(r.id);
826
- out.push(r);
827
- }
812
+ // ── Subagent activity view ─────────────────────────────────────────
813
+ // Shared by the /subagent:view command and the native command-palette
814
+ // entry both open the same overlay.
815
+ //
816
+ // Union of every known run: the background registry append-only for
817
+ // the whole session, the view doubles as the run archive and derives
818
+ // nothing from delivery state plus live in-flight runs (foreground
819
+ // delegate calls included, visible only while in flight). Dedupe by
820
+ // id background runs appear in both.
821
+ async function openSubagentView(ctx: ExtensionContext): Promise<void> {
822
+ const runsProvider = () => {
823
+ const seen = new Set<string>();
824
+ const out: RunHandle[] = [];
825
+ for (const r of [...backgroundRuns.values(), ...liveRuns]) {
826
+ if (!seen.has(r.id)) {
827
+ seen.add(r.id);
828
+ out.push(r);
828
829
  }
829
- return out;
830
- };
831
- if (runsProvider().length === 0) {
832
- ctx.ui.notify("No subagent runs yet.", "info");
833
- return;
834
830
  }
835
- await ctx.ui.custom(
836
- (tui, theme, _keybindings, done) =>
837
- createViewPanel(runsProvider, tui, theme, () => done(undefined)),
838
- {
839
- overlay: true,
840
- overlayOptions: { anchor: "center", width: "90%", maxHeight: "85%" },
841
- },
842
- );
843
- },
831
+ return out;
832
+ };
833
+ await ctx.ui.custom(
834
+ (tui, theme, _keybindings, done) =>
835
+ createViewPanel(runsProvider, tui, theme, () => done(undefined)),
836
+ {
837
+ overlay: true,
838
+ overlayOptions: { anchor: "center", width: "90%", maxHeight: "85%" },
839
+ },
840
+ );
841
+ }
842
+
843
+ pi.registerCommand("subagent:view", {
844
+ description:
845
+ "Open the subagent activity view (watch runs, steer, browse the session's archive)",
846
+ handler: (_args, ctx) => openSubagentView(ctx),
847
+ });
848
+
849
+ // Native command-palette entry — opens the view directly, no editor
850
+ // round-trip, so it works mid-draft.
851
+ paletteCommandRegistry.register({
852
+ id: "subagent:view",
853
+ label: "Subagent: Activity View",
854
+ description: "Watch subagent runs, steer, browse the session's archive",
855
+ run: (_pi, ctx) => openSubagentView(ctx),
844
856
  });
845
857
 
846
858
  pi.registerCommand("subagent:doctor", {
package/src/utils.ts CHANGED
@@ -288,7 +288,10 @@ export function formatDisplayItem(
288
288
  const queued = item.status === "queued";
289
289
  const marker = queued ? " (queued)" : "";
290
290
  if (item.type === "steer") {
291
- return fg("accent", `\u21a9 steer${marker}: ${oneLine(item.text)}`);
291
+ // Color follows delivery status like every other entry (queued = accent,
292
+ // consumed = dim); the ↩ glyph alone carries the external-origin marker.
293
+ const steerColor = queued ? "accent" : "dim";
294
+ return fg(steerColor, `\u21a9 steer${marker}: ${oneLine(item.text)}`);
292
295
  }
293
296
  if (queued) {
294
297
  const body = item.type === "thinking" ? "thinking" : formatToolCall(item.name, item.args, (_c, text) => text);