@mariozechner/pi-coding-agent 0.32.3 → 0.34.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.
Files changed (102) hide show
  1. package/CHANGELOG.md +56 -1
  2. package/README.md +76 -3
  3. package/dist/cli/args.d.ts +5 -1
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +18 -1
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/core/agent-session.d.ts +24 -1
  8. package/dist/core/agent-session.d.ts.map +1 -1
  9. package/dist/core/agent-session.js +65 -9
  10. package/dist/core/agent-session.js.map +1 -1
  11. package/dist/core/bash-executor.d.ts.map +1 -1
  12. package/dist/core/bash-executor.js +2 -1
  13. package/dist/core/bash-executor.js.map +1 -1
  14. package/dist/core/custom-tools/loader.d.ts.map +1 -1
  15. package/dist/core/custom-tools/loader.js +1 -0
  16. package/dist/core/custom-tools/loader.js.map +1 -1
  17. package/dist/core/export-html/template.css +34 -4
  18. package/dist/core/export-html/template.js +17 -4
  19. package/dist/core/hooks/index.d.ts +1 -1
  20. package/dist/core/hooks/index.d.ts.map +1 -1
  21. package/dist/core/hooks/index.js.map +1 -1
  22. package/dist/core/hooks/loader.d.ts +56 -1
  23. package/dist/core/hooks/loader.d.ts.map +1 -1
  24. package/dist/core/hooks/loader.js +54 -2
  25. package/dist/core/hooks/loader.js.map +1 -1
  26. package/dist/core/hooks/runner.d.ts +33 -5
  27. package/dist/core/hooks/runner.d.ts.map +1 -1
  28. package/dist/core/hooks/runner.js +100 -9
  29. package/dist/core/hooks/runner.js.map +1 -1
  30. package/dist/core/hooks/types.d.ts +135 -3
  31. package/dist/core/hooks/types.d.ts.map +1 -1
  32. package/dist/core/hooks/types.js.map +1 -1
  33. package/dist/core/keybindings.d.ts +59 -0
  34. package/dist/core/keybindings.d.ts.map +1 -0
  35. package/dist/core/keybindings.js +149 -0
  36. package/dist/core/keybindings.js.map +1 -0
  37. package/dist/core/sdk.d.ts +3 -0
  38. package/dist/core/sdk.d.ts.map +1 -1
  39. package/dist/core/sdk.js +102 -27
  40. package/dist/core/sdk.js.map +1 -1
  41. package/dist/index.d.ts +1 -1
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +1 -1
  44. package/dist/index.js.map +1 -1
  45. package/dist/main.d.ts.map +1 -1
  46. package/dist/main.js +32 -7
  47. package/dist/main.js.map +1 -1
  48. package/dist/modes/interactive/components/custom-editor.d.ts +13 -12
  49. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  50. package/dist/modes/interactive/components/custom-editor.js +50 -68
  51. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  52. package/dist/modes/interactive/components/hook-editor.d.ts.map +1 -1
  53. package/dist/modes/interactive/components/hook-editor.js +5 -4
  54. package/dist/modes/interactive/components/hook-editor.js.map +1 -1
  55. package/dist/modes/interactive/components/hook-input.d.ts.map +1 -1
  56. package/dist/modes/interactive/components/hook-input.js +4 -3
  57. package/dist/modes/interactive/components/hook-input.js.map +1 -1
  58. package/dist/modes/interactive/components/hook-selector.d.ts.map +1 -1
  59. package/dist/modes/interactive/components/hook-selector.js +6 -5
  60. package/dist/modes/interactive/components/hook-selector.js.map +1 -1
  61. package/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
  62. package/dist/modes/interactive/components/model-selector.js +6 -5
  63. package/dist/modes/interactive/components/model-selector.js.map +1 -1
  64. package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
  65. package/dist/modes/interactive/components/oauth-selector.js +6 -5
  66. package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
  67. package/dist/modes/interactive/components/session-selector.d.ts.map +1 -1
  68. package/dist/modes/interactive/components/session-selector.js +6 -9
  69. package/dist/modes/interactive/components/session-selector.js.map +1 -1
  70. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  71. package/dist/modes/interactive/components/tree-selector.js +14 -15
  72. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  73. package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -1
  74. package/dist/modes/interactive/components/user-message-selector.js +6 -11
  75. package/dist/modes/interactive/components/user-message-selector.js.map +1 -1
  76. package/dist/modes/interactive/interactive-mode.d.ts +34 -1
  77. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  78. package/dist/modes/interactive/interactive-mode.js +300 -64
  79. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  80. package/dist/modes/interactive/theme/theme.d.ts +1 -0
  81. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  82. package/dist/modes/interactive/theme/theme.js +3 -0
  83. package/dist/modes/interactive/theme/theme.js.map +1 -1
  84. package/dist/modes/print-mode.d.ts.map +1 -1
  85. package/dist/modes/print-mode.js +3 -0
  86. package/dist/modes/print-mode.js.map +1 -1
  87. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  88. package/dist/modes/rpc/rpc-mode.js +16 -0
  89. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  90. package/dist/modes/rpc/rpc-types.d.ts +6 -0
  91. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  92. package/dist/modes/rpc/rpc-types.js.map +1 -1
  93. package/docs/hooks.md +114 -4
  94. package/docs/tui.md +18 -15
  95. package/examples/custom-tools/subagent/README.md +2 -2
  96. package/examples/hooks/README.md +3 -0
  97. package/examples/hooks/pirate.ts +44 -0
  98. package/examples/hooks/plan-mode.ts +548 -0
  99. package/examples/hooks/snake.ts +7 -7
  100. package/examples/hooks/todo/index.ts +2 -2
  101. package/examples/hooks/tools.ts +145 -0
  102. package/package.json +5 -4
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import type { HookAPI } from "@mariozechner/pi-coding-agent";
6
- import { isArrowDown, isArrowLeft, isArrowRight, isArrowUp, isEscape, visibleWidth } from "@mariozechner/pi-tui";
6
+ import { matchesKey, visibleWidth } from "@mariozechner/pi-tui";
7
7
 
