@dreb/coding-agent 2.22.1 → 2.24.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 (41) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/README.md +1 -1
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +12 -1
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/extensions/index.d.ts +1 -1
  7. package/dist/core/extensions/index.d.ts.map +1 -1
  8. package/dist/core/extensions/index.js.map +1 -1
  9. package/dist/core/extensions/types.d.ts +14 -1
  10. package/dist/core/extensions/types.d.ts.map +1 -1
  11. package/dist/core/extensions/types.js.map +1 -1
  12. package/dist/core/keybindings.d.ts +5 -0
  13. package/dist/core/keybindings.d.ts.map +1 -1
  14. package/dist/core/keybindings.js +4 -0
  15. package/dist/core/keybindings.js.map +1 -1
  16. package/dist/core/tools/subagent.d.ts +1 -0
  17. package/dist/core/tools/subagent.d.ts.map +1 -1
  18. package/dist/core/tools/subagent.js +35 -2
  19. package/dist/core/tools/subagent.js.map +1 -1
  20. package/dist/index.d.ts +1 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/modes/interactive/components/copy-selector.d.ts +32 -0
  24. package/dist/modes/interactive/components/copy-selector.d.ts.map +1 -0
  25. package/dist/modes/interactive/components/copy-selector.js +155 -0
  26. package/dist/modes/interactive/components/copy-selector.js.map +1 -0
  27. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  28. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  29. package/dist/modes/interactive/interactive-mode.js +89 -9
  30. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  31. package/dist/utils/clipboard.d.ts +4 -1
  32. package/dist/utils/clipboard.d.ts.map +1 -1
  33. package/dist/utils/clipboard.js +12 -2
  34. package/dist/utils/clipboard.js.map +1 -1
  35. package/dist/utils/message-text.d.ts +21 -0
  36. package/dist/utils/message-text.d.ts.map +1 -0
  37. package/dist/utils/message-text.js +118 -0
  38. package/dist/utils/message-text.js.map +1 -0
  39. package/docs/extensions.md +13 -0
  40. package/docs/keybindings.md +1 -0
  41. package/package.json +1 -1
@@ -28,6 +28,7 @@ import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/cha
28
28
  import { copyToClipboard } from "../../utils/clipboard.js";
29
29
  import { extensionForImageMimeType, readClipboardImage } from "../../utils/clipboard-image.js";
30
30
  import { parseGitUrl } from "../../utils/git.js";
31
+ import { extractCopyableText, getMessagePreview, getMessageRoleLabel } from "../../utils/message-text.js";
31
32
  import { ensureTool } from "../../utils/tools-manager.js";
32
33
  import { ArminComponent } from "./components/armin.js";
33
34
  import { AssistantMessageComponent } from "./components/assistant-message.js";
@@ -36,6 +37,7 @@ import { BorderedLoader } from "./components/bordered-loader.js";
36
37
  import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js";
37
38
  import { BuddyComponent } from "./components/buddy-component.js";
38
39
  import { CompactionSummaryMessageComponent } from "./components/compaction-summary-message.js";
40
+ import { CopySelectorComponent } from "./components/copy-selector.js";
39
41
  import { CustomEditor } from "./components/custom-editor.js";
40
42
  import { CustomMessageComponent } from "./components/custom-message.js";
41
43
  import { DaxnutsComponent } from "./components/daxnuts.js";
