@nmzpy/pi-ember-stack 0.1.2 → 0.1.4

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.
Files changed (25) hide show
  1. package/ATTRIBUTION.md +3 -2
  2. package/README.md +8 -7
  3. package/package.json +1 -1
  4. package/plugins/index.ts +21 -13
  5. package/plugins/pi-compact-tools/index.ts +74 -0
  6. package/plugins/{ember → pi-custom-agents}/index.ts +26 -77
  7. /package/plugins/{ember → pi-compact-tools}/questionnaire-tool.ts +0 -0
  8. /package/plugins/{subagent → pi-custom-agents/subagent}/LICENSE +0 -0
  9. /package/plugins/{subagent → pi-custom-agents/subagent}/README.md +0 -0
  10. /package/plugins/{subagent → pi-custom-agents/subagent}/agent-format.md +0 -0
  11. /package/plugins/{subagent → pi-custom-agents/subagent}/agents/architect.md +0 -0
  12. /package/plugins/{subagent → pi-custom-agents/subagent}/agents/coder.md +0 -0
  13. /package/plugins/{subagent → pi-custom-agents/subagent}/agents/general-purpose.md +0 -0
  14. /package/plugins/{subagent → pi-custom-agents/subagent}/agents/reviewer.md +0 -0
  15. /package/plugins/{subagent → pi-custom-agents/subagent}/agents/scout.md +0 -0
  16. /package/plugins/{subagent → pi-custom-agents/subagent}/agents/worker.md +0 -0
  17. /package/plugins/{subagent → pi-custom-agents/subagent}/extensions/agents.ts +0 -0
  18. /package/plugins/{subagent → pi-custom-agents/subagent}/extensions/index.ts +0 -0
  19. /package/plugins/{subagent → pi-custom-agents/subagent}/extensions/model.ts +0 -0
  20. /package/plugins/{subagent → pi-custom-agents/subagent}/extensions/package.json +0 -0
  21. /package/plugins/{subagent → pi-custom-agents/subagent}/extensions/render.ts +0 -0
  22. /package/plugins/{subagent → pi-custom-agents/subagent}/extensions/runner.ts +0 -0
  23. /package/plugins/{subagent → pi-custom-agents/subagent}/extensions/service.ts +0 -0
  24. /package/plugins/{subagent → pi-custom-agents/subagent}/extensions/thread-viewer.ts +0 -0
  25. /package/plugins/{subagent → pi-custom-agents/subagent}/extensions/threads.ts +0 -0
package/ATTRIBUTION.md CHANGED
@@ -1,10 +1,11 @@
1
1
  # Attribution
2
2
 
3
- `plugins/subagent/extensions/` and the upstream bundled agent files were vendored
3
+ `plugins/pi-custom-agents/subagent/extensions/` and the upstream bundled agent
4
+ files were vendored
4
5
  from `@bacnh85/pi-subagent` version `0.5.0`.
5
6
 
6
7
  The upstream package declares the MIT license. The vendored subtree retains
7
- that license in `plugins/subagent/LICENSE`, along with the original README, agent
8
+ that license in `plugins/pi-custom-agents/subagent/LICENSE`, along with the original README, agent
8
9
  format documentation, bundled roles, package version, and attribution.
9
10
  Upstream project: <https://github.com/bacnh85/pi-extensions>.
10
11
 
package/README.md CHANGED
@@ -23,15 +23,15 @@ plugins under `plugins/`. Ember projects enable them in `.pi/ember-stack.json`:
23
23
 
24
24
  ```json
25
25
  {
26
- "plugins": ["ember", "subagent", "devin-auth"]
26
+ "plugins": ["pi-compact-tools", "pi-custom-agents", "devin-auth"]
27
27
  }
28
28
  ```
29
29
 
30
30
  Remove a plugin ID to disable it, or use `/stack-plugins` to toggle one from
31
31
  the TUI. Restart pi after changing the list. The available plugins are:
32
32
 
33
- - `ember`: modes, questionnaire, footer, and compact edit rendering.
34
- - `subagent`: bundled subagent tool and agent definitions.
33
+ - `pi-compact-tools`: collapsed native edit rendering and questionnaire UI.
34
+ - `pi-custom-agents`: primary modes, plans, subagent tool, and bundled agent definitions.
35
35
  - `devin-auth`: Devin provider, OAuth, catalog refresh, and streaming.
36
36
 
37
37
  ## Project setup
@@ -39,7 +39,7 @@ the TUI. Restart pi after changing the list. The available plugins are:
39
39
  The Ember repository contains a project-local `.pi/settings.json` entry for:
40
40
 
41
41
  ```json
42
- "npm:@nmzpy/pi-ember-stack@0.1.2"
42
+ "npm:@nmzpy/pi-ember-stack@0.1.4"
43
43
  ```
44
44
 
