@bastani/atomic 0.8.1-1 → 0.8.2-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 (149) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/builtin/intercom/config.ts +3 -4
  3. package/dist/builtin/intercom/index.ts +6 -6
  4. package/dist/builtin/intercom/package.json +1 -1
  5. package/dist/builtin/mcp/agent-dir.ts +11 -2
  6. package/dist/builtin/mcp/cli.js +12 -6
  7. package/dist/builtin/mcp/config.ts +31 -22
  8. package/dist/builtin/mcp/package.json +1 -1
  9. package/dist/builtin/subagents/package.json +1 -1
  10. package/dist/builtin/subagents/src/agents/agents.ts +63 -23
  11. package/dist/builtin/subagents/src/agents/skills.ts +21 -21
  12. package/dist/builtin/subagents/src/extension/index.ts +9 -8
  13. package/dist/builtin/subagents/src/runs/shared/run-history.ts +13 -10
  14. package/dist/builtin/subagents/src/runs/shared/subagent-prompt-runtime.ts +3 -3
  15. package/dist/builtin/subagents/src/shared/artifacts.ts +18 -17
  16. package/dist/builtin/subagents/src/shared/types.ts +4 -4
  17. package/dist/builtin/web-access/config-paths.ts +11 -0
  18. package/dist/builtin/web-access/exa.ts +3 -2
  19. package/dist/builtin/web-access/gemini-api.ts +2 -1
  20. package/dist/builtin/web-access/gemini-search.ts +2 -1
  21. package/dist/builtin/web-access/gemini-web-config.ts +2 -1
  22. package/dist/builtin/web-access/github-extract.ts +2 -1
  23. package/dist/builtin/web-access/index.ts +11 -8
  24. package/dist/builtin/web-access/package.json +1 -1
  25. package/dist/builtin/web-access/perplexity.ts +2 -1
  26. package/dist/builtin/web-access/video-extract.ts +2 -1
  27. package/dist/builtin/web-access/youtube-extract.ts +2 -1
  28. package/dist/builtin/workflows/builtin/deep-research-codebase.ts +4 -0
  29. package/dist/builtin/workflows/builtin/open-claude-design.ts +39 -22
  30. package/dist/builtin/workflows/builtin/ralph.ts +7 -0
  31. package/dist/builtin/workflows/package.json +1 -1
  32. package/dist/builtin/workflows/skills/workflow/SKILL.md +28 -20
  33. package/dist/builtin/workflows/skills/workflow/references/design-checklist.md +8 -4
  34. package/dist/builtin/workflows/skills/workflow/references/running-workflows.md +52 -23
  35. package/dist/builtin/workflows/skills/workflow/references/sdk-authoring.md +41 -12
  36. package/dist/builtin/workflows/src/extension/config-loader.ts +13 -14
  37. package/dist/builtin/workflows/src/extension/discovery.ts +4 -6
  38. package/dist/builtin/workflows/src/extension/index.ts +675 -524
  39. package/dist/builtin/workflows/src/extension/runtime.ts +40 -16
  40. package/dist/builtin/workflows/src/extension/wiring.ts +3 -0
  41. package/dist/builtin/workflows/src/extension/workflow-schema.ts +43 -33
  42. package/dist/builtin/workflows/src/runs/foreground/executor.ts +34 -10
  43. package/dist/builtin/workflows/src/shared/types.ts +1 -5
  44. package/dist/builtin/workflows/src/tui/graph-view.ts +245 -75
  45. package/dist/builtin/workflows/src/tui/overlay-adapter.ts +23 -0
  46. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +259 -149
  47. package/dist/builtin/workflows/src/tui/status-helpers.ts +3 -3
  48. package/dist/builtin/workflows/src/tui/store-widget-installer.ts +99 -10
  49. package/dist/builtin/workflows/src/tui/switcher.ts +4 -5
  50. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +29 -0
  51. package/dist/cli/args.d.ts.map +1 -1
  52. package/dist/cli/args.js +11 -8
  53. package/dist/cli/args.js.map +1 -1
  54. package/dist/config.d.ts +21 -0
  55. package/dist/config.d.ts.map +1 -1
  56. package/dist/config.js +59 -4
  57. package/dist/config.js.map +1 -1
  58. package/dist/core/agent-session.d.ts +1 -1
  59. package/dist/core/agent-session.d.ts.map +1 -1
  60. package/dist/core/agent-session.js +2 -2
  61. package/dist/core/agent-session.js.map +1 -1
  62. package/dist/core/auth-storage.d.ts +3 -1
  63. package/dist/core/auth-storage.d.ts.map +1 -1
  64. package/dist/core/auth-storage.js +31 -8
  65. package/dist/core/auth-storage.js.map +1 -1
  66. package/dist/core/extensions/runner.d.ts.map +1 -1
  67. package/dist/core/extensions/runner.js +9 -0
  68. package/dist/core/extensions/runner.js.map +1 -1
  69. package/dist/core/extensions/types.d.ts +11 -0
  70. package/dist/core/extensions/types.d.ts.map +1 -1
  71. package/dist/core/extensions/types.js.map +1 -1
  72. package/dist/core/model-registry.d.ts +3 -2
  73. package/dist/core/model-registry.d.ts.map +1 -1
  74. package/dist/core/model-registry.js +25 -8
  75. package/dist/core/model-registry.js.map +1 -1
  76. package/dist/core/package-manager.d.ts +3 -0
  77. package/dist/core/package-manager.d.ts.map +1 -1
  78. package/dist/core/package-manager.js +97 -58
  79. package/dist/core/package-manager.js.map +1 -1
  80. package/dist/core/resource-loader.d.ts +1 -0
  81. package/dist/core/resource-loader.d.ts.map +1 -1
  82. package/dist/core/resource-loader.js +37 -36
  83. package/dist/core/resource-loader.js.map +1 -1
  84. package/dist/core/sdk.d.ts +5 -4
  85. package/dist/core/sdk.d.ts.map +1 -1
  86. package/dist/core/sdk.js +2 -2
  87. package/dist/core/sdk.js.map +1 -1
  88. package/dist/core/settings-manager.d.ts +7 -1
  89. package/dist/core/settings-manager.d.ts.map +1 -1
  90. package/dist/core/settings-manager.js +29 -8
  91. package/dist/core/settings-manager.js.map +1 -1
  92. package/dist/core/system-prompt.d.ts +1 -1
  93. package/dist/core/system-prompt.d.ts.map +1 -1
  94. package/dist/core/system-prompt.js.map +1 -1
  95. package/dist/core/telemetry.d.ts.map +1 -1
  96. package/dist/core/telemetry.js +2 -2
  97. package/dist/core/telemetry.js.map +1 -1
  98. package/dist/core/timings.d.ts.map +1 -1
  99. package/dist/core/timings.js +2 -2
  100. package/dist/core/timings.js.map +1 -1
  101. package/dist/core/tools/index.d.ts +1 -0
  102. package/dist/core/tools/index.d.ts.map +1 -1
  103. package/dist/core/tools/index.js +8 -0
  104. package/dist/core/tools/index.js.map +1 -1
  105. package/dist/core/tools/todos.d.ts.map +1 -1
  106. package/dist/core/tools/todos.js +3 -3
  107. package/dist/core/tools/todos.js.map +1 -1
  108. package/dist/index.d.ts +2 -2
  109. package/dist/index.d.ts.map +1 -1
  110. package/dist/index.js +2 -2
  111. package/dist/index.js.map +1 -1
  112. package/dist/main.d.ts.map +1 -1
  113. package/dist/main.js +6 -6
  114. package/dist/main.js.map +1 -1
  115. package/dist/modes/interactive/components/atomic-banner.d.ts +4 -0
  116. package/dist/modes/interactive/components/atomic-banner.d.ts.map +1 -0
  117. package/dist/modes/interactive/components/atomic-banner.js +34 -0
  118. package/dist/modes/interactive/components/atomic-banner.js.map +1 -0
  119. package/dist/modes/interactive/components/chat-message-renderer.d.ts +99 -0
  120. package/dist/modes/interactive/components/chat-message-renderer.d.ts.map +1 -0
  121. package/dist/modes/interactive/components/chat-message-renderer.js +450 -0
  122. package/dist/modes/interactive/components/chat-message-renderer.js.map +1 -0
  123. package/dist/modes/interactive/components/chat-transcript.d.ts +69 -0
  124. package/dist/modes/interactive/components/chat-transcript.d.ts.map +1 -0
  125. package/dist/modes/interactive/components/chat-transcript.js +183 -0
  126. package/dist/modes/interactive/components/chat-transcript.js.map +1 -0
  127. package/dist/modes/interactive/components/footer.d.ts +16 -4
  128. package/dist/modes/interactive/components/footer.d.ts.map +1 -1
  129. package/dist/modes/interactive/components/footer.js +110 -137
  130. package/dist/modes/interactive/components/footer.js.map +1 -1
  131. package/dist/modes/interactive/components/index.d.ts +2 -0
  132. package/dist/modes/interactive/components/index.d.ts.map +1 -1
  133. package/dist/modes/interactive/components/index.js +2 -0
  134. package/dist/modes/interactive/components/index.js.map +1 -1
  135. package/dist/modes/interactive/interactive-mode.d.ts +9 -0
  136. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  137. package/dist/modes/interactive/interactive-mode.js +192 -137
  138. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  139. package/dist/modes/interactive/theme/catppuccin-mocha.json +5 -5
  140. package/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  141. package/dist/modes/rpc/rpc-mode.js +11 -0
  142. package/dist/modes/rpc/rpc-mode.js.map +1 -1
  143. package/dist/utils/tools-manager.d.ts.map +1 -1
  144. package/dist/utils/tools-manager.js +2 -2
  145. package/dist/utils/tools-manager.js.map +1 -1
  146. package/dist/utils/version-check.d.ts.map +1 -1
  147. package/dist/utils/version-check.js +2 -2
  148. package/dist/utils/version-check.js.map +1 -1
  149. package/package.json +1 -1