@@ -1711,6 +1713,7 @@ export class InteractiveMode {
1711
1713
  this.defaultEditor.onAction("app.session.tree", () => this.showTreeSelector());
1712
1714
  this.defaultEditor.onAction("app.session.fork", () => this.showUserMessageSelector());
1713
1715
  this.defaultEditor.onAction("app.session.resume", () => this.showSessionSelector());
1716
+ this.defaultEditor.onAction("app.clipboard.copyMessages", () => this.showCopySelector());
1714
1717
  this.defaultEditor.onChange = (text) => {
1715
1718
  const wasBashMode = this.isBashMode;
1716
1719
  this.isBashMode = text.trimStart().startsWith("!");
@@ -2226,6 +2229,33 @@ export class InteractiveMode {
2226
2229
  this.ui.requestRender();
2227
2230
  break;
2228
2231
  }
2232
+ case "length_retry": {
2233
+ // Remove the ghost streaming component left from the truncated stream
2234
+ if (this.streamingComponent) {
2235
+ this.chatContainer.removeChild(this.streamingComponent);
2236
+ this.streamingComponent = undefined;
2237
+ this.streamingMessage = undefined;
2238
+ }
2239
+ // Clear any pending tool components from the truncated partial
2240
+ for (const [, component] of this.pendingTools.entries()) {
2241
+ this.chatContainer.removeChild(component);
2242
+ }
2243
+ this.pendingTools.clear();
2244
+ // Show retry status
2245
+ this.statusContainer.clear();
2246
+ if (this.loadingAnimation) {
2247
+ this.loadingAnimation.stop();
2248
+ this.loadingAnimation = undefined;
2249
+ }
2250
+ if (this.retryLoader) {
2251
+ this.retryLoader.stop();
2252
+ this.retryLoader = undefined;
2253
+ }
2254
+ this.retryLoader = new Loader(this.ui, (spinner) => theme.fg("warning", spinner), (text) => theme.fg("muted", text), `Response truncated, retrying with larger token budget (${event.attempt}/${event.maxAttempts})... (${keyText("app.interrupt")} to cancel)`);
2255
+ this.statusContainer.addChild(this.retryLoader);
2256
+ this.ui.requestRender();
2257
+ break;
2258
+ }
2229
2259
  case "background_agent_start":
2230
2260
  case "background_agent_end": {
2231
2261
  this.updateBackgroundAgentStatus();
@@ -3655,18 +3685,68 @@ export class InteractiveMode {
3655
3685
  }
3656
3686
  }
3657
3687
  async handleCopyCommand() {
3658
- const text = this.session.getLastAssistantText();
3659
- if (!text) {
3660
- this.showError("No agent messages to copy yet.");
3688
+ this.showCopySelector();
3689
+ }
3690
+ showCopySelector() {
3691
+ const messages = this.session.messages;
3692
+ if (messages.length === 0) {
3693
+ this.showStatus("No messages to copy");
3661
3694
  return;
3662
3695
  }
3663
- try {
3664
- await copyToClipboard(text);
3665
- this.showStatus("Copied last agent message to clipboard");
3666
- }
3667
- catch (error) {
3668
- this.showError(error instanceof Error ? error.message : String(error));
3696
+ // Build items from session messages
3697
+ const items = messages.map((msg, index) => ({
3698
+ index,
3699
+ roleLabel: getMessageRoleLabel(msg),
3700
+ preview: getMessagePreview(msg),
3701
+ }));
3702
+ // Hide buddy while selector is open to free vertical space
3703
+ const hadBuddy = this.buddyComponent !== null;
3704
+ if (hadBuddy) {
3705
+ this.widgetContainerBelow.clear();
3706
+ this.ui.requestRender();
3669
3707
  }
3708
+ // Calculate max visible items based on terminal height
3709
+ // Reserve lines for: header(4) + borders(2) + spacers(2) + scroll indicator(1) + footer(1) + padding(2) = ~12 lines overhead
3710
+ const terminalRows = this.ui.terminal.rows;
3711
+ const overhead = 12;
3712
+ const maxVisible = Math.max(3, Math.min(15, terminalRows - overhead));
3713
+ this.showSelector((done) => {
3714
+ const selector = new CopySelectorComponent(items, async (selectedIndices) => {
3715
+ done();
3716
+ // Restore buddy
3717
+ if (hadBuddy)
3718
+ this.renderWidgets();
3719
+ this.ui.requestRender();
3720
+ if (selectedIndices.length === 0) {
3721
+ this.showWarning("No messages selected");
3722
+ return;
3723
+ }
3724
+ // Extract text from selected messages in chronological order
3725
+ const selectedTexts = selectedIndices
3726
+ .map((i) => extractCopyableText(messages[i]))
3727
+ .filter((text) => text.length > 0);
3728
+ if (selectedTexts.length === 0) {
3729
+ this.showWarning("Selected messages have no copyable text");
3730
+ return;
3731
+ }
3732
+ const combined = selectedTexts.join("\n\n---\n\n");
3733
+ const result = await copyToClipboard(combined);
3734
+ const count = selectedTexts.length;
3735
+ if (result.method === "osc52") {
3736
+ this.showStatus(`Sent ${count} message${count === 1 ? "" : "s"} to terminal clipboard (OSC 52)`);
3737
+ }
3738
+ else {
3739
+ this.showStatus(`Copied ${count} message${count === 1 ? "" : "s"} to clipboard`);
3740
+ }
3741
+ }, () => {
3742
+ done();
3743
+ // Restore buddy
3744
+ if (hadBuddy)
3745
+ this.renderWidgets();
3746
+ this.ui.requestRender();
3747
+ }, maxVisible);
3748
+ return { component: selector, focus: selector.getMessageList() };
3749
+ });
3670
3750
  }
3671
3751
  handleNameCommand(text) {
3672
3752
  const name = text.replace(/^\/name\s*/, "").trim();