45
45
  On a new clone, start pi from the project directory. Pi will ask for a
@@ -68,9 +68,10 @@ of this repository.
68
68
 
69
69
  ## Development
70
70
 
71
- The package entrypoint is `plugins/index.ts`. Ember functionality is under
72
- `plugins/ember/`, subagents and bundled agents are under `plugins/subagent/`,
73
- and Devin auth is under `plugins/devin-auth/`.
71
+ The package entrypoint is `plugins/index.ts`. Compact tools are under
72
+ `plugins/pi-compact-tools/`, while primary modes, plans, subagents, and bundled
73
+ agents are under `plugins/pi-custom-agents/`. Devin auth is under
74
+ `plugins/devin-auth/`.
74
75
 
75
76
  Run the package typecheck with:
76
77
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nmzpy/pi-ember-stack",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Ember's configurable pi plugin stack with modes, questionnaire, compact edits, subagents, and Devin auth",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/plugins/index.ts CHANGED
@@ -3,14 +3,19 @@ import * as path from "node:path";
3
3
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
4
4
 
5
5
  import devinAuthPlugin from "./devin-auth/extensions/index.ts";
6
- import emberPlugin from "./ember/index.ts";
7
- import subagentPlugin from "./subagent/extensions/index.ts";
6
+ import piCompactToolsPlugin from "./pi-compact-tools/index.ts";
7
+ import piCustomAgentsPlugin, {
8
+ type PiCustomAgentsOptions,
9
+ } from "./pi-custom-agents/index.ts";
8
10
 
9
- type PluginId = "ember" | "subagent" | "devin-auth";
11
+ type PluginId = "pi-compact-tools" | "pi-custom-agents" | "devin-auth";
10
12
  type StackPlugin = {
11
13
  id: PluginId;
12
14
  description: string;
13
- extension: (pi: ExtensionAPI) => void | Promise<void>;
15
+ extension: (
16
+ pi: ExtensionAPI,
17
+ options?: PiCustomAgentsOptions,
18
+ ) => void | Promise<void>;
14
19
  };
15
20
 