@@ -9,7 +9,7 @@ import * as path from "node:path";
9
9
  import { getProviders, } from "@earendil-works/pi-ai";
10
10
  import { CombinedAutocompleteProvider, Container, fuzzyFilter, getCapabilities, hyperlink, Loader, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TUI, visibleWidth, } from "@earendil-works/pi-tui";
11
11
  import { spawn, spawnSync } from "child_process";
12
- import { APP_NAME, APP_TITLE, CHANGELOG_URL, ENV_OFFLINE, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, getShareViewerUrl, VERSION, } from "../../config.js";
12
+ import { APP_NAME, APP_TITLE, CHANGELOG_URL, ENV_OFFLINE, getEnvValue, getAgentDir, getAuthPath, getDebugLogPath, getDocsPath, getShareViewerUrl, VERSION, } from "../../config.js";
13
13
  import { parseSkillBlock, } from "../../core/agent-session.js";
14
14
  import { pickWhimsicalWorkingMessage } from "./whimsical-messages.js";
15
15
  import { SessionImportFileNotFoundError, } from "../../core/agent-session-runtime.js";
@@ -37,18 +37,21 @@ import { AssistantMessageComponent } from "./components/assistant-message.js";
37
37
  import { BashExecutionComponent } from "./components/bash-execution.js";
