@mariozechner/pi-coding-agent 0.46.0 → 0.47.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 (90) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/core/agent-session.d.ts +11 -3
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +81 -14
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/export-html/ansi-to-html.d.ts +22 -0
  7. package/dist/core/export-html/ansi-to-html.d.ts.map +1 -0
  8. package/dist/core/export-html/ansi-to-html.js +249 -0
  9. package/dist/core/export-html/ansi-to-html.js.map +1 -0
  10. package/dist/core/export-html/index.d.ts +17 -0
  11. package/dist/core/export-html/index.d.ts.map +1 -1
  12. package/dist/core/export-html/index.js +52 -23
  13. package/dist/core/export-html/index.js.map +1 -1
  14. package/dist/core/export-html/template.css +0 -33
  15. package/dist/core/export-html/template.js +171 -18
  16. package/dist/core/export-html/tool-renderer.d.ts +35 -0
  17. package/dist/core/export-html/tool-renderer.d.ts.map +1 -0
  18. package/dist/core/export-html/tool-renderer.js +57 -0
  19. package/dist/core/export-html/tool-renderer.js.map +1 -0
  20. package/dist/core/extensions/index.d.ts +1 -1
  21. package/dist/core/extensions/index.d.ts.map +1 -1
  22. package/dist/core/extensions/index.js.map +1 -1
  23. package/dist/core/extensions/runner.d.ts +5 -1
  24. package/dist/core/extensions/runner.d.ts.map +1 -1
  25. package/dist/core/extensions/runner.js +41 -0
  26. package/dist/core/extensions/runner.js.map +1 -1
  27. package/dist/core/extensions/types.d.ts +24 -1
  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/prompt-templates.d.ts.map +1 -1
  31. package/dist/core/prompt-templates.js +4 -27
  32. package/dist/core/prompt-templates.js.map +1 -1
  33. package/dist/core/skills.d.ts.map +1 -1
  34. package/dist/core/skills.js +6 -37
  35. package/dist/core/skills.js.map +1 -1
  36. package/dist/core/system-prompt.d.ts.map +1 -1
  37. package/dist/core/system-prompt.js +19 -14
  38. package/dist/core/system-prompt.js.map +1 -1
  39. package/dist/core/tools/path-utils.d.ts +1 -0
  40. package/dist/core/tools/path-utils.d.ts.map +1 -1
  41. package/dist/core/tools/path-utils.js +7 -0
  42. package/dist/core/tools/path-utils.js.map +1 -1
  43. package/dist/core/tools/read.d.ts.map +1 -1
  44. package/dist/core/tools/read.js +13 -2
  45. package/dist/core/tools/read.js.map +1 -1
  46. package/dist/index.d.ts +2 -1
  47. package/dist/index.d.ts.map +1 -1
  48. package/dist/index.js +1 -0
  49. package/dist/index.js.map +1 -1
  50. package/dist/main.d.ts.map +1 -1
  51. package/dist/main.js +32 -0
  52. package/dist/main.js.map +1 -1
  53. package/dist/modes/interactive/components/custom-editor.d.ts +2 -2
  54. package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -1
  55. package/dist/modes/interactive/components/custom-editor.js +2 -2
  56. package/dist/modes/interactive/components/custom-editor.js.map +1 -1
  57. package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -1
  58. package/dist/modes/interactive/components/extension-editor.js +1 -1
  59. package/dist/modes/interactive/components/extension-editor.js.map +1 -1
  60. package/dist/modes/interactive/components/tree-selector.d.ts +7 -0
  61. package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -1
  62. package/dist/modes/interactive/components/tree-selector.js +140 -4
  63. package/dist/modes/interactive/components/tree-selector.js.map +1 -1
  64. package/dist/modes/interactive/interactive-mode.d.ts +0 -1
  65. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  66. package/dist/modes/interactive/interactive-mode.js +4 -29
  67. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  68. package/dist/modes/print-mode.d.ts.map +1 -1
  69. package/dist/modes/print-mode.js +1 -1
  70. package/dist/modes/print-mode.js.map +1 -1
  71. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  72. package/dist/modes/rpc/rpc-mode.js +1 -0
  73. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  74. package/dist/utils/frontmatter.d.ts +8 -0
  75. package/dist/utils/frontmatter.d.ts.map +1 -0
  76. package/dist/utils/frontmatter.js +26 -0
  77. package/dist/utils/frontmatter.js.map +1 -0
  78. package/docs/extensions.md +51 -0
  79. package/docs/rpc.md +15 -15
  80. package/docs/tui.md +26 -0
  81. package/examples/extensions/input-transform.ts +43 -0
  82. package/examples/extensions/modal-editor.ts +1 -1
  83. package/examples/extensions/overlay-test.ts +8 -3
  84. package/examples/extensions/question.ts +1 -1
  85. package/examples/extensions/questionnaire.ts +1 -1
  86. package/examples/extensions/rainbow-editor.ts +1 -8
  87. package/examples/extensions/subagent/agents.ts +3 -32
  88. package/examples/extensions/with-deps/package-lock.json +2 -2
  89. package/examples/extensions/with-deps/package.json +1 -1
  90. package/package.json +6 -5
