@mariozechner/pi-coding-agent 0.37.8 → 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 (165) hide show
  1. package/CHANGELOG.md +115 -4
  2. package/README.md +11 -0
  3. package/dist/cli/args.d.ts +2 -0
  4. package/dist/cli/args.d.ts.map +1 -1
  5. package/dist/cli/args.js +8 -0
  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 +75 -35
  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 +3 -3
  16. package/dist/core/extensions/index.d.ts.map +1 -1
  17. package/dist/core/extensions/index.js +1 -1
  18. package/dist/core/extensions/index.js.map +1 -1
  19. package/dist/core/extensions/loader.d.ts +8 -6
  20. package/dist/core/extensions/loader.d.ts.map +1 -1
  21. package/dist/core/extensions/loader.js +94 -211
  22. package/dist/core/extensions/loader.js.map +1 -1
  23. package/dist/core/extensions/runner.d.ts +27 -30
  24. package/dist/core/extensions/runner.d.ts.map +1 -1
  25. package/dist/core/extensions/runner.js +102 -45
  26. package/dist/core/extensions/runner.js.map +1 -1
  27. package/dist/core/extensions/types.d.ts +155 -30
  28. package/dist/core/extensions/types.d.ts.map +1 -1
  29. package/dist/core/extensions/types.js.map +1 -1
  30. package/dist/core/extensions/wrapper.d.ts +5 -3
  31. package/dist/core/extensions/wrapper.d.ts.map +1 -1
  32. package/dist/core/extensions/wrapper.js +6 -4
  33. package/dist/core/extensions/wrapper.js.map +1 -1
  34. package/dist/core/index.d.ts +2 -2
  35. package/dist/core/index.d.ts.map +1 -1
  36. package/dist/core/index.js +1 -1
  37. package/dist/core/index.js.map +1 -1
  38. package/dist/core/model-resolver.d.ts +4 -2
  39. package/dist/core/model-resolver.d.ts.map +1 -1
  40. package/dist/core/model-resolver.js +8 -9
  41. package/dist/core/model-resolver.js.map +1 -1
  42. package/dist/core/sdk.d.ts +8 -5
  43. package/dist/core/sdk.d.ts.map +1 -1
  44. package/dist/core/sdk.js +39 -87
  45. package/dist/core/sdk.js.map +1 -1
  46. package/dist/core/settings-manager.d.ts +8 -0
  47. package/dist/core/settings-manager.d.ts.map +1 -1
  48. package/dist/core/settings-manager.js +9 -1
  49. package/dist/core/settings-manager.js.map +1 -1
  50. package/dist/core/system-prompt.d.ts.map +1 -1
  51. package/dist/core/system-prompt.js +1 -5
  52. package/dist/core/system-prompt.js.map +1 -1
  53. package/dist/core/tools/bash.d.ts +25 -1
  54. package/dist/core/tools/bash.d.ts.map +1 -1
  55. package/dist/core/tools/bash.js +103 -73
  56. package/dist/core/tools/bash.js.map +1 -1
  57. package/dist/core/tools/edit.d.ts +17 -1
  58. package/dist/core/tools/edit.d.ts.map +1 -1
  59. package/dist/core/tools/edit.js +12 -5
  60. package/dist/core/tools/edit.js.map +1 -1
  61. package/dist/core/tools/find.d.ts +18 -1
  62. package/dist/core/tools/find.d.ts.map +1 -1
  63. package/dist/core/tools/find.js +68 -18
  64. package/dist/core/tools/find.js.map +1 -1
  65. package/dist/core/tools/grep.d.ts +15 -1
  66. package/dist/core/tools/grep.d.ts.map +1 -1
  67. package/dist/core/tools/grep.js +22 -10
  68. package/dist/core/tools/grep.js.map +1 -1
  69. package/dist/core/tools/index.d.ts +7 -7
  70. package/dist/core/tools/index.d.ts.map +1 -1
  71. package/dist/core/tools/index.js +1 -1
  72. package/dist/core/tools/index.js.map +1 -1
  73. package/dist/core/tools/ls.d.ts +21 -1
  74. package/dist/core/tools/ls.d.ts.map +1 -1
  75. package/dist/core/tools/ls.js +80 -72
  76. package/dist/core/tools/ls.js.map +1 -1
  77. package/dist/core/tools/read.d.ts +14 -0
  78. package/dist/core/tools/read.d.ts.map +1 -1
  79. package/dist/core/tools/read.js +12 -5
  80. package/dist/core/tools/read.js.map +1 -1
  81. package/dist/core/tools/write.d.ts +15 -1
  82. package/dist/core/tools/write.d.ts.map +1 -1
  83. package/dist/core/tools/write.js +9 -4
  84. package/dist/core/tools/write.js.map +1 -1
  85. package/dist/index.d.ts +5 -4
  86. package/dist/index.d.ts.map +1 -1
  87. package/dist/index.js +4 -2
  88. package/dist/index.js.map +1 -1
  89. package/dist/main.d.ts.map +1 -1
  90. package/dist/main.js +58 -116
  91. package/dist/main.js.map +1 -1
  92. package/dist/modes/index.d.ts +2 -2
  93. package/dist/modes/index.d.ts.map +1 -1
  94. package/dist/modes/index.js.map +1 -1
  95. package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
  96. package/dist/modes/interactive/components/assistant-message.js +7 -3
  97. package/dist/modes/interactive/components/assistant-message.js.map +1 -1
  98. package/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
  99. package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -0
  100. package/dist/modes/interactive/components/countdown-timer.js +33 -0
  101. package/dist/modes/interactive/components/countdown-timer.js.map +1 -0
  102. package/dist/modes/interactive/components/custom-editor.d.ts +1 -1
  103. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  104. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  105. package/dist/modes/interactive/components/extension-input.d.ts +10 -2
  106. package/dist/modes/interactive/components/extension-input.d.ts.map +1 -1
  107. package/dist/modes/interactive/components/extension-input.js +18 -14
  108. package/dist/modes/interactive/components/extension-input.js.map +1 -1
  109. package/dist/modes/interactive/components/extension-selector.d.ts +10 -2
  110. package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -1
  111. package/dist/modes/interactive/components/extension-selector.js +18 -22
  112. package/dist/modes/interactive/components/extension-selector.js.map +1 -1
  113. package/dist/modes/interactive/components/tool-execution.d.ts +6 -0
  114. package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
  115. package/dist/modes/interactive/components/tool-execution.js +50 -23
  116. package/dist/modes/interactive/components/tool-execution.js.map +1 -1
  117. package/dist/modes/interactive/interactive-mode.d.ts +44 -3
  118. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  119. package/dist/modes/interactive/interactive-mode.js +440 -139
  120. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  121. package/dist/modes/interactive/theme/theme.d.ts +7 -0
  122. package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
  123. package/dist/modes/interactive/theme/theme.js +34 -0
  124. package/dist/modes/interactive/theme/theme.js.map +1 -1
  125. package/dist/modes/print-mode.d.ts +14 -7
  126. package/dist/modes/print-mode.d.ts.map +1 -1
  127. package/dist/modes/print-mode.js +45 -21
  128. package/dist/modes/print-mode.js.map +1 -1
  129. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  130. package/dist/modes/rpc/rpc-mode.js +111 -101
  131. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  132. package/dist/modes/rpc/rpc-types.d.ts +3 -0
  133. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  134. package/dist/modes/rpc/rpc-types.js.map +1 -1
  135. package/dist/utils/clipboard-image.d.ts.map +1 -1
  136. package/dist/utils/clipboard-image.js +1 -1
  137. package/dist/utils/clipboard-image.js.map +1 -1
  138. package/dist/utils/clipboard.d.ts.map +1 -1
  139. package/dist/utils/clipboard.js +35 -7
  140. package/dist/utils/clipboard.js.map +1 -1
  141. package/docs/extensions.md +211 -15
  142. package/docs/sdk.md +68 -9
  143. package/docs/tui.md +81 -4
  144. package/examples/extensions/README.md +3 -0
  145. package/examples/extensions/claude-rules.ts +5 -2
  146. package/examples/extensions/handoff.ts +1 -1
  147. package/examples/extensions/interactive-shell.ts +196 -0
  148. package/examples/extensions/mac-system-theme.ts +25 -0
  149. package/examples/extensions/modal-editor.ts +85 -0
  150. package/examples/extensions/overlay-test.ts +145 -0
  151. package/examples/extensions/pirate.ts +7 -4
  152. package/examples/extensions/preset.ts +3 -3
  153. package/examples/extensions/qna.ts +1 -1
  154. package/examples/extensions/rainbow-editor.ts +95 -0
  155. package/examples/extensions/shutdown-command.ts +63 -0
  156. package/examples/extensions/snake.ts +1 -1
  157. package/examples/extensions/ssh.ts +220 -0
  158. package/examples/extensions/timed-confirm.ts +32 -25
  159. package/examples/extensions/todo.ts +1 -1
  160. package/examples/extensions/tool-override.ts +143 -0
  161. package/examples/extensions/tools.ts +1 -1
  162. package/examples/extensions/with-deps/package-lock.json +2 -2
  163. package/examples/extensions/with-deps/package.json +1 -1
  164. package/examples/sdk/04-skills.ts +4 -1
  165. package/package.json +6 -6