38
38
  import { BorderedLoader } from "./components/bordered-loader.js";
39
39
  import { BranchSummaryMessageComponent } from "./components/branch-summary-message.js";
40
+ import { chatEntriesFromAgentMessages, renderChatMessageEntry, } from "./components/chat-message-renderer.js";
41
+ import { addChatTranscriptEntry } from "./components/chat-transcript.js";
40
42
  import { CompactionSummaryMessageComponent } from "./components/compaction-summary-message.js";
41
43
  import { CountdownTimer } from "./components/countdown-timer.js";
42
44
  import { CustomEditor } from "./components/custom-editor.js";
43
45
  import { CustomMessageComponent } from "./components/custom-message.js";
44
46
  import { DaxnutsComponent } from "./components/daxnuts.js";
47
+ import { renderAtomicAnsiBanner } from "./components/atomic-banner.js";
45
48
  import { DynamicBorder } from "./components/dynamic-border.js";
46
49
  import { EarendilAnnouncementComponent } from "./components/earendil-announcement.js";
47
50
  import { ExtensionEditorComponent } from "./components/extension-editor.js";
48
51
  import { ExtensionInputComponent } from "./components/extension-input.js";
49
52
  import { ExtensionSelectorComponent } from "./components/extension-selector.js";
50
- import { FooterComponent } from "./components/footer.js";
51
- import { formatKeyText, keyDisplayText, keyHint, keyText, rawKeyHint, } from "./components/keybinding-hints.js";
53
+ import { FooterComponent, UsageMeterComponent } from "./components/footer.js";
54
+ import { formatKeyText, keyDisplayText, keyText, } from "./components/keybinding-hints.js";
52
55
  import { LoginDialogComponent } from "./components/login-dialog.js";
53
56
  import { ModelSelectorComponent } from "./components/model-selector.js";
54
57
  import { OAuthSelectorComponent, } from "./components/oauth-selector.js";
@@ -72,9 +75,14 @@ class ExpandableText extends Text {
72
75
  super(expanded ? getExpandedText() : getCollapsedText(), paddingX, paddingY);
73
76
  this.getCollapsedText = getCollapsedText;
74
77
  this.getExpandedText = getExpandedText;
78
+ this.expanded = expanded;
75
79
  }
76
80
  setExpanded(expanded) {
77
- this.setText(expanded ? this.getExpandedText() : this.getCollapsedText());
81
+ this.expanded = expanded;
82
+ this.refresh();
83
+ }
84
+ refresh() {
85
+ this.setText(this.expanded ? this.getExpandedText() : this.getCollapsedText());
78
86
  }
79
87
  }
80
88
  const DEAD_TERMINAL_ERROR_CODES = new Set(["EIO", "EPIPE", "ENOTCONN"]);
