@matyah00/openpi 0.1.2

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 (98) hide show
  1. package/README.md +117 -0
  2. package/agents/agent-chain.yaml +113 -0
  3. package/agents/backend.md +13 -0
  4. package/agents/basher.md +27 -0
  5. package/agents/builder.md +14 -0
  6. package/agents/code-searcher.md +27 -0
  7. package/agents/context-pruner.md +29 -0
  8. package/agents/directory-lister.md +25 -0
  9. package/agents/documenter.md +13 -0
  10. package/agents/editor.md +27 -0
  11. package/agents/file-picker.md +27 -0
  12. package/agents/frontend.md +14 -0
  13. package/agents/glob-matcher.md +25 -0
  14. package/agents/librarian.md +27 -0
  15. package/agents/loop-controller.md +41 -0
  16. package/agents/pi-pi/agent-expert.md +97 -0
  17. package/agents/pi-pi/cli-expert.md +41 -0
  18. package/agents/pi-pi/config-expert.md +63 -0
  19. package/agents/pi-pi/ext-expert.md +43 -0
  20. package/agents/pi-pi/keybinding-expert.md +134 -0
  21. package/agents/pi-pi/pi-orchestrator.md +57 -0
  22. package/agents/pi-pi/prompt-expert.md +70 -0
  23. package/agents/pi-pi/skill-expert.md +42 -0
  24. package/agents/pi-pi/theme-expert.md +40 -0
  25. package/agents/pi-pi/tui-expert.md +85 -0
  26. package/agents/plan-reviewer.md +22 -0
  27. package/agents/planner.md +14 -0
  28. package/agents/problem-architect.md +55 -0
  29. package/agents/red-team.md +13 -0
  30. package/agents/reviewer.md +14 -0
  31. package/agents/rule-verifier.md +35 -0
  32. package/agents/scout.md +14 -0
  33. package/agents/security-auditor.md +35 -0
  34. package/agents/ship-guard.md +34 -0
  35. package/agents/spec-reviewer.md +41 -0
  36. package/agents/teams.yaml +73 -0
  37. package/agents/tester.md +27 -0
  38. package/agents/thinker.md +26 -0
  39. package/agents/worker.md +27 -0
  40. package/damage-control-rules.yaml +277 -0
  41. package/extensions/agent-chain.ts +293 -0
  42. package/extensions/agent-team.ts +312 -0
  43. package/extensions/audit-tools.ts +260 -0
  44. package/extensions/commands.ts +169 -0
  45. package/extensions/damage-control-continue.ts +243 -0
  46. package/extensions/lib/packagePaths.ts +13 -0
  47. package/extensions/minimal.ts +34 -0
  48. package/extensions/openpi.ts +255 -0
  49. package/extensions/pure-focus.ts +24 -0
  50. package/extensions/purpose-gate.ts +84 -0
  51. package/extensions/search-tools.ts +277 -0
  52. package/extensions/state-tools.ts +276 -0
  53. package/extensions/system-select.ts +120 -0
  54. package/extensions/theme-cycler.ts +181 -0
  55. package/extensions/themeMap.ts +145 -0
  56. package/extensions/tool-counter-widget.ts +68 -0
  57. package/extensions/tool-counter.ts +102 -0
  58. package/extensions/workflow.ts +642 -0
  59. package/package.json +60 -0
  60. package/prompts/blueprint.md +66 -0
  61. package/prompts/clarify.md +26 -0
  62. package/prompts/compress.md +23 -0
  63. package/prompts/debate.md +23 -0
  64. package/prompts/deep.md +36 -0
  65. package/prompts/deps.md +24 -0
  66. package/prompts/explore.md +22 -0
  67. package/prompts/ghost-test.md +22 -0
  68. package/prompts/goal.md +26 -0
  69. package/prompts/parallel.md +42 -0
  70. package/prompts/plan-team.md +31 -0
  71. package/prompts/prime.md +17 -0
  72. package/prompts/review.md +23 -0
  73. package/prompts/sentinel.md +29 -0
  74. package/prompts/ship.md +30 -0
  75. package/prompts/snapshot.md +26 -0
  76. package/prompts/spec.md +58 -0
  77. package/prompts/test.md +13 -0
  78. package/prompts/validate.md +19 -0
  79. package/skills/bowser/SKILL.md +114 -0
  80. package/skills/env-scanner/SKILL.md +25 -0
  81. package/skills/security-guard/SKILL.md +24 -0
  82. package/skills/session-continuity/SKILL.md +20 -0
  83. package/skills/spec-driven/SKILL.md +25 -0
  84. package/skills/test-first/SKILL.md +23 -0
  85. package/skills/ultrathink/SKILL.md +27 -0
  86. package/themes/catppuccin-mocha.json +86 -0
  87. package/themes/cyberpunk.json +81 -0
  88. package/themes/dracula.json +81 -0
  89. package/themes/everforest.json +82 -0
  90. package/themes/gruvbox.json +80 -0
  91. package/themes/midnight-ocean.json +76 -0
  92. package/themes/nord.json +84 -0
  93. package/themes/ocean-breeze.json +83 -0
  94. package/themes/rose-pine.json +82 -0
  95. package/themes/synthwave.json +82 -0
  96. package/themes/tokyo-night.json +83 -0
  97. package/tsconfig.json +15 -0
  98. package/types/pi-shims.d.ts +102 -0