@@ -14,7 +14,7 @@
14
14
  */
15
15
  import { isContextOverflow, modelsAreEqual, supportsXhigh } from "@mariozechner/pi-ai";
16
16
  import { getAuthPath } from "../config.js";
17
- import { executeBash as executeBashCommand } from "./bash-executor.js";
17
+ import { executeBash as executeBashCommand, executeBashWithOperations } from "./bash-executor.js";
18
18
  import { calculateContextTokens, collectEntriesForBranchSummary, compact, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
19
19
  import { exportSessionToHtml } from "./export-html/index.js";
20
20
  import { expandPromptTemplate } from "./prompt-templates.js";
@@ -59,6 +59,8 @@ export class AgentSession {
59
59
  // Extension system
60
60
  _extensionRunner = undefined;
61
61
  _turnIndex = 0;
62
+ _skills;
63
+ _skillWarnings;
62
64
  _skillsSettings;
63
65
  // Model registry for API key resolution
64
66
  _modelRegistry;
@@ -75,6 +77,8 @@ export class AgentSession {
75
77
  this._scopedModels = config.scopedModels ?? [];
76
78
  this._promptTemplates = config.promptTemplates ?? [];
77
79
  this._extensionRunner = config.extensionRunner;
80
+ this._skills = config.skills ?? [];
81
+ this._skillWarnings = config.skillWarnings ?? [];
78
82
  this._skillsSettings = config.skillsSettings;
79
83
  this._modelRegistry = config.modelRegistry;
80
84
  this._toolRegistry = config.toolRegistry ?? new Map();
@@ -288,6 +292,10 @@ export class AgentSession {
288
292
  get isStreaming() {
289
293
  return this.agent.state.isStreaming;
290
294
  }
295
+ /** Current retry attempt (0 if not retrying) */
296
+ get retryAttempt() {
297
+ return this._retryAttempt;
298
+ }
291
299
  /**
292
300
  * Get the names of currently active tools.
293
301
  * Returns the names of tools currently set on the agent.
@@ -432,7 +440,7 @@ export class AgentSession {
432
440
  this._pendingNextTurnMessages = [];
433
441
  // Emit before_agent_start extension event
434
442
  if (this._extensionRunner) {
435
- const result = await this._extensionRunner.emitBeforeAgentStart(expandedText, options?.images);
443
+ const result = await this._extensionRunner.emitBeforeAgentStart(expandedText, options?.images, this._baseSystemPrompt);
436
444
  // Add all custom messages from extensions
437
445
  if (result?.messages) {
438
446
  for (const msg of result.messages) {
@@ -446,12 +454,12 @@ export class AgentSession {
446
454
  });
447
455
  }
448
456
  }
449
- // Apply extension systemPromptAppend on top of base prompt
450
- if (result?.systemPromptAppend) {
451
- this.agent.setSystemPrompt(`${this._baseSystemPrompt}\n\n${result.systemPromptAppend}`);
457
+ // Apply extension-modified system prompt, or reset to base
458
+ if (result?.systemPrompt) {
459
+ this.agent.setSystemPrompt(result.systemPrompt);
452
460
  }
453
461
  else {
454
- // Ensure we're using the base prompt (in case previous turn had appends)
462
+ // Ensure we're using the base prompt (in case previous turn had modifications)
455
463
  this.agent.setSystemPrompt(this._baseSystemPrompt);
456
464
  }
457
465
  }
@@ -656,6 +664,14 @@ export class AgentSession {
656
664
  get skillsSettings() {
657
665
  return this._skillsSettings;
658
666
  }
667
+ /** Skills loaded by SDK (empty if --no-skills or skills: [] was passed) */
668
+ get skills() {
669
+ return this._skills;
670
+ }
671
+ /** Skill loading warnings captured by SDK */
672
+ get skillWarnings() {
673
+ return this._skillWarnings;
674
+ }
659
675
  /**
660
676
  * Abort current operation and wait for agent to become idle.
661
677
  */
@@ -1001,8 +1017,20 @@ export class AgentSession {
1001
1017
  if (skipAbortedCheck && assistantMessage.stopReason === "aborted")
1002
1018
  return;
1003
1019
  const contextWindow = this.model?.contextWindow ?? 0;
1020
+ // Skip overflow check if the message came from a different model.
1021
+ // This handles the case where user switched from a smaller-context model (e.g. opus)
1022
+ // to a larger-context model (e.g. codex) - the overflow error from the old model
1023
+ // shouldn't trigger compaction for the new model.
1024
+ const sameModel = this.model && assistantMessage.provider === this.model.provider && assistantMessage.model === this.model.id;
1025
+ // Skip overflow check if the error is from before a compaction in the current path.
1026
+ // This handles the case where an error was kept after compaction (in the "kept" region).
1027
+ // The error shouldn't trigger another compaction since we already compacted.
1028
+ // Example: opus fails → switch to codex → compact → switch back to opus → opus error
1029
+ // is still in context but shouldn't trigger compaction again.
1030
+ const compactionEntry = this.sessionManager.getBranch().find((e) => e.type === "compaction");
1031
+ const errorIsFromBeforeCompaction = compactionEntry && assistantMessage.timestamp < new Date(compactionEntry.timestamp).getTime();
1004
1032
  // Case 1: Overflow - LLM returned context overflow error
1005
- if (isContextOverflow(assistantMessage, contextWindow)) {
1033
+ if (sameModel && !errorIsFromBeforeCompaction && isContextOverflow(assistantMessage, contextWindow)) {
1006
1034
  // Remove the error message from agent state (it IS saved to session for history,
1007
1035
  // but we don't want it in context for the retry)
1008
1036
  const messages = this.agent.state.messages;
@@ -1244,7 +1272,7 @@ export class AgentSession {
1244
1272
  */
1245
1273
  abortRetry() {
1246
1274
  this._retryAbortController?.abort();
1247
- this._retryAttempt = 0;
1275
+ // Note: _retryAttempt is reset in the catch block of _autoRetry
1248
1276
  this._resolveRetry();
1249
1277
  }
1250
1278
  /**
@@ -1279,43 +1307,55 @@ export class AgentSession {
1279
1307
  * @param command The bash command to execute
1280
1308
  * @param onChunk Optional streaming callback for output
1281
1309
  * @param options.excludeFromContext If true, command output won't be sent to LLM (!! prefix)
1310
+ * @param options.operations Custom BashOperations for remote execution
1282
1311
  */
1283
1312
  async executeBash(command, onChunk, options) {
1284
1313
  this._bashAbortController = new AbortController();
1285
1314
  try {
1286
- const result = await executeBashCommand(command, {
1287
- onChunk,
1288
- signal: this._bashAbortController.signal,
1289
- });
1290
- // Create and save message
1291
- const bashMessage = {
1292
- role: "bashExecution",
1293
- command,
1294
- output: result.output,
1295
- exitCode: result.exitCode,
1296
- cancelled: result.cancelled,
1297
- truncated: result.truncated,
1298
- fullOutputPath: result.fullOutputPath,
1299
- timestamp: Date.now(),
1300
- excludeFromContext: options?.excludeFromContext,
1301
- };
1302
- // If agent is streaming, defer adding to avoid breaking tool_use/tool_result ordering
1303
- if (this.isStreaming) {
1304
- // Queue for later - will be flushed on agent_end
1305
- this._pendingBashMessages.push(bashMessage);
1306
- }
1307
- else {
1308
- // Add to agent state immediately
1309
- this.agent.appendMessage(bashMessage);
1310
- // Save to session
1311
- this.sessionManager.appendMessage(bashMessage);
1312
- }
1315
+ const result = options?.operations
1316
+ ? await executeBashWithOperations(command, process.cwd(), options.operations, {
1317
+ onChunk,
1318
+ signal: this._bashAbortController.signal,
1319
+ })
1320
+ : await executeBashCommand(command, {
1321
+ onChunk,
1322
+ signal: this._bashAbortController.signal,
1323
+ });
1324
+ this.recordBashResult(command, result, options);
1313
1325
  return result;
1314
1326
  }
1315
1327
  finally {
1316
1328
  this._bashAbortController = undefined;
1317
1329
  }
1318
1330
  }
1331
+ /**
1332
+ * Record a bash execution result in session history.
1333
+ * Used by executeBash and by extensions that handle bash execution themselves.
1334
+ */
1335
+ recordBashResult(command, result, options) {
1336
+ const bashMessage = {
1337
+ role: "bashExecution",
1338
+ command,
1339
+ output: result.output,
1340
+ exitCode: result.exitCode,
1341
+ cancelled: result.cancelled,
1342
+ truncated: result.truncated,
1343
+ fullOutputPath: result.fullOutputPath,
1344
+ timestamp: Date.now(),
1345
+ excludeFromContext: options?.excludeFromContext,
1346
+ };
1347
+ // If agent is streaming, defer adding to avoid breaking tool_use/tool_result ordering
1348
+ if (this.isStreaming) {
1349
+ // Queue for later - will be flushed on agent_end
1350
+ this._pendingBashMessages.push(bashMessage);
1351
+ }
1352
+ else {
1353
+ // Add to agent state immediately
1354
+ this.agent.appendMessage(bashMessage);
1355
+ // Save to session
1356
+ this.sessionManager.appendMessage(bashMessage);
1357
+ }
1358
+ }
1319
1359
  /**
1320
1360
  * Cancel running bash command.
1321
1361
  */