@@ -218,7 +226,8 @@ export class InteractiveMode {
218
226
  this.editorContainer.addChild(this.editor);
219
227
  this.footerDataProvider = new FooterDataProvider(this.sessionManager.getCwd());
220
228
  this.footer = new FooterComponent(this.session, this.footerDataProvider);
221
- this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
229
+ this.usageMeter = new UsageMeterComponent(this.session);
230
+ this.usageMeter.setAutoCompactEnabled(this.session.autoCompactionEnabled);
222
231
  // Load hide thinking block setting
223
232
  this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
224
233
  // Register themes from resource loader and initialize
@@ -385,43 +394,10 @@ export class InteractiveMode {
385
394
  this.fdPath = fdPath;
386
395
  // Add header container as first child
387
396
  this.ui.addChild(this.headerContainer);
388
- // Add header with keybindings from config (unless silenced)
397
+ // Add the quiet startup identity (unless silenced). Resource details are
398
+ // disclosed separately in the chat canvas via the tools/resources toggle.
389
399
  if (this.options.verbose || !this.settingsManager.getQuietStartup()) {
390
- const logo = theme.bold(theme.fg("accent", APP_NAME)) +
391
- theme.fg("dim", ` v${this.version}`);
392
- // Build startup instructions using keybinding hint helpers
393
- const hint = (keybinding, description) => keyHint(keybinding, description);
394
- const expandedInstructions = [
395
- hint("app.interrupt", "to interrupt"),
396
- hint("app.clear", "to clear"),
397
- rawKeyHint(`${keyText("app.clear")} twice`, "to exit"),
398
- hint("app.exit", "to exit (empty)"),
399
- hint("app.suspend", "to suspend"),
400
- keyHint("tui.editor.deleteToLineEnd", "to delete to end"),
401
- hint("app.thinking.cycle", "to cycle thinking level"),
402
- rawKeyHint(`${keyText("app.model.cycleForward")}/${keyText("app.model.cycleBackward")}`, "to cycle models"),
403
- hint("app.model.select", "to select model"),
404
- hint("app.tools.expand", "to expand tools"),
405
- hint("app.thinking.toggle", "to expand thinking"),
406
- hint("app.editor.external", "for external editor"),
407
- rawKeyHint("/", "for commands"),
408
- rawKeyHint("!", "to run bash"),
409
- rawKeyHint("!!", "to run bash (no context)"),
410
- hint("app.message.followUp", "to queue follow-up"),
411
- hint("app.message.dequeue", "to edit all queued messages"),
412
- hint("app.clipboard.pasteImage", "to paste image"),
413
- rawKeyHint("drop files", "to attach"),
414
- ].join("\n");
415
- const compactInstructions = [
416
- hint("app.interrupt", "interrupt"),
417
- rawKeyHint(`${keyText("app.clear")}/${keyText("app.exit")}`, "clear/exit"),
418
- rawKeyHint("/", "commands"),
419
- rawKeyHint("!", "bash"),
420
- hint("app.tools.expand", "more"),
421
- ].join(theme.fg("muted", " · "));
422
- const compactOnboarding = theme.fg("dim", `Press ${keyText("app.tools.expand")} to show full startup help and loaded resources.`);
423
- const onboarding = theme.fg("dim", `Atomic is built with Pi. Pi can explain its own features and look up its docs. Ask it how to use or extend Pi.`);
424
- this.builtInHeader = new ExpandableText(() => `${logo}\n${compactInstructions}\n${compactOnboarding}\n\n${onboarding}`, () => `${logo}\n${expandedInstructions}\n\n${onboarding}`, this.getStartupExpansionState(), 1, 0);
400
+ this.builtInHeader = new ExpandableText(() => this.getStartupIdentityText(), () => this.getStartupIdentityText(), this.getStartupExpansionState(), 1, 0);
425
401
  // Setup UI layout
426
402
  this.headerContainer.addChild(new Spacer(1));
427
403
  this.headerContainer.addChild(this.builtInHeader);
@@ -437,6 +413,7 @@ export class InteractiveMode {
437
413
  this.ui.addChild(this.statusContainer);
438
414
  this.renderWidgets(); // Initialize with default spacer
439
415
  this.ui.addChild(this.widgetContainerAbove);
416
+ this.ui.addChild(this.usageMeter);
440
417
  this.ui.addChild(this.editorContainer);
441
418
  this.ui.addChild(this.widgetContainerBelow);
442
419
  this.ui.addChild(this.footer);
@@ -547,7 +524,7 @@ export class InteractiveMode {
547
524
  }
548
525
  }
549
526
  async checkForPackageUpdates() {
550
- if (process.env[ENV_OFFLINE]) {
527
+ if (getEnvValue(ENV_OFFLINE)) {
551
528
  return [];
552
529
  }
553
530
  try {
@@ -632,7 +609,7 @@ export class InteractiveMode {
632
609
  return undefined;
633
610
  }
634
611
  reportInstallTelemetry(version) {
635
- if (process.env[ENV_OFFLINE]) {
612
+ if (getEnvValue(ENV_OFFLINE)) {
636
613
  return;
637
614
  }
638
615
  if (!isInstallTelemetryEnabled(this.settingsManager)) {
@@ -681,6 +658,25 @@ export class InteractiveMode {
681
658
  }
682
659
  return this.formatDisplayPath(absolutePath);
683
660
  }
661
+ getStartupIdentityText() {
662
+ const appLabel = APP_NAME.length > 0
663
+ ? `${APP_NAME[0].toUpperCase()}${APP_NAME.slice(1)}`
664
+ : "Atomic";
665
+ const title = `${theme.bold(theme.fg("text", appLabel))} ${theme.fg("muted", `v${this.version}`)}`;
666
+ const model = this.session.state.model;
667
+ const provider = model ? theme.fg("dim", `(${model.provider})`) : theme.fg("dim", "(no-provider)");
668
+ const thinking = model?.reasoning ? ` ${this.session.thinkingLevel || "off"}` : "";
669
+ const modelLine = `${provider} ${theme.fg("muted", `${model?.id ?? "no-model"}${thinking}`)}`;
670
+ const cwd = theme.fg("muted", this.formatDisplayPath(this.sessionManager.getCwd()));
671
+ const metaLines = [title, modelLine, cwd];
672
+ const markLines = this.getAtomicAnsiMarkLines();
673
+ return markLines
674
+ .map((line, index) => `${line} ${metaLines[index] ?? ""}`.trimEnd())
675
+ .join("\n");
676
+ }
677
+ getAtomicAnsiMarkLines() {
678
+ return renderAtomicAnsiBanner(theme, this.session.thinkingLevel || "off");
679
+ }
684
680
  getStartupExpansionState() {
685
681
  return this.options.verbose || this.toolOutputExpanded;
686
682
  }
@@ -952,6 +948,63 @@ export class InteractiveMode {
952
948
  }
953
949
  return lines.join("\n");
954
950
  }
951
+ getResourceDiagnosticsTotal(values) {
952
+ return values.reduce((total, diagnostics) => total + diagnostics.length, 0);
953
+ }
954
+ formatResourceCount(count, singular, plural = `${singular}s`) {
955
+ if (count <= 0) {
956
+ return undefined;
957
+ }
958
+ return `${count} ${count === 1 ? singular : plural}`;
959
+ }
960
+ addResourceDisclosure(options) {
961
+ const contextLabels = options.contextFiles.map((contextFile) => this.formatContextPath(contextFile.path));
962
+ const promptCount = options.prompts.length + options.templates.length;
963
+ const summaryParts = [
964
+ contextLabels.length > 0 ? contextLabels.join(", ") : "context ready",
965
+ this.formatResourceCount(options.skills.length, "skill"),
966
+ this.formatResourceCount(promptCount, "prompt"),
967
+ this.formatResourceCount(options.extensions.length, "extension"),
968
+ this.formatResourceCount(options.themes.length, "theme"),
969
+ this.formatResourceCount(options.diagnosticsTotal, "issue"),
970
+ ].filter((part) => part !== undefined && part.length > 0);
971
+ const collapsed = `${theme.bold(theme.fg("muted", "RESOURCES"))} ${theme.fg("muted", summaryParts.join(" · "))}`;
972
+ const ok = theme.fg("success", "✓");
973
+ const pending = theme.fg("dim", "○");
974
+ const sep = theme.fg("dim", " · ");
975
+ const label = (value) => theme.bold(theme.fg("text", value.padEnd(10)));
976
+ const mutedList = (values, maxItems = 4) => {
977
+ if (values.length === 0) {
978
+ return theme.fg("dim", "none");
979
+ }
980
+ const shown = values.slice(0, maxItems).join(", ");
981
+ const suffix = values.length > maxItems ? `, +${values.length - maxItems}` : "";
982
+ return theme.fg("dim", `${shown}${suffix}`);
983
+ };
984
+ const extensionLabels = this.getCompactExtensionLabels([...options.extensions]);
985
+ const themeLabels = options.themes.map((loadedTheme) => loadedTheme.name ??
986
+ (loadedTheme.sourcePath
987
+ ? this.getCompactPathLabel(loadedTheme.sourcePath, undefined)
988
+ : "theme"));
989
+ const expandedSummary = [
990
+ `${ok} ${label("Ready")} ${contextLabels.length > 0 ? contextLabels.join(", ") : "context loaded"}`,
991
+ `${ok} ${label("Skills")} ${options.skills.length} available${sep}${mutedList(options.skills.map((skill) => skill.name))}`,
992
+ `${ok} ${label("Prompts")} ${promptCount} available${sep}${mutedList([
993
+ ...options.templates.map((template) => `/${template.name}`),
994
+ ...options.prompts.map((prompt) => prompt.name),
995
+ ])}`,
996
+ `${ok} ${label("Extensions")} ${options.extensions.length} available${sep}${mutedList(extensionLabels)}`,
997
+ ];
998
+ if (themeLabels.length > 0) {
999
+ expandedSummary.push(`${ok} ${label("Themes")} ${themeLabels.length} loaded${sep}${mutedList(themeLabels)}`);
1000
+ }
1001
+ if (options.diagnosticsTotal > 0) {
1002
+ expandedSummary.push(`${pending} ${label("Issues")} ${options.diagnosticsTotal} noted${sep}${theme.fg("dim", "details below")}`);
1003
+ }
1004
+ const expanded = `${collapsed}\n${expandedSummary.join("\n")}${options.expandedBody.length > 0 ? `\n\n${options.expandedBody}` : ""}`;
1005
+ this.chatContainer.addChild(new ExpandableText(() => collapsed, () => expanded, this.getStartupExpansionState(), 0, 0));
1006
+ this.chatContainer.addChild(new Spacer(1));
1007
+ }
955
1008
  showLoadedResources(options) {
956
1009
  const showListing = options?.force ||
957
1010
  this.options.verbose ||
@@ -960,21 +1013,6 @@ export class InteractiveMode {
960
1013
  if (!showListing && !showDiagnostics) {
961
1014
  return;
962
1015
  }
963
- const sectionHeader = (name, color = "mdHeading") => theme.fg(color, `[${name}]`);
964
- const formatCompactList = (items, options) => {
965
- const labels = items
966
- .map((item) => item.trim())
967
- .filter((item) => item.length > 0);
968
- if (options?.sort !== false) {
969
- labels.sort((a, b) => a.localeCompare(b));
970
- }
971
- return theme.fg("dim", ` ${labels.join(", ")}`);
972
- };
973
- const addLoadedSection = (name, collapsedBody, expandedBody = collapsedBody, color = "mdHeading") => {
974
- const section = new ExpandableText(() => `${sectionHeader(name, color)}\n${collapsedBody}`, () => `${sectionHeader(name, color)}\n${expandedBody}`, this.getStartupExpansionState(), 0, 0);
975
- this.chatContainer.addChild(section);
976
- this.chatContainer.addChild(new Spacer(1));
977
- };
978
1016
  const skillsResult = this.session.resourceLoader.getSkills();
979
1017
  const promptsResult = this.session.resourceLoader.getPrompts();
980
1018
  const themesResult = this.session.resourceLoader.getThemes();
@@ -1008,13 +1046,13 @@ export class InteractiveMode {
1008
1046
  }
1009
1047
  if (showListing) {
1010
1048
  const contextFiles = this.session.resourceLoader.getAgentsFiles().agentsFiles;
1049
+ const templates = this.session.promptTemplates;
1050
+ const customThemes = themesResult.themes.filter((t) => t.sourcePath);
1051
+ const expandedSections = [];
1011
1052
  if (contextFiles.length > 0) {
1012
- this.chatContainer.addChild(new Spacer(1));
1013
- const contextList = contextFiles
1014
- .map((f) => theme.fg("dim", ` ${this.formatDisplayPath(f.path)}`))
1015
- .join("\n");
1016
- const contextCompactList = formatCompactList(contextFiles.map((contextFile) => this.formatContextPath(contextFile.path)), { sort: false });
1017
- addLoadedSection("Context", contextCompactList, contextList);
1053
+ expandedSections.push(`${theme.bold(theme.fg("muted", "CONTEXT"))}\n${contextFiles
1054
+ .map((contextFile) => theme.fg("dim", ` ${this.formatDisplayPath(contextFile.path)}`))
1055
+ .join("\n")}`);
1018
1056
  }
1019
1057
  const skills = skillsResult.skills;
1020
1058
  if (skills.length > 0) {
@@ -1022,21 +1060,18 @@ export class InteractiveMode {
1022
1060
  path: skill.filePath,
1023
1061
  sourceInfo: skill.sourceInfo,
1024
1062
  })));
1025
- const skillList = this.formatScopeGroups(groups, {
1063
+ expandedSections.push(`${theme.bold(theme.fg("muted", "SKILLS"))}\n${this.formatScopeGroups(groups, {
1026
1064
  formatPath: (item) => this.formatDisplayPath(item.path),
1027
1065
  formatPackagePath: (item) => this.getShortPath(item.path, item.sourceInfo),
1028
- });
1029
- const skillCompactList = formatCompactList(skills.map((skill) => skill.name));
1030
- addLoadedSection("Skills", skillCompactList, skillList);
1066
+ })}`);
1031
1067
  }
1032
- const templates = this.session.promptTemplates;
1033
1068
  if (templates.length > 0) {
1034
1069
  const groups = this.buildScopeGroups(templates.map((template) => ({
1035
1070
  path: template.filePath,
1036
1071
  sourceInfo: template.sourceInfo,
1037
1072
  })));
1038
1073
  const templateByPath = new Map(templates.map((t) => [t.filePath, t]));
1039
- const templateList = this.formatScopeGroups(groups, {
1074
+ expandedSections.push(`${theme.bold(theme.fg("muted", "PROMPTS"))}\n${this.formatScopeGroups(groups, {
1040
1075
  formatPath: (item) => {
1041
1076
  const template = templateByPath.get(item.path);
1042
1077
  return template
@@ -1049,35 +1084,62 @@ export class InteractiveMode {
1049
1084
  ? `/${template.name}`
1050
1085
  : this.formatDisplayPath(item.path);
1051
1086
  },
1052
- });
1053
- const promptCompactList = formatCompactList(templates.map((template) => `/${template.name}`));
1054
- addLoadedSection("Prompts", promptCompactList, templateList);
1087
+ })}`);
1088
+ }
1089
+ const prompts = promptsResult.prompts;
1090
+ if (prompts.length > 0) {
1091
+ const groups = this.buildScopeGroups(prompts.map((prompt) => ({
1092
+ path: prompt.filePath,
1093
+ sourceInfo: prompt.sourceInfo,
1094
+ })));
1095
+ const promptByPath = new Map(prompts.map((prompt) => [prompt.filePath, prompt]));
1096
+ expandedSections.push(`${theme.bold(theme.fg("muted", "PROMPTS"))}\n${this.formatScopeGroups(groups, {
1097
+ formatPath: (item) => promptByPath.get(item.path)?.name ?? this.formatDisplayPath(item.path),
1098
+ formatPackagePath: (item) => promptByPath.get(item.path)?.name ?? this.formatDisplayPath(item.path),
1099
+ })}`);
1055
1100
  }
1056
1101
  if (extensions.length > 0) {
1057
1102
  const groups = this.buildScopeGroups(extensions);
1058
- const extList = this.formatScopeGroups(groups, {
1103
+ expandedSections.push(`${theme.bold(theme.fg("muted", "EXTENSIONS"))}\n${this.formatScopeGroups(groups, {
1059
1104
  formatPath: (item) => this.formatExtensionDisplayPath(item.path),
1060
1105
  formatPackagePath: (item) => this.formatExtensionDisplayPath(this.getShortPath(item.path, item.sourceInfo)),
1061
- });
1062
- const extensionCompactList = formatCompactList(this.getCompactExtensionLabels(extensions));
1063
- addLoadedSection("Extensions", extensionCompactList, extList, "mdHeading");
1106
+ })}`);
1064
1107
  }
1065
- // Show loaded themes (excluding built-in)
1066
- const loadedThemes = themesResult.themes;
1067
- const customThemes = loadedThemes.filter((t) => t.sourcePath);
1068
1108
  if (customThemes.length > 0) {
1069
1109
  const groups = this.buildScopeGroups(customThemes.map((loadedTheme) => ({
1070
1110
  path: loadedTheme.sourcePath,
1071
1111
  sourceInfo: loadedTheme.sourceInfo,
1072
1112
  })));
1073
- const themeList = this.formatScopeGroups(groups, {
1113
+ expandedSections.push(`${theme.bold(theme.fg("muted", "THEMES"))}\n${this.formatScopeGroups(groups, {
1074
1114
  formatPath: (item) => this.formatDisplayPath(item.path),
1075
1115
  formatPackagePath: (item) => this.getShortPath(item.path, item.sourceInfo),
1116
+ })}`);
1117
+ }
1118
+ const extensionDiagnostics = [];
1119
+ const extensionErrors = this.session.resourceLoader.getExtensions().errors;
1120
+ for (const error of extensionErrors) {
1121
+ extensionDiagnostics.push({
1122
+ type: "error",
1123
+ message: error.error,
1124
+ path: error.path,
1076
1125
  });
1077
- const themeCompactList = formatCompactList(customThemes.map((loadedTheme) => loadedTheme.name ??
1078
- this.getCompactPathLabel(loadedTheme.sourcePath, loadedTheme.sourceInfo)));
1079
- addLoadedSection("Themes", themeCompactList, themeList);
1080
1126
  }
1127
+ extensionDiagnostics.push(...this.session.extensionRunner.getCommandDiagnostics(), ...this.getBuiltInCommandConflictDiagnostics(this.session.extensionRunner), ...this.session.extensionRunner.getShortcutDiagnostics());
1128
+ this.addResourceDisclosure({
1129
+ contextFiles,
1130
+ skills,
1131
+ prompts,
1132
+ templates,
1133
+ extensions,
1134
+ themes: customThemes,
1135
+ diagnosticsTotal: this.getResourceDiagnosticsTotal([
1136
+ skillsResult.diagnostics,
1137
+ promptsResult.diagnostics,
1138
+ extensionDiagnostics,
1139
+ themesResult.diagnostics,
1140
+ ]),
1141
+ expandedBody: this.options.verbose ? expandedSections.join("\n\n") : "",
1142
+ });
1081
1143
  }
1082
1144
  if (showDiagnostics) {
1083
1145
  const skillDiagnostics = skillsResult.diagnostics;
@@ -1207,7 +1269,8 @@ export class InteractiveMode {
1207
1269
  }
1208
1270
  applyRuntimeSettings() {
1209
1271
  this.footer.setSession(this.session);
1210
- this.footer.setAutoCompactEnabled(this.session.autoCompactionEnabled);
1272
+ this.usageMeter.setSession(this.session);
1273
+ this.usageMeter.setAutoCompactEnabled(this.session.autoCompactionEnabled);
1211
1274
  this.footerDataProvider.setCwd(this.sessionManager.getCwd());
1212
1275
  this.hideThinkingBlock = this.settingsManager.getHideThinkingBlock();
1213
1276
  this.ui.setShowHardwareCursor(this.settingsManager.getShowHardwareCursor());
@@ -1602,6 +1665,15 @@ export class InteractiveMode {
1602
1665
  },
1603
1666
  getToolsExpanded: () => this.toolOutputExpanded,
1604
1667
  setToolsExpanded: (expanded) => this.setToolsExpanded(expanded),
1668
+ getChatRenderSettings: () => ({
1669
+ hideThinkingBlock: this.hideThinkingBlock,
1670
+ hiddenThinkingLabel: this.hiddenThinkingLabel,
1671
+ toolOutputExpanded: this.toolOutputExpanded,
1672
+ showImages: this.settingsManager.getShowImages(),
1673
+ imageWidthCells: this.settingsManager.getImageWidthCells(),
1674
+ getToolDefinition: (toolName) => this.getRegisteredToolDefinition(toolName),
1675
+ getCustomMessageRenderer: (customType) => this.session.extensionRunner.getMessageRenderer(customType),
1676
+ }),
1605
1677
  };
1606
1678
  }
1607
1679
  /**
@@ -2224,6 +2296,7 @@ export class InteractiveMode {
2224
2296
  break;
2225
2297
  case "thinking_level_changed":
2226
2298
  this.footer.invalidate();
2299
+ this.refreshBuiltInHeader();
2227
2300
  this.updateEditorBorderColor();
2228
2301
  break;
2229
2302
  case "message_start":
@@ -2499,6 +2572,25 @@ export class InteractiveMode {
2499
2572
  this.lastStatusText = text;
2500
2573
  this.ui.requestRender();
2501
2574
  }
2575
+ chatMessageRenderOptions() {
2576
+ return {
2577
+ ui: this.ui,
2578
+ cwd: this.sessionManager.getCwd(),
2579
+ markdownTheme: this.getMarkdownThemeWithSettings(),
2580
+ hideThinkingBlock: this.hideThinkingBlock,
2581
+ hiddenThinkingLabel: this.hiddenThinkingLabel,
2582
+ toolOutputExpanded: this.toolOutputExpanded,
2583
+ showImages: this.settingsManager.getShowImages(),
2584
+ imageWidthCells: this.settingsManager.getImageWidthCells(),
2585
+ getToolDefinition: (toolName) => this.getRegisteredToolDefinition(toolName),
2586
+ getCustomMessageRenderer: (customType) => this.session.extensionRunner.getMessageRenderer(customType),
2587
+ };
2588
+ }
2589
+ addRenderedChatEntry(entry) {
2590
+ const component = renderChatMessageEntry(entry, this.chatMessageRenderOptions());
2591
+ addChatTranscriptEntry(this.chatContainer, component, entry.role);
2592
+ return component;
2593
+ }
2502
2594
  addMessageToChat(message, options) {
2503
2595
  switch (message.role) {
2504
2596
  case "bashExecution": {
@@ -2585,64 +2677,22 @@ export class InteractiveMode {
2585
2677
  */
2586
2678
  renderSessionContext(sessionContext, options = {}) {
2587
2679
  this.pendingTools.clear();
2588
- const renderedPendingTools = new Map();
2589
2680
  if (options.updateFooter) {
2590
2681
  this.footer.invalidate();
2591
2682
  this.updateEditorBorderColor();
2592
2683
  }
2593
- for (const message of sessionContext.messages) {
2594
- // Assistant messages need special handling for tool calls
2595
- if (message.role === "assistant") {
2596
- this.addMessageToChat(message);
2597
- // Render tool call components
2598
- for (const content of message.content) {
2599
- if (content.type === "toolCall") {
2600
- const component = new ToolExecutionComponent(content.name, content.id, content.arguments, {
2601
- showImages: this.settingsManager.getShowImages(),
2602
- imageWidthCells: this.settingsManager.getImageWidthCells(),
2603
- }, this.getRegisteredToolDefinition(content.name), this.ui, this.sessionManager.getCwd());
2604
- component.setExpanded(this.toolOutputExpanded);
2605
- this.chatContainer.addChild(component);
2606
- if (message.stopReason === "aborted" ||
2607
- message.stopReason === "error") {
2608
- let errorMessage;
2609
- if (message.stopReason === "aborted") {
2610
- const retryAttempt = this.session.retryAttempt;
2611
- errorMessage =
2612
- retryAttempt > 0
2613
- ? `Aborted after ${retryAttempt} retry attempt${retryAttempt > 1 ? "s" : ""}`
2614
- : "Operation aborted";
2615
- }
2616
- else {
2617
- errorMessage = message.errorMessage || "Error";
2618
- }
2619
- component.updateResult({
2620
- content: [{ type: "text", text: errorMessage }],
2621
- isError: true,
2622
- });
2623
- }
2624
- else {
2625
- renderedPendingTools.set(content.id, component);
2626
- }
2627
- }
2628
- }
2684
+ const entries = chatEntriesFromAgentMessages(sessionContext.messages);
2685
+ for (const entry of entries) {
2686
+ const component = this.addRenderedChatEntry(entry);
2687
+ if (entry.kind === "tool" &&
2688
+ entry.isPartial !== false &&
2689
+ component instanceof ToolExecutionComponent) {
2690
+ this.pendingTools.set(entry.toolCallId, component);
2629
2691
  }
2630
- else if (message.role === "toolResult") {
2631
- // Match tool results to pending tool components
2632
- const component = renderedPendingTools.get(message.toolCallId);
2633
- if (component) {
2634
- component.updateResult(message);
2635
- renderedPendingTools.delete(message.toolCallId);
2636
- }
2637
- }
2638
- else {
2639
- // All other messages use standard rendering
2640
- this.addMessageToChat(message, options);
2692
+ if (options.populateHistory && entry.kind === "user") {
2693
+ this.editor.addToHistory?.(entry.text);
2641
2694
  }
2642
2695
  }
2643
- for (const [toolCallId, component] of renderedPendingTools) {
2644
- this.pendingTools.set(toolCallId, component);
2645
- }
2646
2696
  this.ui.requestRender();
2647
2697
  }
2648
2698
  renderInitialMessages() {
@@ -2862,6 +2912,11 @@ export class InteractiveMode {
2862
2912
  this.showStatus(`Restored ${restored} queued message${restored > 1 ? "s" : ""} to editor`);
2863
2913
  }
2864
2914
  }
2915
+ refreshBuiltInHeader() {
2916
+ if (this.builtInHeader instanceof ExpandableText) {
2917
+ this.builtInHeader.refresh();
2918
+ }
2919
+ }
2865
2920
  updateEditorBorderColor() {
2866
2921
  if (this.isBashMode) {
2867
2922
  this.editor.borderColor = theme.getBashModeBorderColor();
@@ -3243,7 +3298,7 @@ export class InteractiveMode {
3243
3298
  }, {
3244
3299
  onAutoCompactChange: (enabled) => {
3245
3300
  this.session.setAutoCompactionEnabled(enabled);
3246
- this.footer.setAutoCompactEnabled(enabled);
3301
+ this.usageMeter.setAutoCompactEnabled(enabled);
3247
3302
  },
3248
3303
  onShowImagesChange: (enabled) => {
3249
3304
  this.settingsManager.setShowImages(enabled);