16
21
  type StackPluginConfig = {
@@ -19,21 +24,21 @@ type StackPluginConfig = {
19
24
 
20
25
  const CONFIG_RELATIVE_PATH = path.join(".pi", "ember-stack.json");
21
26
  const DEFAULT_PLUGIN_IDS: readonly PluginId[] = [
22
- "ember",
23
- "subagent",
27
+ "pi-compact-tools",
28
+ "pi-custom-agents",
24
29
  "devin-auth",
25
30
  ];
26
31
 
27
32
  const PLUGINS: readonly StackPlugin[] = [
28
33
  {
29
- id: "ember",
30
- description: "Ember modes, questionnaire, footer, and compact edit rendering",
31
- extension: emberPlugin,
34
+ id: "pi-compact-tools",
35
+ description: "Collapsed native edit rendering and questionnaire UI",
36
+ extension: piCompactToolsPlugin,
32
37
  },
33
38
  {
34
- id: "subagent",
35
- description: "Bundled subagent tool and agent definitions",
36
- extension: subagentPlugin,
39
+ id: "pi-custom-agents",
40
+ description: "Primary modes, plans, subagents, and bundled agent definitions",
41
+ extension: piCustomAgentsPlugin,
37
42
  },
38
43
  {
39
44
  id: "devin-auth",
@@ -130,9 +135,12 @@ function registerPluginCommand(
130
135
  export default async function piEmberStackPlugin(pi: ExtensionAPI): Promise<void> {
131
136
  const cwd = process.cwd();
132
137
  const enabledPlugins = readEnabledPlugins(cwd);
138
+ const pluginOptions: PiCustomAgentsOptions = {
139
+ compactToolsEnabled: enabledPlugins.has("pi-compact-tools"),
140
+ };
133
141
  for (const plugin of PLUGINS) {
134
142
  if (!enabledPlugins.has(plugin.id)) continue;
135
- await plugin.extension(pi);
143
+ await plugin.extension(pi, pluginOptions);
136
144
  }
137
145
  registerPluginCommand(pi, cwd, enabledPlugins);
138
146
  }
@@ -0,0 +1,74 @@
1
+ import * as path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import { createEditTool } from "@earendil-works/pi-coding-agent";
4
+ import { Text } from "@earendil-works/pi-tui";
5
+
6
+ import { registerQuestionnaireTool } from "./questionnaire-tool.ts";
7
+
8
+ const SOURCE_ROOT = path.dirname(fileURLToPath(import.meta.url));
9
+
10
+ function registerCollapsedEditTool(extensionApi: any): void {
11
+ const editDefinition = createEditTool(SOURCE_ROOT);
12
+ extensionApi.registerTool({
13
+ name: "edit",
14
+ label: "edit",
15
+ description: editDefinition.description,
16
+ parameters: editDefinition.parameters,
17
+ renderShell: "self",
18
+
19
+ async execute(
20
+ toolCallId: string,
21
+ params: any,
22
+ signal: AbortSignal,
23
+ onUpdate: any,
24
+ ctx: any,
25
+ ) {
26
+ return createEditTool(ctx.cwd).execute(
27
+ toolCallId,
28
+ params,
29
+ signal,
30
+ onUpdate,
31
+ );
32
+ },
33
+
34
+ renderCall(args: any, theme: any): any {
35
+ const filePath = String(args?.path ?? args?.file_path ?? "");
36
+ return new Text(
37
+ theme.fg("toolTitle", theme.bold("edit ")) +
38
+ theme.fg("accent", filePath),
39
+ 0,
40
+ 0,
41
+ );
42
+ },
43
+
44
+ renderResult(result: any, { isPartial }: any, theme: any): any {
45
+ if (isPartial) return new Text(theme.fg("warning", "Editing..."), 0, 0);
46
+
47
+ const content = result.content?.find((item: any) => item.type === "text");
48
+ if (content?.text?.startsWith("Error")) {
49
+ return new Text(theme.fg("error", content.text.split("\n")[0]), 0, 0);
50
+ }
51
+
52
+ const diff = typeof result.details?.diff === "string" ? result.details.diff : "";
53
+ let additions = 0;
54
+ let removals = 0;
55
+ for (const line of diff.split("\n")) {
56
+ if (line.startsWith("+") && !line.startsWith("+++")) additions++;
57
+ if (line.startsWith("-") && !line.startsWith("---")) removals++;
58
+ }
59
+
60
+ return new Text(
61
+ theme.fg("success", `+${additions}`) +
62
+ theme.fg("dim", " / ") +
63
+ theme.fg("error", `-${removals}`),
64
+ 0,
65
+ 0,
66
+ );
67
+ },
68
+ });
69
+ }
70
+
71
+ export default function piCompactToolsPlugin(pi: any): void {
72
+ registerQuestionnaireTool(pi);
73
+ registerCollapsedEditTool(pi);
74
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Pi Ember Stack — Primary Modes Extension
2
+ * Pi Custom Agents — Primary Modes Extension
3
3
  *
4
4
  * Toggleable primary modes for the Ember project, mirroring opencode's
5
5
  * mode: primary agents. Each mode is a slash command that:
@@ -20,13 +20,12 @@ import * as fs from "node:fs";
20
20
  import * as path from "node:path";
21
21
  import { isAbsolute, relative, resolve, sep } from "node:path";
22
22
  import { fileURLToPath } from "node:url";
23
- import { createEditTool } from "@earendil-works/pi-coding-agent";
24
- import { Text, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
23
+ import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
25
24
  import {
26
25
  askQuestionnaire,
27
- registerQuestionnaireTool,
28
26
  type QuestionnaireQuestion,
29
- } from "./questionnaire-tool.ts";
27
+ } from "../pi-compact-tools/questionnaire-tool.ts";
28
+ import subagentPlugin from "./subagent/extensions/index.ts";
30
29
 
31
30
  function formatTokens(count: number): string {
32
31
  if (count < 1000) return count.toString();
@@ -52,8 +51,8 @@ const FULL_TOOLS = ["read", "bash", "edit", "write", "grep", "find", "ls", "ques
52
51
 
53
52
  const SOURCE_ROOT = path.dirname(fileURLToPath(import.meta.url));
54
53
  const SUBAGENT_FILES: Record<string, string> = {
55
- coder: path.join(SOURCE_ROOT, "..", "subagent", "agents", "coder.md"),
56
- architect: path.join(SOURCE_ROOT, "..", "subagent", "agents", "architect.md"),
54
+ coder: path.join(SOURCE_ROOT, "subagent", "agents", "coder.md"),
55
+ architect: path.join(SOURCE_ROOT, "subagent", "agents", "architect.md"),
57
56
  };
58
57
 
59
58
  interface ModeConfig {
@@ -559,73 +558,21 @@ function getLastModeFromSession(ctx: any): string | null {
559
558
  return null;
560
559
  }
561
560
 
562
- export default function piEmberStackExtension(pi: any) {
561
+ export type PiCustomAgentsOptions = {
562
+ compactToolsEnabled: boolean;
563
+ };
564
+
565
+ export default async function piCustomAgentsPlugin(
566
+ pi: any,
567
+ options: PiCustomAgentsOptions = { compactToolsEnabled: true },
568
+ ) {
563
569
  let currentMode: string = DEFAULT_MODE;
564
570
  let lastMessagedMode: string | null = null;
565
571
  let waitingForPlan = false;
566
- registerQuestionnaireTool(pi);
567
- registerCollapsedEditTool(pi);
568
-
569
- function registerCollapsedEditTool(extensionApi: any): void {
570
- const editDefinition = createEditTool(SOURCE_ROOT);
571
- extensionApi.registerTool({
572
- name: "edit",
573
- label: "edit",
574
- description: editDefinition.description,
575
- parameters: editDefinition.parameters,
576
- renderShell: "self",
577
-
578
- async execute(
579
- toolCallId: string,
580
- params: any,
581
- signal: AbortSignal,
582
- onUpdate: any,
583
- ctx: any,
584
- ) {
585
- return createEditTool(ctx.cwd).execute(
586
- toolCallId,
587
- params,
588
- signal,
589
- onUpdate,
590
- );
591
- },
592
-
593
- renderCall(args: any, theme: any): any {
594
- const filePath = String(args?.path ?? args?.file_path ?? "");
595
- return new Text(
596
- theme.fg("toolTitle", theme.bold("edit ")) +
597
- theme.fg("accent", filePath),
598
- 0,
599
- 0,
600
- );
601
- },
602
-
603
- renderResult(result: any, { isPartial }: any, theme: any): any {
604
- if (isPartial) return new Text(theme.fg("warning", "Editing..."), 0, 0);
605
-
606
- const content = result.content?.find((item: any) => item.type === "text");
607
- if (content?.text?.startsWith("Error")) {
608
- return new Text(theme.fg("error", content.text.split("\n")[0]), 0, 0);
609
- }
610
-
611
- const diff = typeof result.details?.diff === "string" ? result.details.diff : "";
612
- let additions = 0;
613
- let removals = 0;
614
- for (const line of diff.split("\n")) {
615
- if (line.startsWith("+") && !line.startsWith("+++")) additions++;
616
- if (line.startsWith("-") && !line.startsWith("---")) removals++;
617
- }
618
-
619
- return new Text(
620
- theme.fg("success", `+${additions}`) +
621
- theme.fg("dim", " / ") +
622
- theme.fg("error", `-${removals}`),
623
- 0,
624
- 0,
625
- );
626
- },
627
- });
628
- }
572
+ const compactToolsEnabled = options.compactToolsEnabled;
573
+ const toolsForMode = (tools: string[]): string[] => compactToolsEnabled
574
+ ? tools
575
+ : tools.filter((toolName) => toolName !== "questionnaire");
629
576
 
630
577
  for (const modeId of MODE_IDS) {
631
578
  const mode = MODES[modeId];
@@ -655,11 +602,11 @@ export default function piEmberStackExtension(pi: any) {
655
602
  const mode = MODES[modeId];
656
603
  if (!mode) return;
657
604
  currentMode = modeId;
658
- pi.setActiveTools(mode.tools);
605
+ pi.setActiveTools(toolsForMode(mode.tools));
659
606
  if (modeId === DEFAULT_MODE) {
660
607
  ctx.ui.notify("Coder mode. Full access restored.");
661
608
  } else {
662
- ctx.ui.notify(`${mode.label} mode enabled. Tools: ${mode.tools.join(", ")}`);
609
+ ctx.ui.notify(`${mode.label} mode enabled. Tools: ${toolsForMode(mode.tools).join(", ")}`);
663
610
  }
664
611
  updateStatus(ctx);
665
612
  }
@@ -956,11 +903,11 @@ export default function piEmberStackExtension(pi: any) {
956
903
  if (restored && restored !== DEFAULT_MODE) {
957
904
  currentMode = restored;
958
905
  lastMessagedMode = restored;
959
- pi.setActiveTools(MODES[restored].tools);
906
+ pi.setActiveTools(toolsForMode(MODES[restored].tools));
960
907
  } else {
961
908
  currentMode = DEFAULT_MODE;
962
909
  lastMessagedMode = null;
963
- pi.setActiveTools(FULL_TOOLS);
910
+ pi.setActiveTools(toolsForMode(FULL_TOOLS));
964
911
  }
965
912
  installCustomFooter(ctx);
966
913
  updateStatus(ctx);
@@ -971,13 +918,15 @@ export default function piEmberStackExtension(pi: any) {
971
918
  if (restored && restored !== DEFAULT_MODE) {
972
919
  currentMode = restored;
973
920
  lastMessagedMode = restored;
974
- pi.setActiveTools(MODES[restored].tools);
921
+ pi.setActiveTools(toolsForMode(MODES[restored].tools));
975
922
  } else {
976
923
  currentMode = DEFAULT_MODE;
977
924
  lastMessagedMode = null;
978
- pi.setActiveTools(FULL_TOOLS);
925
+ pi.setActiveTools(toolsForMode(FULL_TOOLS));
979
926
  }
980
927
  updateStatus(ctx);
981
928
  installCustomFooter(ctx);
982
929
  });
930
+
931
+ await subagentPlugin(pi);
983
932
  }