@mariozechner/pi-coding-agent 0.38.0 → 0.39.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 (109) hide show
  1. package/CHANGELOG.md +31 -0
  2. package/README.md +1 -0
  3. package/dist/cli/args.d.ts +1 -0
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +5 -1
  6. package/dist/cli/args.js.map +1 -1
  7. package/dist/core/agent-session.d.ts +23 -0
  8. package/dist/core/agent-session.d.ts.map +1 -1
  9. package/dist/core/agent-session.js +62 -34
  10. package/dist/core/agent-session.js.map +1 -1
  11. package/dist/core/bash-executor.d.ts +6 -0
  12. package/dist/core/bash-executor.d.ts.map +1 -1
  13. package/dist/core/bash-executor.js +77 -0
  14. package/dist/core/bash-executor.js.map +1 -1
  15. package/dist/core/extensions/index.d.ts +1 -1
  16. package/dist/core/extensions/index.d.ts.map +1 -1
  17. package/dist/core/extensions/index.js.map +1 -1
  18. package/dist/core/extensions/runner.d.ts +4 -3
  19. package/dist/core/extensions/runner.d.ts.map +1 -1
  20. package/dist/core/extensions/runner.js +44 -7
  21. package/dist/core/extensions/runner.js.map +1 -1
  22. package/dist/core/extensions/types.d.ts +39 -3
  23. package/dist/core/extensions/types.d.ts.map +1 -1
  24. package/dist/core/extensions/types.js.map +1 -1
  25. package/dist/core/index.d.ts +1 -1
  26. package/dist/core/index.d.ts.map +1 -1
  27. package/dist/core/index.js +1 -1
  28. package/dist/core/index.js.map +1 -1
  29. package/dist/core/sdk.d.ts +5 -2
  30. package/dist/core/sdk.d.ts.map +1 -1
  31. package/dist/core/sdk.js +20 -12
  32. package/dist/core/sdk.js.map +1 -1
  33. package/dist/core/system-prompt.d.ts.map +1 -1
  34. package/dist/core/system-prompt.js +1 -5
  35. package/dist/core/system-prompt.js.map +1 -1
  36. package/dist/core/tools/bash.d.ts +25 -1
  37. package/dist/core/tools/bash.d.ts.map +1 -1
  38. package/dist/core/tools/bash.js +103 -73
  39. package/dist/core/tools/bash.js.map +1 -1
  40. package/dist/core/tools/edit.d.ts +17 -1
  41. package/dist/core/tools/edit.d.ts.map +1 -1
  42. package/dist/core/tools/edit.js +12 -5
  43. package/dist/core/tools/edit.js.map +1 -1
  44. package/dist/core/tools/find.d.ts +18 -1
  45. package/dist/core/tools/find.d.ts.map +1 -1
  46. package/dist/core/tools/find.js +68 -18
  47. package/dist/core/tools/find.js.map +1 -1
  48. package/dist/core/tools/grep.d.ts +15 -1
  49. package/dist/core/tools/grep.d.ts.map +1 -1
  50. package/dist/core/tools/grep.js +22 -10
  51. package/dist/core/tools/grep.js.map +1 -1
  52. package/dist/core/tools/index.d.ts +7 -7
  53. package/dist/core/tools/index.d.ts.map +1 -1
  54. package/dist/core/tools/index.js +1 -1
  55. package/dist/core/tools/index.js.map +1 -1
  56. package/dist/core/tools/ls.d.ts +21 -1
  57. package/dist/core/tools/ls.d.ts.map +1 -1
  58. package/dist/core/tools/ls.js +80 -72
  59. package/dist/core/tools/ls.js.map +1 -1
  60. package/dist/core/tools/read.d.ts +14 -0
  61. package/dist/core/tools/read.d.ts.map +1 -1
  62. package/dist/core/tools/read.js +12 -5
  63. package/dist/core/tools/read.js.map +1 -1
  64. package/dist/core/tools/write.d.ts +15 -1
  65. package/dist/core/tools/write.d.ts.map +1 -1
  66. package/dist/core/tools/write.js +9 -4
  67. package/dist/core/tools/write.js.map +1 -1
  68. package/dist/index.d.ts +3 -3
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +1 -1
  71. package/dist/index.js.map +1 -1
  72. package/dist/main.d.ts.map +1 -1
  73. package/dist/main.js +11 -1
  74. package/dist/main.js.map +1 -1
  75. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  76. package/dist/modes/interactive/components/assistant-message.js +7 -3
  77. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  78. package/dist/modes/interactive/components/tool-execution.d.ts +6 -0
  79. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  80. package/dist/modes/interactive/components/tool-execution.js +50 -23
  81. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  82. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  83. package/dist/modes/interactive/interactive-mode.js +153 -46
  84. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  85. package/dist/modes/interactive/theme/theme.d.ts +7 -0
  86. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  87. package/dist/modes/interactive/theme/theme.js +34 -0
  88. package/dist/modes/interactive/theme/theme.js.map +1 -1
  89. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  90. package/dist/modes/rpc/rpc-mode.js +10 -0
  91. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  92. package/dist/utils/clipboard.d.ts.map +1 -1
  93. package/dist/utils/clipboard.js +35 -7
  94. package/dist/utils/clipboard.js.map +1 -1
  95. package/docs/extensions.md +101 -6
  96. package/docs/sdk.md +3 -3
  97. package/examples/extensions/README.md +2 -0
  98. package/examples/extensions/claude-rules.ts +5 -2
  99. package/examples/extensions/interactive-shell.ts +196 -0
  100. package/examples/extensions/mac-system-theme.ts +25 -0
  101. package/examples/extensions/overlay-test.ts +145 -0
  102. package/examples/extensions/pirate.ts +7 -4
  103. package/examples/extensions/preset.ts +2 -2
  104. package/examples/extensions/ssh.ts +220 -0
  105. package/examples/extensions/tool-override.ts +143 -0
  106. package/examples/extensions/with-deps/package-lock.json +2 -2
  107. package/examples/extensions/with-deps/package.json +1 -1
  108. package/examples/sdk/04-skills.ts +4 -1
  109. package/package.json +5 -5
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Tool Override Example - Demonstrates overriding built-in tools
3
+ *
4
+ * Extensions can register tools with the same name as built-in tools to replace them.
5
+ * This is useful for:
6
+ * - Adding logging or auditing to tool calls
7
+ * - Implementing access control or sandboxing
8
+ * - Routing tool calls to remote systems (e.g., pi-ssh-remote)
9
+ * - Modifying tool behavior for specific workflows
10
+ *
11
+ * This example overrides the `read` tool to:
12
+ * 1. Log all file access to a log file
13
+ * 2. Block access to sensitive paths (e.g., .env files)
14
+ * 3. Delegate to the original read implementation for allowed files
15
+ *
16
+ * Since no custom renderCall/renderResult are provided, the built-in renderer
17
+ * is used automatically (syntax highlighting, line numbers, truncation warnings).
18
+ *
19
+ * Usage:
20
+ * pi -e ./tool-override.ts
21
+ */
22
+
23
+ import type { TextContent } from "@mariozechner/pi-ai";
24
+ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
25
+ import { Type } from "@sinclair/typebox";
26
+ import { appendFileSync, constants, readFileSync } from "fs";
27
+ import { access, readFile } from "fs/promises";
28
+ import { homedir } from "os";
29
+ import { join, resolve } from "path";
30
+
31
+ const LOG_FILE = join(homedir(), ".pi", "agent", "read-access.log");
32
+
33
+ // Paths that are blocked from reading
34
+ const BLOCKED_PATTERNS = [
35
+ /\.env$/,
36
+ /\.env\..+$/,
37
+ /secrets?\.(json|yaml|yml|toml)$/i,
38
+ /credentials?\.(json|yaml|yml|toml)$/i,
39
+ /\/\.ssh\//,
40
+ /\/\.aws\//,
41
+ /\/\.gnupg\//,
42
+ ];
43
+
44
+ function isBlockedPath(path: string): boolean {
45
+ return BLOCKED_PATTERNS.some((pattern) => pattern.test(path));
46
+ }
47
+
48
+ function logAccess(path: string, allowed: boolean, reason?: string) {
49
+ const timestamp = new Date().toISOString();
50
+ const status = allowed ? "ALLOWED" : "BLOCKED";
51
+ const msg = reason ? ` (${reason})` : "";
52
+ const line = `[${timestamp}] ${status}: ${path}${msg}\n`;
53
+
54
+ try {
55
+ appendFileSync(LOG_FILE, line);
56
+ } catch {
57
+ // Ignore logging errors
58
+ }
59
+ }
60
+
61
+ const readSchema = Type.Object({
62
+ path: Type.String({ description: "Path to the file to read (relative or absolute)" }),
63
+ offset: Type.Optional(Type.Number({ description: "Line number to start reading from (1-indexed)" })),
64
+ limit: Type.Optional(Type.Number({ description: "Maximum number of lines to read" })),
65
+ });
66
+
67
+ export default function (pi: ExtensionAPI) {
68
+ pi.registerTool({
69
+ name: "read", // Same name as built-in - this will override it
70
+ label: "read (audited)",
71
+ description:
72
+ "Read the contents of a file with access logging. Some sensitive paths (.env, secrets, credentials) are blocked.",
73
+ parameters: readSchema,
74
+
75
+ async execute(_toolCallId, params, _onUpdate, ctx) {
76
+ const { path, offset, limit } = params;
77
+ const absolutePath = resolve(ctx.cwd, path);
78
+
79
+ // Check if path is blocked
80
+ if (isBlockedPath(absolutePath)) {
81
+ logAccess(absolutePath, false, "matches blocked pattern");
82
+ return {
83
+ content: [
84
+ {
85
+ type: "text",
86
+ text: `Access denied: "${path}" matches a blocked pattern (sensitive file). This tool blocks access to .env files, secrets, credentials, and SSH/AWS/GPG directories.`,
87
+ },
88
+ ],
89
+ details: { blocked: true },
90
+ };
91
+ }
92
+
93
+ // Log allowed access
94
+ logAccess(absolutePath, true);
95
+
96
+ // Perform the actual read (simplified implementation)
97
+ try {
98
+ await access(absolutePath, constants.R_OK);
99
+ const content = await readFile(absolutePath, "utf-8");
100
+ const lines = content.split("\n");
101
+
102
+ // Apply offset and limit
103
+ const startLine = offset ? Math.max(0, offset - 1) : 0;
104
+ const endLine = limit ? startLine + limit : lines.length;
105
+ const selectedLines = lines.slice(startLine, endLine);
106
+
107
+ // Basic truncation (50KB limit)
108
+ let text = selectedLines.join("\n");
109
+ const maxBytes = 50 * 1024;
110
+ if (Buffer.byteLength(text, "utf-8") > maxBytes) {
111
+ text = `${text.slice(0, maxBytes)}\n\n[Output truncated at 50KB]`;
112
+ }
113
+
114
+ return {
115
+ content: [{ type: "text", text }] as TextContent[],
116
+ details: { lines: lines.length },
117
+ };
118
+ } catch (error: any) {
119
+ return {
120
+ content: [{ type: "text", text: `Error reading file: ${error.message}` }] as TextContent[],
121
+ details: { error: true },
122
+ };
123
+ }
124
+ },
125
+
126
+ // No renderCall/renderResult - uses built-in renderer automatically
127
+ // (syntax highlighting, line numbers, truncation warnings, etc.)
128
+ });
129
+
130
+ // Also register a command to view the access log
131
+ pi.registerCommand("read-log", {
132
+ description: "View the file access log",
133
+ handler: async (_args, ctx) => {
134
+ try {
135
+ const log = readFileSync(LOG_FILE, "utf-8");
136
+ const lines = log.trim().split("\n").slice(-20); // Last 20 entries
137
+ ctx.ui.notify(`Recent file access:\n${lines.join("\n")}`, "info");
138
+ } catch {
139
+ ctx.ui.notify("No access log found", "info");
140
+ }
141
+ },
142
+ });
143
+ }
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
- "version": "1.2.0",
3
+ "version": "1.3.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pi-extension-with-deps",
9
- "version": "1.2.0",
9
+ "version": "1.3.0",
10
10
  "dependencies": {
11
11
  "ms": "^2.1.3"
12
12
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "pi-extension-with-deps",
3
3
  "private": true,
4
- "version": "1.2.0",
4
+ "version": "1.3.0",
5
5
  "type": "module",
6
6
  "scripts": {
7
7
  "clean": "echo 'nothing to clean'",
@@ -8,11 +8,14 @@
8
8
  import { createAgentSession, discoverSkills, SessionManager, type Skill } from "@mariozechner/pi-coding-agent";
9
9
 
10
10
  // Discover all skills from cwd/.pi/skills, ~/.pi/agent/skills, etc.
11
- const allSkills = discoverSkills();
11
+ const { skills: allSkills, warnings } = discoverSkills();
12
12
  console.log(
13
13
  "Discovered skills:",
14
14
  allSkills.map((s) => s.name),
15
15
  );
16
+ if (warnings.length > 0) {
17
+ console.log("Warnings:", warnings);
18
+ }
16
19
 
17
20
  // Filter to specific skills
18
21
  const filteredSkills = allSkills.filter((s) => s.name.includes("browser") || s.name.includes("search"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mariozechner/pi-coding-agent",
3
- "version": "0.38.0",
3
+ "version": "0.39.0",
4
4
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
5
5
  "type": "module",
6
6
  "piConfig": {
@@ -39,9 +39,9 @@
39
39
  },
40
40
  "dependencies": {
41
41
  "@mariozechner/clipboard": "^0.3.0",
42
- "@mariozechner/pi-agent-core": "^0.38.0",
43
- "@mariozechner/pi-ai": "^0.38.0",
44
- "@mariozechner/pi-tui": "^0.38.0",
42
+ "@mariozechner/pi-agent-core": "^0.39.0",
43
+ "@mariozechner/pi-ai": "^0.39.0",
44
+ "@mariozechner/pi-tui": "^0.39.0",
45
45
  "chalk": "^5.5.0",
46
46
  "cli-highlight": "^2.1.11",
47
47
  "diff": "^8.0.2",
@@ -58,7 +58,7 @@
58
58
  "@types/ms": "^2.1.0",
59
59
  "@types/node": "^24.3.0",
60
60
  "@types/proper-lockfile": "^4.1.4",
61
- "shx": "^0.3.4",
61
+ "shx": "^0.4.0",
62
62
  "typescript": "^5.7.3",
63
63
  "vitest": "^3.2.4"
64
64
  },