@@ -12,11 +12,15 @@
12
12
  *
13
13
  * Modes use this class and add their own I/O layer on top.
14
14
  */
15
+ import { readFileSync } from "node:fs";
15
16
  import { isContextOverflow, modelsAreEqual, supportsXhigh } from "@mariozechner/pi-ai";
16
17
  import { getAuthPath } from "../config.js";
18
+ import { theme } from "../modes/interactive/theme/theme.js";
19
+ import { stripFrontmatter } from "../utils/frontmatter.js";
17
20
  import { executeBash as executeBashCommand, executeBashWithOperations } from "./bash-executor.js";
18
21
  import { calculateContextTokens, collectEntriesForBranchSummary, compact, generateBranchSummary, prepareCompaction, shouldCompact, } from "./compaction/index.js";
19
22
  import { exportSessionToHtml } from "./export-html/index.js";
23
+ import { createToolHtmlRenderer } from "./export-html/tool-renderer.js";
20
24
  import { expandPromptTemplate } from "./prompt-templates.js";
21
25
  // ============================================================================
22
26
  // Constants
@@ -394,8 +398,25 @@ export class AgentSession {
394
398
  return;
395
399
  }
396
400
  }
397
- // Expand file-based prompt templates if requested
398
- const expandedText = expandPromptTemplates ? expandPromptTemplate(text, [...this._promptTemplates]) : text;
401
+ // Emit input event for extension interception (before skill/template expansion)
402
+ let currentText = text;
403
+ let currentImages = options?.images;
404
+ if (this._extensionRunner?.hasHandlers("input")) {
405
+ const inputResult = await this._extensionRunner.emitInput(currentText, currentImages, options?.source ?? "interactive");
406
+ if (inputResult.action === "handled") {
407
+ return;
408
+ }
409
+ if (inputResult.action === "transform") {
410
+ currentText = inputResult.text;
411
+ currentImages = inputResult.images ?? currentImages;
412
+ }
413
+ }
414
+ // Expand skill commands (/skill:name args) and prompt templates (/template args)
415
+ let expandedText = currentText;
416
+ if (expandPromptTemplates) {
417
+ expandedText = this._expandSkillCommand(expandedText);
418
+ expandedText = expandPromptTemplate(expandedText, [...this._promptTemplates]);
419
+ }
399
420
  // If streaming, queue via steer() or followUp() based on option