@@ -0,0 +1,145 @@
1
+ /**
2
+ * themeMap.ts — Per-extension default theme assignments
3
+ *
4
+ * Themes live in .pi/themes/ and are mapped by extension filename (no extension).
5
+ * Each extension calls applyExtensionTheme(import.meta.url, ctx) in its session_start
6
+ * hook to automatically load its designated theme on boot.
7
+ *
8
+ * Available themes (.pi/themes/):
9
+ * catppuccin-mocha · cyberpunk · dracula · everforest · gruvbox
10
+ * midnight-ocean · nord · ocean-breeze · rose-pine
11
+ * synthwave · tokyo-night
12
+ */
13
+
14
+ import type { ExtensionContext } from "@mariozechner/pi-coding-agent";
15
+ import { basename } from "path";
16
+ import { fileURLToPath } from "url";
17
+
18
+ // ── Theme assignments ──────────────────────────────────────────────────────
19
+ //
20
+ // Key = extension filename without extension (matches extensions/<key>.ts)
21
+ // Value = theme name from .pi/themes/<value>.json
22
+ //
23
+ export const THEME_MAP: Record<string, string> = {
24
+ "agent-chain": "midnight-ocean", // deep sequential pipeline
25
+ "agent-team": "dracula", // rich orchestration palette
26
+ "coms": "ocean-breeze", // peer-to-peer messaging, cross-boundary
27
+ "coms-net": "ocean-breeze", // peer-to-peer messaging, cross-boundary
28
+ "cross-agent": "ocean-breeze", // cross-boundary, connecting
29
+ "damage-control": "gruvbox", // grounded, earthy safety
30
+ "minimal": "synthwave", // synthwave by default now!
31
+ "pi-pi": "rose-pine", // warm creative meta-agent
32
+ "pure-focus": "everforest", // calm, distraction-free
33
+ "purpose-gate": "tokyo-night", // intentional, sharp focus
34
+ "session-replay": "catppuccin-mocha", // soft, reflective history
35
+ "subagent-widget": "cyberpunk", // multi-agent futuristic
36
+ "system-select": "catppuccin-mocha", // soft selection UI
37
+ "theme-cycler": "synthwave", // neon, it's a theme tool
38
+ "tilldone": "everforest", // task-focused calm
39
+ "tool-counter": "synthwave", // techy metrics
40
+ "tool-counter-widget":"synthwave", // same family
41
+ };
42
+
43
+ // ── Helpers ───────────────────────────────────────────────────────────────
44
+
45
+ /** Derive the extension name (e.g. "minimal") from its import.meta.url. */
46
+ function extensionName(fileUrl: string): string {
47
+ const filePath = fileUrl.startsWith("file://") ? fileURLToPath(fileUrl) : fileUrl;
48
+ return basename(filePath).replace(/\.[^.]+$/, "");
49
+ }
50
+
51
+ // ── Theme ──────────────────────────────────────────────────────────────────
52
+
53
+ /**
54
+ * Apply the mapped theme for an extension on session boot.
55
+ *
56
+ * @param fileUrl Pass `import.meta.url` from the calling extension file.
57
+ * @param ctx The ExtensionContext from the session_start handler.
58
+ * @returns true if the theme was applied successfully, false otherwise.
59
+ */
60
+ export function applyExtensionTheme(fileUrl: string, ctx: ExtensionContext): boolean {
61
+ if (!ctx.hasUI) return false;
62
+
63
+ const name = extensionName(fileUrl);
64
+
65
+ // If there are multiple extensions stacked in 'ipi', they each fire session_start
66
+ // and try to apply their own mapped theme. The LAST one to fire wins.
67
+ // Since system-select is last in the ipi alias array, it was setting 'catppuccin-mocha'.
68
+
69
+ // We want to skip theme application for all secondary extensions if they are stacked,
70
+ // so the primary extension (first in the array) dictates the theme.
71
+ const primaryExt = primaryExtensionName();
72
+ if (primaryExt && primaryExt !== name) {
73
+ return true; // Pretend we succeeded, but don't overwrite the primary theme
74
+ }
75
+
76
+ let themeName = THEME_MAP[name];
77
+
78
+ if (!themeName) {
79
+ themeName = "synthwave";
80
+ }
81
+
82
+ const result = ctx.ui.setTheme(themeName);
83
+
84
+ if (!result.success && themeName !== "synthwave") {
85
+ return ctx.ui.setTheme("synthwave").success;
86
+ }
87
+
88
+ return result.success;
89
+ }
90
+ // ── Title ──────────────────────────────────────────────────────────────────
91
+
92
+ /**
93
+ * Read process.argv to find the first -e / --extension flag value.
94
+ *
95
+ * When Pi is launched as:
96
+ * pi -e extensions/subagent-widget.ts -e extensions/pure-focus.ts
97
+ *
98
+ * process.argv contains those paths verbatim. Every stacked extension calls
99
+ * this and gets the same answer ("subagent-widget"), so all setTitle calls
100
+ * are idempotent — no shared state or deduplication needed.
101
+ *
102
+ * Returns null if no -e flag is present (e.g. plain `pi` with no extensions).
103
+ */
104
+ function primaryExtensionName(): string | null {
105
+ const argv = process.argv;
106
+ for (let i = 0; i < argv.length - 1; i++) {
107
+ if (argv[i] === "-e" || argv[i] === "--extension") {
108
+ return basename(argv[i + 1]).replace(/\.[^.]+$/, "");
109
+ }
110
+ }
111
+ return null;
112
+ }
113
+
114
+ /**
115
+ * Set the terminal title to "π - <first-extension-name>" on session boot.
116
+ * Reads the title from process.argv so all stacked extensions agree on the
117
+ * same value — no coordination or shared state required.
118
+ *
119
+ * Deferred 150 ms to fire after Pi's own startup title-set.
120
+ */
121
+ function applyExtensionTitle(ctx: ExtensionContext): void {
122
+ if (!ctx.hasUI) return;
123
+ const name = primaryExtensionName();
124
+ if (!name) return;
125
+ setTimeout(() => ctx.ui.setTitle(`π - ${name}`), 150);
126
+ }
127
+
128
+ // ── Combined default ───────────────────────────────────────────────────────
129
+
130
+ /**
131
+ * Apply both the mapped theme AND the terminal title for an extension.
132
+ * Drop-in replacement for applyExtensionTheme — call this in every session_start.
133
+ *
134
+ * Usage:
135
+ * import { applyExtensionDefaults } from "./themeMap.ts";
136
+ *
137
+ * pi.on("session_start", async (_event, ctx) => {
138
+ * applyExtensionDefaults(import.meta.url, ctx);
139
+ * // ... rest of handler
140
+ * });
141
+ */
142
+ export function applyExtensionDefaults(fileUrl: string, ctx: ExtensionContext): void {
143
+ applyExtensionTheme(fileUrl, ctx);
144
+ applyExtensionTitle(ctx);
145
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Tool Counter Widget — Tool call counts in a widget above the editor
3
+ *
4
+ * Shows a persistent, live-updating widget with per-tool background colors.
5
+ * Format: Tools (N): [Bash 3] [Read 7] [Write 2]
6
+ *
7
+ * Usage: pi -e extensions/tool-counter-widget.ts
8
+ */
9
+
10
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
11
+ import { Box, Text } from "@mariozechner/pi-tui";
12
+ import { applyExtensionDefaults } from "./themeMap.ts";
13
+
14
+ const palette = [
15
+ [12, 40, 80], // deep navy
16
+ [50, 20, 70], // dark purple
17
+ [10, 55, 45], // dark teal
18
+ [70, 30, 10], // dark rust
19
+ [55, 15, 40], // dark plum
20
+ [15, 50, 65], // dark ocean
21
+ [45, 45, 15], // dark olive
22
+ [65, 18, 25], // dark wine
23
+ ];
24
+
25
+ function bg(rgb: number[], s: string): string {
26
+ return `\x1b[48;2;${rgb[0]};${rgb[1]};${rgb[2]}m${s}\x1b[49m`;
27
+ }
28
+
29
+ export default function (pi: ExtensionAPI) {
30
+ const counts: Record<string, number> = {};
31
+ const toolColors: Record<string, number[]> = {};
32
+ let total = 0;
33
+ let colorIdx = 0;
34
+
35
+ pi.on("tool_execution_end", async (event) => {
36
+ if (!(event.toolName in toolColors)) {
37
+ toolColors[event.toolName] = palette[colorIdx % palette.length];
38
+ colorIdx++;
39
+ }
40
+ counts[event.toolName] = (counts[event.toolName] || 0) + 1;
41
+ total++;
42
+ });
43
+
44
+ pi.on("session_start", async (_event, ctx) => {
45
+ applyExtensionDefaults(import.meta.url, ctx);
46
+ ctx.ui.setWidget("tool-counter", (_tui, theme) => {
47
+ const text = new Text("", 1, 1);
48
+
49
+ return {
50
+ render(width: number): string[] {
51
+ const entries = Object.entries(counts);
52
+ const parts = entries.map(([name, count]) => {
53
+ const rgb = toolColors[name];
54
+ return bg(rgb, `\x1b[38;2;220;220;220m ${name} ${count} \x1b[39m`);
55
+ });
56
+ text.setText(
57
+ theme.fg("accent", `Tools (${total}):`) +
58
+ (entries.length > 0 ? " " + parts.join(" ") : "")
59
+ );
60
+ return text.render(width);
61
+ },
62
+ invalidate() {
63
+ text.invalidate();
64
+ },
65
+ };
66
+ });
67
+ });
68
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Tool Counter — Rich two-line custom footer
3
+ *
4
+ * Line 1: model + context meter on left, tokens in/out + cost on right
5
+ * Line 2: cwd (branch) on left, tool call tally on right
6
+ *
7
+ * Demonstrates: setFooter, footerData.getGitBranch(), onBranchChange(),
8
+ * session branch traversal for token/cost accumulation.
9
+ *
10
+ * Usage: pi -e extensions/tool-counter.ts
11
+ */
12
+
13
+ import type { AssistantMessage } from "@mariozechner/pi-ai";
14
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
15
+ import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
16
+ import { basename } from "node:path";
17
+ import { applyExtensionDefaults } from "./themeMap.ts";
18
+
19
+ export default function (pi: ExtensionAPI) {
20
+ const counts: Record<string, number> = {};
21
+
22
+ pi.on("tool_execution_end", async (event) => {
23
+ counts[event.toolName] = (counts[event.toolName] || 0) + 1;
24
+ });
25
+
26
+ pi.on("session_start", async (_event, ctx) => {
27
+ applyExtensionDefaults(import.meta.url, ctx);
28
+ ctx.ui.setFooter((tui, theme, footerData) => {
29
+ const unsub = footerData.onBranchChange(() => tui.requestRender());
30
+
31
+ return {
32
+ dispose: unsub,
33
+ invalidate() {},
34
+ render(width: number): string[] {
35
+ // --- Line 1: cwd + branch (left), tokens + cost (right) ---
36
+ let tokIn = 0;
37
+ let tokOut = 0;
38
+ let cost = 0;
39
+ for (const entry of ctx.sessionManager.getBranch()) {
40
+ if (entry.type === "message" && entry.message.role === "assistant") {
41
+ const m = entry.message as AssistantMessage;
42
+ tokIn += m.usage.input;
43
+ tokOut += m.usage.output;
44
+ cost += m.usage.cost.total;
45
+ }
46
+ }
47
+
48
+ const fmt = (n: number) => n < 1000 ? `${n}` : `${(n / 1000).toFixed(1)}k`;
49
+ const dir = basename(ctx.cwd);
50
+ const branch = footerData.getGitBranch();
51
+
52
+ // --- Line 1: model + context meter (left), tokens + cost (right) ---
53
+ const usage = ctx.getContextUsage();
54
+ const pct = usage ? usage.percent : 0;
55
+ const filled = Math.round(pct / 10) || 1;
56
+ const bar = "#".repeat(filled) + "-".repeat(10 - filled);
57
+ const model = ctx.model?.id || "no-model";
58
+
59
+ const l1Left =
60
+ theme.fg("dim", ` ${model} `) +
61
+ theme.fg("warning", "[") +
62
+ theme.fg("success", "#".repeat(filled)) +
63
+ theme.fg("dim", "-".repeat(10 - filled)) +
64
+ theme.fg("warning", "]") +
65
+ theme.fg("dim", " ") +
66
+ theme.fg("accent", `${Math.round(pct)}%`);
67
+
68
+ const l1Right =
69
+ theme.fg("success", `${fmt(tokIn)}`) +
70
+ theme.fg("dim", " in ") +
71
+ theme.fg("accent", `${fmt(tokOut)}`) +
72
+ theme.fg("dim", " out ") +
73
+ theme.fg("warning", `$${cost.toFixed(4)}`) +
74
+ theme.fg("dim", " ");
75
+
76
+ const pad1 = " ".repeat(Math.max(1, width - visibleWidth(l1Left) - visibleWidth(l1Right)));
77
+ const line1 = truncateToWidth(l1Left + pad1 + l1Right, width, "");
78
+
79
+ // --- Line 2: cwd + branch (left), tool tally (right) ---
80
+ const l2Left =
81
+ theme.fg("dim", ` ${dir}`) +
82
+ (branch
83
+ ? theme.fg("dim", " ") + theme.fg("warning", "(") + theme.fg("success", branch) + theme.fg("warning", ")")
84
+ : "");
85
+
86
+ const entries = Object.entries(counts);
87
+ const l2Right = entries.length === 0
88
+ ? theme.fg("dim", "waiting for tools ")
89
+ : entries.map(
90
+ ([name, count]) =>
91
+ theme.fg("accent", name) + theme.fg("dim", " ") + theme.fg("success", `${count}`)
92
+ ).join(theme.fg("warning", " | ")) + theme.fg("dim", " ");
93
+
94
+ const pad2 = " ".repeat(Math.max(1, width - visibleWidth(l2Left) - visibleWidth(l2Right)));
95
+ const line2 = truncateToWidth(l2Left + pad2 + l2Right, width, "");
96
+
97
+ return [line1, line2];
98
+ },
99
+ };
100
+ });
101
+ });
102
+ }