8
8
  const GAME_WIDTH = 40;
9
9
  const GAME_HEIGHT = 15;
@@ -150,7 +150,7 @@ class SnakeComponent {
150
150
  handleInput(data: string): void {
151
151
  // If paused (resuming), wait for any key
152
152
  if (this.paused) {
153
- if (isEscape(data) || data === "q" || data === "Q") {
153
+ if (matchesKey(data, "escape") || data === "q" || data === "Q") {
154
154
  // Quit without clearing save
155
155
  this.dispose();
156
156
  this.onClose();
@@ -163,7 +163,7 @@ class SnakeComponent {
163
163
  }
164
164
 
165
165
  // ESC to pause and save
166
- if (isEscape(data)) {
166
+ if (matchesKey(data, "escape")) {
167
167
  this.dispose();
168
168
  this.onSave(this.state);
169
169
  this.onClose();
@@ -179,13 +179,13 @@ class SnakeComponent {
179
179
  }
180
180
 
181
181
  // Arrow keys or WASD
182
- if (isArrowUp(data) || data === "w" || data === "W") {
182
+ if (matchesKey(data, "up") || data === "w" || data === "W") {
183
183
  if (this.state.direction !== "down") this.state.nextDirection = "up";
184
- } else if (isArrowDown(data) || data === "s" || data === "S") {
184
+ } else if (matchesKey(data, "down") || data === "s" || data === "S") {
185
185
  if (this.state.direction !== "up") this.state.nextDirection = "down";
186
- } else if (isArrowRight(data) || data === "d" || data === "D") {
186
+ } else if (matchesKey(data, "right") || data === "d" || data === "D") {
187
187
  if (this.state.direction !== "left") this.state.nextDirection = "right";
188
- } else if (isArrowLeft(data) || data === "a" || data === "A") {
188
+ } else if (matchesKey(data, "left") || data === "a" || data === "A") {
189
189
  if (this.state.direction !== "right") this.state.nextDirection = "left";
190
190
  }
191
191
 
@@ -6,7 +6,7 @@
6
6
  */
7
7
 
8
8
  import type { HookAPI, Theme } from "@mariozechner/pi-coding-agent";
9
- import { isCtrlC, isEscape, truncateToWidth } from "@mariozechner/pi-tui";
9
+ import { matchesKey, truncateToWidth } from "@mariozechner/pi-tui";
10
10
 
11
11
  interface Todo {
12
12
  id: number;
@@ -35,7 +35,7 @@ class TodoListComponent {
35
35
  }
36
36
 
37
37
  handleInput(data: string): void {
38
- if (isEscape(data) || isCtrlC(data)) {
38
+ if (matchesKey(data, "escape") || matchesKey(data, "ctrl+c")) {
39
39
  this.onClose();
40
40
  }
41
41
  }
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Tools Hook
3
+ *
4
+ * Provides a /tools command to enable/disable tools interactively.
5
+ * Tool selection persists across session reloads and respects branch navigation.
6
+ *
7
+ * Usage:
8
+ * 1. Copy this file to ~/.pi/agent/hooks/ or your project's .pi/hooks/
9
+ * 2. Use /tools to open the tool selector
10
+ */
11
+
12
+ import { getSettingsListTheme } from "@mariozechner/pi-coding-agent";
13
+ import type { HookAPI, HookContext } from "@mariozechner/pi-coding-agent/hooks";
14
+ import { Container, type SettingItem, SettingsList } from "@mariozechner/pi-tui";
15
+
16
+ // State persisted to session
17
+ interface ToolsState {
18
+ enabledTools: string[];
19
+ }
20
+
21
+ export default function toolsHook(pi: HookAPI) {
22
+ // Track enabled tools
23
+ let enabledTools: Set<string> = new Set();
24
+ let allTools: string[] = [];
25
+
26
+ // Persist current state
27
+ function persistState() {
28
+ pi.appendEntry<ToolsState>("tools-config", {
29
+ enabledTools: Array.from(enabledTools),
30
+ });
31
+ }
32
+
33
+ // Apply current tool selection
34
+ function applyTools() {
35
+ pi.setActiveTools(Array.from(enabledTools));
36
+ }
37
+
38
+ // Find the last tools-config entry in the current branch
39
+ function restoreFromBranch(ctx: HookContext) {
40
+ allTools = pi.getAllTools();
41
+
42
+ // Get entries in current branch only
43
+ const branchEntries = ctx.sessionManager.getBranch();
44
+ let savedTools: string[] | undefined;
45
+
46
+ for (const entry of branchEntries) {
47
+ if (entry.type === "custom" && entry.customType === "tools-config") {
48
+ const data = entry.data as ToolsState | undefined;
49
+ if (data?.enabledTools) {
50
+ savedTools = data.enabledTools;
51
+ }
52
+ }
53
+ }
54
+
55
+ if (savedTools) {
56
+ // Restore saved tool selection (filter to only tools that still exist)
57
+ enabledTools = new Set(savedTools.filter((t: string) => allTools.includes(t)));
58
+ applyTools();
59
+ } else {
60
+ // No saved state - sync with currently active tools
61
+ enabledTools = new Set(pi.getActiveTools());
62
+ }
63
+ }
64
+
65
+ // Register /tools command
66
+ pi.registerCommand("tools", {
67
+ description: "Enable/disable tools",
68
+ handler: async (_args, ctx) => {
69
+ // Refresh tool list
70
+ allTools = pi.getAllTools();
71
+
72
+ await ctx.ui.custom((tui, theme, done) => {
73
+ // Build settings items for each tool
74
+ const items: SettingItem[] = allTools.map((tool) => ({
75
+ id: tool,
76
+ label: tool,
77
+ currentValue: enabledTools.has(tool) ? "enabled" : "disabled",
78
+ values: ["enabled", "disabled"],
79
+ }));
80
+
81
+ const container = new Container();
82
+ container.addChild(
83
+ new (class {
84
+ render(_width: number) {
85
+ return [theme.fg("accent", theme.bold("Tool Configuration")), ""];
86
+ }
87
+ invalidate() {}
88
+ })(),
89
+ );
90
+
91
+ const settingsList = new SettingsList(
92
+ items,
93
+ Math.min(items.length + 2, 15),
94
+ getSettingsListTheme(),
95
+ (id, newValue) => {
96
+ // Update enabled state and apply immediately
97
+ if (newValue === "enabled") {
98
+ enabledTools.add(id);
99
+ } else {
100
+ enabledTools.delete(id);
101
+ }
102
+ applyTools();
103
+ persistState();
104
+ },
105
+ () => {
106
+ // Close dialog
107
+ done(undefined);
108
+ },
109
+ );
110
+
111
+ container.addChild(settingsList);
112
+
113
+ const component = {
114
+ render(width: number) {
115
+ return container.render(width);
116
+ },
117
+ invalidate() {
118
+ container.invalidate();
119
+ },
120
+ handleInput(data: string) {
121
+ settingsList.handleInput?.(data);
122
+ tui.requestRender();
123
+ },
124
+ };
125
+
126
+ return component;
127
+ });
128
+ },
129
+ });
130
+
131
+ // Restore state on session start
132
+ pi.on("session_start", async (_event, ctx) => {
133
+ restoreFromBranch(ctx);
134
+ });
135
+
136
+ // Restore state when navigating the session tree
137
+ pi.on("session_tree", async (_event, ctx) => {
138
+ restoreFromBranch(ctx);
139
+ });
140
+
141
+ // Restore state after branching
142
+ pi.on("session_branch", async (_event, ctx) => {
143
+ restoreFromBranch(ctx);
144
+ });
145
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mariozechner/pi-coding-agent",
3
- "version": "0.32.3",
3
+ "version": "0.34.0",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -38,9 +38,10 @@
38
38
  "prepublishOnly": "npm run clean && npm run build"
39
39
  },
40
40
  "dependencies": {
41
- "@mariozechner/pi-agent-core": "^0.32.3",
42
- "@mariozechner/pi-ai": "^0.32.3",
43
- "@mariozechner/pi-tui": "^0.32.3",
41
+ "@crosscopy/clipboard": "^0.2.8",
42
+ "@mariozechner/pi-agent-core": "^0.34.0",
43
+ "@mariozechner/pi-ai": "^0.34.0",
44
+ "@mariozechner/pi-tui": "^0.34.0",
44
45
  "chalk": "^5.5.0",
45
46
  "cli-highlight": "^2.1.11",
46
47
  "diff": "^8.0.2",