400
421
  if (this.isStreaming) {
401
422
  if (!options?.streamingBehavior) {
@@ -432,8 +453,8 @@ export class AgentSession {
432
453
  const messages = [];
433
454
  // Add user message
434
455
  const userContent = [{ type: "text", text: expandedText }];
435
- if (options?.images) {
436
- userContent.push(...options.images);
456
+ if (currentImages) {
457
+ userContent.push(...currentImages);
437
458
  }
438
459
  messages.push({
439
460
  role: "user",
@@ -447,7 +468,7 @@ export class AgentSession {
447
468
  this._pendingNextTurnMessages = [];
448
469
  // Emit before_agent_start extension event
449
470
  if (this._extensionRunner) {
450
- const result = await this._extensionRunner.emitBeforeAgentStart(expandedText, options?.images, this._baseSystemPrompt);
471
+ const result = await this._extensionRunner.emitBeforeAgentStart(expandedText, currentImages, this._baseSystemPrompt);
451
472
  // Add all custom messages from extensions
452
473
  if (result?.messages) {
453
474
  for (const msg of result.messages) {
@@ -502,10 +523,41 @@ export class AgentSession {
502
523
  return true;
503
524
  }
504
525
  }
526
+ /**
527
+ * Expand skill commands (/skill:name args) to their full content.
528
+ * Returns the expanded text, or the original text if not a skill command or skill not found.
529
+ * Emits errors via extension runner if file read fails.
530
+ */
531
+ _expandSkillCommand(text) {
532
+ if (!text.startsWith("/skill:"))
533
+ return text;
534
+ const spaceIndex = text.indexOf(" ");
535
+ const skillName = spaceIndex === -1 ? text.slice(7) : text.slice(7, spaceIndex);
536
+ const args = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1).trim();
537
+ const skill = this._skills.find((s) => s.name === skillName);
538
+ if (!skill)
539
+ return text; // Unknown skill, pass through
540
+ try {
541
+ const content = readFileSync(skill.filePath, "utf-8");
542
+ const body = stripFrontmatter(content).trim();
543
+ const header = `Skill location: ${skill.filePath}\nReferences are relative to ${skill.baseDir}.`;
544
+ const skillMessage = `${header}\n\n${body}`;
545
+ return args ? `${skillMessage}\n\n---\n\nUser: ${args}` : skillMessage;
546
+ }
547
+ catch (err) {
548
+ // Emit error like extension commands do
549
+ this._extensionRunner?.emitError({
550
+ extensionPath: skill.filePath,
551
+ event: "skill_expansion",
552
+ error: err instanceof Error ? err.message : String(err),
553
+ });
554
+ return text; // Return original on error
555
+ }
556
+ }
505
557
  /**
506
558
  * Queue a steering message to interrupt the agent mid-run.
507
559
  * Delivered after current tool execution, skips remaining tools.
508
- * Expands file-based prompt templates. Errors on extension commands.
560
+ * Expands skill commands and prompt templates. Errors on extension commands.
509
561
  * @throws Error if text is an extension command
510
562
  */
511
563
  async steer(text) {
@@ -513,14 +565,15 @@ export class AgentSession {
513
565
  if (text.startsWith("/")) {
514
566
  this._throwIfExtensionCommand(text);
515
567
  }
516
- // Expand file-based prompt templates
517
- const expandedText = expandPromptTemplate(text, [...this._promptTemplates]);
568
+ // Expand skill commands and prompt templates
569
+ let expandedText = this._expandSkillCommand(text);
570
+ expandedText = expandPromptTemplate(expandedText, [...this._promptTemplates]);
518
571
  await this._queueSteer(expandedText);
519
572
  }
520
573
  /**
521
574
  * Queue a follow-up message to be processed after the agent finishes.
522
575
  * Delivered only when agent has no more tool calls or steering messages.
523
- * Expands file-based prompt templates. Errors on extension commands.
576
+ * Expands skill commands and prompt templates. Errors on extension commands.
524
577
  * @throws Error if text is an extension command
525
578
  */
526
579
  async followUp(text) {
@@ -528,8 +581,9 @@ export class AgentSession {
528
581
  if (text.startsWith("/")) {
529
582
  this._throwIfExtensionCommand(text);
530
583
  }
531
- // Expand file-based prompt templates
532
- const expandedText = expandPromptTemplate(text, [...this._promptTemplates]);
584
+ // Expand skill commands and prompt templates
585
+ let expandedText = this._expandSkillCommand(text);
586
+ expandedText = expandPromptTemplate(expandedText, [...this._promptTemplates]);
533
587
  await this._queueFollowUp(expandedText);
534
588
  }
535
589
  /**
@@ -641,6 +695,7 @@ export class AgentSession {
641
695
  expandPromptTemplates: false,
642
696
  streamingBehavior: options?.deliverAs,
643
697
  images,
698
+ source: "extension",
644
699
  });
645
700
  }
646
701
  /**
@@ -1203,8 +1258,8 @@ export class AgentSession {
1203
1258
  if (isContextOverflow(message, contextWindow))
1204
1259
  return false;
1205
1260
  const err = message.errorMessage;
1206
- // Match: overloaded_error, rate limit, 429, 500, 502, 503, 504, service unavailable, connection error, other side closed, fetch failed
1207
- return /overloaded|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server error|internal error|connection.?error|other side closed|fetch failed/i.test(err);
1261
+ // Match: overloaded_error, rate limit, 429, 500, 502, 503, 504, service unavailable, connection errors, fetch failed
1262
+ return /overloaded|rate.?limit|too many requests|429|500|502|503|504|service.?unavailable|server error|internal error|connection.?error|connection.?refused|other side closed|fetch failed|upstream.?connect|reset before headers/i.test(err);
1208
1263
  }
1209
1264
  /**
1210
1265
  * Handle retryable errors with exponential backoff.
@@ -1743,7 +1798,19 @@ export class AgentSession {
1743
1798
  */
1744
1799
  async exportToHtml(outputPath) {
1745
1800
  const themeName = this.settingsManager.getTheme();
1746
- return await exportSessionToHtml(this.sessionManager, this.state, { outputPath, themeName });
1801
+ // Create tool renderer if we have an extension runner (for custom tool HTML rendering)
1802
+ let toolRenderer;
1803
+ if (this._extensionRunner) {
1804
+ toolRenderer = createToolHtmlRenderer({
1805
+ getToolDefinition: (name) => this._extensionRunner.getToolDefinition(name),
1806
+ theme,
1807
+ });
1808
+ }
1809
+ return await exportSessionToHtml(this.sessionManager, this.state, {
1810
+ outputPath,
1811
+ themeName,
1812
+ toolRenderer,
1813
+ });
1747
1814
  }
1748
1815
  // =========================================================================
1749
1816
  // Utilities