@golba98/codexa 1.0.3 → 1.0.5

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 (125) hide show
  1. package/README.md +17 -18
  2. package/bin/codexa.js +62 -144
  3. package/package.json +4 -3
  4. package/src/app.tsx +642 -303
  5. package/src/commands/handler.ts +7 -18
  6. package/src/config/buildInfo.ts +2 -2
  7. package/src/config/layeredConfig.ts +1 -1
  8. package/src/config/runtimeConfig.ts +1 -1
  9. package/src/config/settings.ts +1 -1
  10. package/src/config/trustStore.ts +1 -1
  11. package/src/core/README.md +52 -0
  12. package/src/core/agent/loop.ts +282 -0
  13. package/src/core/agent/protocol.ts +211 -0
  14. package/src/core/agent/tools.ts +414 -0
  15. package/src/core/{codexExecArgs.ts → codex/codexExecArgs.ts} +2 -2
  16. package/src/core/{codexLaunch.ts → codex/codexLaunch.ts} +3 -3
  17. package/src/core/{codexPrompt.ts → codex/codexPrompt.ts} +3 -3
  18. package/src/core/debug/modelStateDebug.ts +34 -0
  19. package/src/core/executables/antigravityExecutable.ts +48 -0
  20. package/src/core/executables/executableResolver.ts +5 -1
  21. package/src/core/models/codexModelCapabilities.ts +57 -4
  22. package/src/core/models/codexModelsCacheSeed.ts +153 -0
  23. package/src/core/models/providerModelCache.ts +106 -0
  24. package/src/core/perf/renderDebug.ts +10 -6
  25. package/src/core/process/CommandRunner.ts +12 -1
  26. package/src/core/providerLauncher/registry.ts +64 -18
  27. package/src/core/providerLauncher/types.ts +12 -9
  28. package/src/core/providerLauncher/workspaceConfig.ts +41 -26
  29. package/src/core/providerRuntime/anthropic.ts +10 -6
  30. package/src/core/providerRuntime/antigravity.ts +461 -0
  31. package/src/core/providerRuntime/claudeCodeDiscovery.ts +724 -446
  32. package/src/core/providerRuntime/claudeCodeDiscoveryDebug.ts +55 -0
  33. package/src/core/providerRuntime/contextMetadata.ts +12 -24
  34. package/src/core/providerRuntime/local.ts +129 -51
  35. package/src/core/providerRuntime/mistralVibe.ts +663 -0
  36. package/src/core/providerRuntime/models.ts +40 -15
  37. package/src/core/providerRuntime/reasoning.ts +9 -6
  38. package/src/core/providerRuntime/registry.ts +76 -4
  39. package/src/core/providerRuntime/types.ts +20 -14
  40. package/src/core/providers/codexSubprocess.ts +2 -2
  41. package/src/core/providers/types.ts +1 -1
  42. package/src/core/{attachments.ts → shared/attachments.ts} +27 -4
  43. package/src/core/{cleanupFastFail.ts → shared/cleanupFastFail.ts} +1 -1
  44. package/src/core/{hollowResponseFormat.ts → shared/hollowResponseFormat.ts} +1 -1
  45. package/src/core/terminal/clearFrameBoundary.ts +814 -0
  46. package/src/core/terminal/frameLock.ts +109 -0
  47. package/src/core/terminal/inkRenderReset.ts +123 -0
  48. package/src/core/terminal/terminalControl.ts +22 -0
  49. package/src/core/terminal/terminalTitle.ts +16 -102
  50. package/src/core/{channel.ts → version/channel.ts} +1 -1
  51. package/src/core/{updateCheck.ts → version/updateCheck.ts} +10 -5
  52. package/src/core/{launchContext.ts → workspace/launchContext.ts} +9 -16
  53. package/src/core/{planStorage.ts → workspace/planStorage.ts} +2 -2
  54. package/src/core/{workspaceGuard.ts → workspace/workspaceGuard.ts} +97 -13
  55. package/src/headless/execRunner.ts +2 -2
  56. package/src/index.tsx +66 -98
  57. package/src/session/appSession.ts +10 -7
  58. package/src/session/chatLifecycle.ts +1 -1
  59. package/src/session/liveRenderScheduler.ts +1 -1
  60. package/src/session/types.ts +1 -1
  61. package/src/test/runtimeTestUtils.ts +90 -0
  62. package/src/ui/{ActivityBars.tsx → chrome/ActivityBars.tsx} +1 -1
  63. package/src/ui/{ActivityIndicator.tsx → chrome/ActivityIndicator.tsx} +3 -3
  64. package/src/ui/{AnimatedStatusText.tsx → chrome/AnimatedStatusText.tsx} +3 -3
  65. package/src/ui/chrome/AppShell.tsx +672 -0
  66. package/src/ui/{BottomComposer.tsx → chrome/BottomComposer.tsx} +159 -158
  67. package/src/ui/{DashCard.tsx → chrome/DashCard.tsx} +2 -2
  68. package/src/ui/{RunFooter.tsx → chrome/RunFooter.tsx} +3 -3
  69. package/src/ui/chrome/RuntimeStatusBar.tsx +108 -0
  70. package/src/ui/{Spinner.tsx → chrome/Spinner.tsx} +1 -1
  71. package/src/ui/{TopHeader.tsx → chrome/TopHeader.tsx} +54 -38
  72. package/src/ui/{UpdateAvailableCard.tsx → chrome/UpdateAvailableCard.tsx} +5 -5
  73. package/src/ui/{commandNormalize.ts → input/commandNormalize.ts} +1 -1
  74. package/src/ui/{focus.ts → input/focus.ts} +1 -1
  75. package/src/ui/{inputBuffer.ts → input/inputBuffer.ts} +3 -3
  76. package/src/ui/layout.ts +298 -24
  77. package/src/ui/{AttachmentImportPanel.tsx → panels/AttachmentImportPanel.tsx} +3 -3
  78. package/src/ui/{AuthPanel.tsx → panels/AuthPanel.tsx} +5 -5
  79. package/src/ui/{BackendPicker.tsx → panels/BackendPicker.tsx} +2 -2
  80. package/src/ui/{ModePicker.tsx → panels/ModePicker.tsx} +2 -2
  81. package/src/ui/{ModelPicker.tsx → panels/ModelPicker.tsx} +2 -2
  82. package/src/ui/{ModelPickerScreen.tsx → panels/ModelPickerScreen.tsx} +224 -42
  83. package/src/ui/{ModelReasoningPicker.tsx → panels/ModelReasoningPicker.tsx} +5 -5
  84. package/src/ui/{Panel.tsx → panels/Panel.tsx} +1 -1
  85. package/src/ui/{PermissionsPanel.tsx → panels/PermissionsPanel.tsx} +3 -3
  86. package/src/ui/{PlanActionPicker.tsx → panels/PlanActionPicker.tsx} +2 -2
  87. package/src/ui/{PlanReviewPanel.tsx → panels/PlanReviewPanel.tsx} +5 -5
  88. package/src/ui/panels/ProviderPicker.tsx +737 -0
  89. package/src/ui/{ReasoningPicker.tsx → panels/ReasoningPicker.tsx} +3 -3
  90. package/src/ui/{SelectionPanel.tsx → panels/SelectionPanel.tsx} +5 -1
  91. package/src/ui/{SettingsPanel.tsx → panels/SettingsPanel.tsx} +4 -4
  92. package/src/ui/{TextEntryPanel.tsx → panels/TextEntryPanel.tsx} +4 -4
  93. package/src/ui/{ThemePicker.tsx → panels/ThemePicker.tsx} +2 -2
  94. package/src/ui/{UpdatePromptPanel.tsx → panels/UpdatePromptPanel.tsx} +17 -23
  95. package/src/ui/{Markdown.tsx → render/Markdown.tsx} +2 -2
  96. package/src/ui/{diffRenderer.ts → render/diffRenderer.ts} +1 -1
  97. package/src/ui/{logoVariants.ts → render/logoVariants.ts} +4 -8
  98. package/src/ui/{modeDisplay.ts → render/modeDisplay.ts} +2 -2
  99. package/src/ui/{outputPipeline.ts → render/outputPipeline.ts} +2 -2
  100. package/src/ui/{runtimeDisplay.ts → render/runtimeDisplay.ts} +23 -10
  101. package/src/ui/{textLayout.ts → render/textLayout.ts} +15 -4
  102. package/src/ui/{ActionRequiredBlock.tsx → timeline/ActionRequiredBlock.tsx} +3 -3
  103. package/src/ui/{AgentBlock.tsx → timeline/AgentBlock.tsx} +10 -10
  104. package/src/ui/{StaticIntroItem.tsx → timeline/StaticIntroItem.tsx} +2 -2
  105. package/src/ui/{ThinkingBlock.tsx → timeline/ThinkingBlock.tsx} +4 -4
  106. package/src/ui/{Timeline.tsx → timeline/Timeline.tsx} +1620 -1471
  107. package/src/ui/timeline/TranscriptShell.tsx +322 -0
  108. package/src/ui/{TurnGroup.tsx → timeline/TurnGroup.tsx} +15 -15
  109. package/src/ui/timeline/layoutListWindow.ts +145 -0
  110. package/src/ui/{progressEntries.ts → timeline/progressEntries.ts} +3 -3
  111. package/src/ui/{runActivityView.ts → timeline/runActivityView.ts} +1 -1
  112. package/src/ui/{timelineMeasure.ts → timeline/timelineMeasure.ts} +206 -134
  113. package/src/core/codex.ts +0 -124
  114. package/src/ui/AppShell.tsx +0 -706
  115. package/src/ui/ProviderPicker.tsx +0 -321
  116. package/src/ui/StaticTranscriptItem.tsx +0 -56
  117. /package/src/core/{inputDebug.ts → debug/inputDebug.ts} +0 -0
  118. /package/src/core/{clipboard.ts → shared/clipboard.ts} +0 -0
  119. /package/src/core/{githubDiagnostics.ts → shared/githubDiagnostics.ts} +0 -0
  120. /package/src/core/{projectInstructions.ts → workspace/projectInstructions.ts} +0 -0
  121. /package/src/core/{workspaceActivity.ts → workspace/workspaceActivity.ts} +0 -0
  122. /package/src/core/{workspaceRoot.ts → workspace/workspaceRoot.ts} +0 -0
  123. /package/src/ui/{busyStatusAnimation.ts → chrome/busyStatusAnimation.ts} +0 -0
  124. /package/src/ui/{slashCommands.ts → input/slashCommands.ts} +0 -0
  125. /package/src/ui/{terminalAnswerFormat.ts → render/terminalAnswerFormat.ts} +0 -0
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
- import { type ReasoningEffortCapability } from "../core/models/codexModelCapabilities.js";
3
- import { formatReasoningLabel } from "../config/settings.js";
4
- import { FOCUS_IDS } from "./focus.js";
2
+ import { type ReasoningEffortCapability } from "../../core/models/codexModelCapabilities.js";
3
+ import { formatReasoningLabel } from "../../config/settings.js";
4
+ import { FOCUS_IDS } from "../input/focus.js";
5
5
  import { SelectionPanel } from "./SelectionPanel.js";
6
6
 
7
7
  interface ReasoningPickerProps {
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  import { Box, Text, useFocus, useInput } from "ink";
3
3
  import SelectInput from "ink-select-input";
4
- import { useTheme } from "./theme.js";
4
+ import { useTheme } from "../theme.js";
5
5
 
6
6
  interface SelectionPanelProps {
7
7
  focusId: string;
@@ -53,6 +53,7 @@ export function SelectionPanel({
53
53
  paddingY={1}
54
54
  marginTop={1}
55
55
  width="100%"
56
+ flexDirection="column"
56
57
  >
57
58
  <SelectInput
58
59
  items={items}
@@ -61,6 +62,9 @@ export function SelectionPanel({
61
62
  onSelect={(item) => onSelect(item.value)}
62
63
  onHighlight={(item) => onHighlight?.(item.value)}
63
64
  />
65
+ <Box marginTop={1}>
66
+ <Text color={theme.textDim}>Esc to close · Enter to confirm</Text>
67
+ </Box>
64
68
  </Box>
65
69
  </Box>
66
70
  );
@@ -1,8 +1,8 @@
1
1
  import React, { useMemo, useState } from "react";
2
2
  import { Box, Text, useFocus, useInput } from "ink";
3
- import type { SettingDefinition } from "../config/settings.js";
4
- import type { FocusTargetId } from "./focus.js";
5
- import { useTheme } from "./theme.js";
3
+ import type { SettingDefinition } from "../../config/settings.js";
4
+ import type { FocusTargetId } from "../input/focus.js";
5
+ import { useTheme } from "../theme.js";
6
6
 
7
7
  export interface SettingsPanelProps<TKey extends string> {
8
8
  focusId: FocusTargetId;
@@ -39,7 +39,7 @@ export function SettingsPanel<TKey extends string>({
39
39
 
40
40
  const activeSetting = settings[selectedIndex] ?? settings[0];
41
41
  const subtitle = useMemo(() => {
42
- const instructions = "↑↓ setting ←→ option Enter save Esc cancel";
42
+ const instructions = "↑↓ setting ←→ option Esc to close · Enter to confirm";
43
43
  if (!activeSetting?.description) {
44
44
  return instructions;
45
45
  }
@@ -1,8 +1,8 @@
1
1
  import React, { useMemo, useState } from "react";
2
2
  import { Box, Text, useFocus, useInput } from "ink";
3
- import type { FocusTargetId } from "./focus.js";
4
- import { useTheme } from "./theme.js";
5
- import { stripMouseEscapes } from "./inputBuffer.js";
3
+ import type { FocusTargetId } from "../input/focus.js";
4
+ import { useTheme } from "../theme.js";
5
+ import { stripMouseEscapes } from "../input/inputBuffer.js";
6
6
 
7
7
  interface TextEntryPanelProps {
8
8
  focusId: FocusTargetId;
@@ -27,7 +27,7 @@ export function TextEntryPanel({
27
27
  placeholder = "",
28
28
  initialValue = "",
29
29
  inputLabel = "Input",
30
- footerHint = "Enter submit Esc cancel Backspace delete",
30
+ footerHint = "Esc to close · Enter to confirm",
31
31
  onSubmit,
32
32
  onCancel,
33
33
  }: TextEntryPanelProps) {
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
- import { AVAILABLE_THEMES, formatThemeLabel } from "../config/settings.js";
3
- import { FOCUS_IDS } from "./focus.js";
2
+ import { AVAILABLE_THEMES, formatThemeLabel } from "../../config/settings.js";
3
+ import { FOCUS_IDS } from "../input/focus.js";
4
4
  import { SelectionPanel } from "./SelectionPanel.js";
5
5
 
6
6
  interface ThemePickerProps {
@@ -1,8 +1,8 @@
1
1
  import React, { useEffect, useRef, useState } from "react";
2
2
  import { Box, Text, useFocus, useInput } from "ink";
3
3
  import { spawn } from "child_process";
4
- import { useTheme } from "./theme.js";
5
- import { CODEXA_NPM_PACKAGE, CODEXA_UPDATE_COMMAND } from "../core/updateCheck.js";
4
+ import { useTheme } from "../theme.js";
5
+ import { CODEXA_NPM_PACKAGE, CODEXA_UPDATE_COMMAND, formatVersionLabel } from "../../core/version/updateCheck.js";
6
6
 
7
7
  type Phase = "menu" | "running" | "done" | "error";
8
8
 
@@ -38,6 +38,10 @@ export function UpdatePromptPanel({
38
38
  const spawnStartedRef = useRef(false);
39
39
 
40
40
  useInput((input, key) => {
41
+ if (key.escape) {
42
+ onSkip();
43
+ return;
44
+ }
41
45
  if (phase === "menu") {
42
46
  if (key.upArrow || input === "k") {
43
47
  setSelectedIndex((i) => Math.max(0, i - 1));
@@ -57,12 +61,8 @@ export function UpdatePromptPanel({
57
61
  }
58
62
  return;
59
63
  }
60
- if (key.escape) {
61
- onSkip();
62
- return;
63
- }
64
64
  } else if (phase === "done" || phase === "error") {
65
- if (key.return || key.escape) {
65
+ if (key.return) {
66
66
  onSkip();
67
67
  }
68
68
  }
@@ -107,9 +107,9 @@ export function UpdatePromptPanel({
107
107
  };
108
108
  }, [phase]);
109
109
 
110
- const hintText = phase === "menu"
111
- ? "↑↓/jk select Enter confirm Esc skip"
112
- : "Press Enter to close";
110
+ const footerText = phase === "menu"
111
+ ? "Esc to close · Enter to confirm"
112
+ : "Esc to close";
113
113
 
114
114
  return (
115
115
  <Box flexDirection="column" width="100%" marginTop={1}>
@@ -122,11 +122,10 @@ export function UpdatePromptPanel({
122
122
  flexDirection="column"
123
123
  >
124
124
  <Box>
125
- <Text color={theme.accent} bold>Update available </Text>
126
- <Text color={theme.textMuted}>{hintText}</Text>
125
+ <Text color={theme.accent} bold>{`Update available: Codexa ${formatVersionLabel(latestVersion)}`}</Text>
127
126
  </Box>
128
127
  <Box marginTop={1}>
129
- <Text color={theme.text}>{`✨ ${currentVersion} -> ${latestVersion}`}</Text>
128
+ <Text color={theme.text}>{`${formatVersionLabel(currentVersion)} -> ${formatVersionLabel(latestVersion)}`}</Text>
130
129
  </Box>
131
130
  <Box>
132
131
  <Text color={theme.textMuted}>{`Package: ${CODEXA_NPM_PACKAGE}`}</Text>
@@ -138,7 +137,7 @@ export function UpdatePromptPanel({
138
137
 
139
138
  <Box
140
139
  borderStyle="round"
141
- borderColor={theme.borderFocused}
140
+ borderColor={phase === "menu" ? theme.borderFocused : theme.border}
142
141
  paddingX={2}
143
142
  paddingY={1}
144
143
  marginTop={1}
@@ -160,9 +159,6 @@ export function UpdatePromptPanel({
160
159
  </Text>
161
160
  </Box>
162
161
  ))}
163
- <Box marginTop={1}>
164
- <Text color={theme.textDim}>Press enter to continue</Text>
165
- </Box>
166
162
  </>
167
163
  )}
168
164
 
@@ -179,9 +175,6 @@ export function UpdatePromptPanel({
179
175
  <>
180
176
  <Text color={theme.success}>{"Codexa was updated successfully."}</Text>
181
177
  <Text color={theme.textMuted}>{"Restart Codexa to use the new version."}</Text>
182
- <Box marginTop={1}>
183
- <Text color={theme.textDim}>Press Enter to close</Text>
184
- </Box>
185
178
  </>
186
179
  )}
187
180
 
@@ -192,11 +185,12 @@ export function UpdatePromptPanel({
192
185
  {outputLines.slice(-5).map((line, i) => (
193
186
  <Text key={i} color={theme.textDim}>{line}</Text>
194
187
  ))}
195
- <Box marginTop={1}>
196
- <Text color={theme.textDim}>Press Enter to close</Text>
197
- </Box>
198
188
  </>
199
189
  )}
190
+
191
+ <Box marginTop={1}>
192
+ <Text color={theme.textDim}>{footerText}</Text>
193
+ </Box>
200
194
  </Box>
201
195
  </Box>
202
196
  );
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  import { Box, Text } from "ink";
3
- import { useTheme } from "./theme.js";
4
- import { Panel } from "./Panel.js";
3
+ import { useTheme } from "../theme.js";
4
+ import { Panel } from "../panels/Panel.js";
5
5
  import { maybeRenderDiff, type DiffRenderLineType } from "./diffRenderer.js";
6
6
  import { formatLocalPathForTerminal, formatTerminalAnswerInline } from "./terminalAnswerFormat.js";
7
7
 
@@ -1,4 +1,4 @@
1
- import { sanitizeTerminalOutput } from "../core/terminal/terminalSanitize.js";
1
+ import { sanitizeTerminalOutput } from "../../core/terminal/terminalSanitize.js";
2
2
 
3
3
  export type DiffRenderLineType = "file" | "hunk" | "add" | "remove" | "context" | "meta";
4
4
 
@@ -10,8 +10,8 @@ import { getTextWidth } from "./textLayout.js";
10
10
  // The companion `wrap="truncate"` rule keeps each row on exactly one terminal
11
11
  // line regardless of the surrounding Ink flex layout.
12
12
 
13
- // Canonical Codexa brand wordmark — the ██ block art is the authoritative logo.
14
- // Always shown on any normal-width terminal (≥ LOGO_LARGE_MIN_COLS cols).
13
+ // Canonical Codexa brand wordmark — the ██ block art is the authoritative
14
+ // large logo for wide/max layouts.
15
15
  export const CODEXA_WORDMARK = [
16
16
  " ██████╗ ██████╗ ██████╗ ███████╗██╗ ██╗ █████╗ ",
17
17
  "██╔════╝██╔═══██╗██╔══██╗██╔════╝╚██╗██╔╝██╔══██╗",
@@ -48,16 +48,13 @@ export const LOGO_COMPACT_MIN_COLS = 48;
48
48
  // Minimum terminal rows each variant needs to render without crowding out the
49
49
  // metadata + composer. A wide-but-short terminal (e.g. VS Code's bottom panel)
50
50
  // must step DOWN to a smaller logo instead of dropping straight to text-only.
51
- export const LOGO_LARGE_MIN_ROWS = 24;
51
+ export const LOGO_LARGE_MIN_ROWS = 35;
52
52
  export const LOGO_MEDIUM_MIN_ROWS = 16;
53
53
  export const LOGO_COMPACT_MIN_ROWS = 12;
54
54
 
55
- // LOGO_MEDIUM is kept as an exported constant but intentionally omitted from
56
- // LOGO_VARIANTS. At 72+ cols, LOGO_LARGE_MIN_COLS = LOGO_MEDIUM_MIN_COLS = 72,
57
- // so LOGO_LARGE always wins. Below 72 cols the viewport is too narrow for the
58
- // thin ASCII art to add value over the compact single-line fallback.
59
55
  const LOGO_VARIANTS: readonly { logo: readonly string[]; minCols: number; minRows: number }[] = [
60
56
  { logo: LOGO_LARGE, minCols: LOGO_LARGE_MIN_COLS, minRows: LOGO_LARGE_MIN_ROWS },
57
+ { logo: LOGO_MEDIUM, minCols: LOGO_MEDIUM_MIN_COLS, minRows: LOGO_MEDIUM_MIN_ROWS },
61
58
  { logo: LOGO_COMPACT, minCols: LOGO_COMPACT_MIN_COLS, minRows: LOGO_COMPACT_MIN_ROWS },
62
59
  ];
63
60
 
@@ -92,7 +89,6 @@ export function selectLogoVariantForViewport(cols: number, rows: number): readon
92
89
  if (process.env["CODEXA_COMPACT_LOGO"] === "1") {
93
90
  return rows >= LOGO_COMPACT_MIN_ROWS ? LOGO_COMPACT : [];
94
91
  }
95
- if (cols >= LOGO_LARGE_MIN_COLS) return LOGO_LARGE;
96
92
  for (const variant of LOGO_VARIANTS) {
97
93
  if (cols >= variant.minCols && rows >= variant.minRows) {
98
94
  return variant.logo;
@@ -1,5 +1,5 @@
1
- import { formatModeLabel } from "../config/settings.js";
2
- import type { Theme } from "./theme.js";
1
+ import { formatModeLabel } from "../../config/settings.js";
2
+ import type { Theme } from "../theme.js";
3
3
 
4
4
  export interface ModeDisplaySpec {
5
5
  label: string;
@@ -1,5 +1,5 @@
1
- import { isNoiseLine } from "../core/providers/codexTranscript.js";
2
- import { sanitizeTerminalOutput } from "../core/terminal/terminalSanitize.js";
1
+ import { isNoiseLine } from "../../core/providers/codexTranscript.js";
2
+ import { sanitizeTerminalOutput } from "../../core/terminal/terminalSanitize.js";
3
3
  import { parseMarkdown, type Segment } from "./Markdown.js";
4
4
 
5
5
  /**
@@ -1,10 +1,11 @@
1
- import { formatModeLabel, formatReasoningLabel } from "../config/settings.js";
2
- import type { RuntimeSummary } from "../config/runtimeConfig.js";
3
- import type { CodexModelCapability } from "../core/models/codexModelCapabilities.js";
4
- import type { ModelSpec } from "../core/models/modelSpecs.js";
5
- import type { ModelContextMetadata } from "../core/providerRuntime/contextMetadata.js";
6
- import { contextMetadataToModelSpec, formatContextCompact } from "../core/providerRuntime/contextMetadata.js";
7
- import type { ActiveProviderRoute } from "../core/providerRuntime/types.js";
1
+ import { formatModeLabel, formatReasoningLabel } from "../../config/settings.js";
2
+ import type { RuntimeSummary } from "../../config/runtimeConfig.js";
3
+ import type { CodexModelCapability } from "../../core/models/codexModelCapabilities.js";
4
+ import type { ModelSpec } from "../../core/models/modelSpecs.js";
5
+ import type { ModelContextMetadata } from "../../core/providerRuntime/contextMetadata.js";
6
+ import { contextMetadataToModelSpec, formatContextCompact } from "../../core/providerRuntime/contextMetadata.js";
7
+ import type { ActiveProviderRoute } from "../../core/providerRuntime/types.js";
8
+ import { getAntigravityModelLabel } from "../../core/providerRuntime/antigravity.js";
8
9
 
9
10
  export interface ActiveRuntimeDisplayInput {
10
11
  route: ActiveProviderRoute;
@@ -28,7 +29,9 @@ const PROVIDER_DISPLAY: Record<string, string> = {
28
29
  openai: "OpenAI Codex CLI",
29
30
  anthropic: "Claude Code CLI",
30
31
  google: "Gemini CLI",
32
+ mistral: "Mistral Vibe CLI",
31
33
  local: "Local",
34
+ antigravity: "Antigravity CLI",
32
35
  };
33
36
 
34
37
  function formatContextLimit(value: number): string {
@@ -53,6 +56,9 @@ function getModelLabel(route: ActiveProviderRoute, capability?: CodexModelCapabi
53
56
  if (route.providerId === "anthropic") {
54
57
  return capability?.label ?? route.modelId;
55
58
  }
59
+ if (route.providerId === "antigravity") {
60
+ return getAntigravityModelLabel(route.modelId);
61
+ }
56
62
  return route.modelId;
57
63
  }
58
64
 
@@ -65,7 +71,10 @@ export function buildActiveRuntimeDisplay({
65
71
  contextMetadata = null,
66
72
  }: ActiveRuntimeDisplayInput): ActiveRuntimeDisplay {
67
73
  const providerLabel = PROVIDER_DISPLAY[route.providerId] ?? route.providerId;
68
- const reasoning = formatReasoningLabel(route.reasoning ?? reasoningLevel);
74
+ const rawReasoning = route.providerId === "antigravity"
75
+ ? route.reasoning
76
+ : route.reasoning ?? reasoningLevel;
77
+ const reasoning = rawReasoning ? formatReasoningLabel(rawReasoning) : null;
69
78
  const modelLabel = getModelLabel(route, modelCapability);
70
79
  const validContextMetadata = isContextForRoute(contextMetadata, route) ? contextMetadata : null;
71
80
  const contextDisplay = validContextMetadata?.contextLength != null
@@ -82,8 +91,12 @@ export function buildActiveRuntimeDisplay({
82
91
 
83
92
  return {
84
93
  providerLabel,
85
- modelDisplay: `${providerLabel} / ${modelLabel} / reasoning: ${reasoning}`,
86
- footerModelDisplay: `${providerLabel} / ${modelLabel} (${reasoning})`,
94
+ modelDisplay: reasoning
95
+ ? `${providerLabel} / ${modelLabel} / reasoning: ${reasoning}`
96
+ : `${providerLabel} / ${modelLabel}`,
97
+ footerModelDisplay: reasoning
98
+ ? `${providerLabel} / ${modelLabel} (${reasoning})`
99
+ : `${providerLabel} / ${modelLabel}`,
87
100
  contextDisplay,
88
101
  modeLabel: formatModeLabel(mode),
89
102
  modelSpec,
@@ -198,9 +198,14 @@ export function splitTextAtColumn(text: string, column: number): { before: strin
198
198
 
199
199
  // ─── Text wrapping ────────────────────────────────────────────────────────────
200
200
 
201
- export function wrapTextRows(text: string, maxWidth: number): WrappedTextRow[] {
201
+ export function wrapTextRows(
202
+ text: string,
203
+ maxWidth: number,
204
+ firstLineWidth: number = maxWidth,
205
+ ): WrappedTextRow[] {
202
206
  const normalized = normalizeLineBreaks(text);
203
207
  const safeWidth = Math.max(1, maxWidth);
208
+ const safeFirstWidth = Math.max(1, firstLineWidth);
204
209
  const rows: WrappedTextRow[] = [];
205
210
  let rowStart = 0;
206
211
  let rowText = "";
@@ -220,7 +225,9 @@ export function wrapTextRows(text: string, maxWidth: number): WrappedTextRow[] {
220
225
  continue;
221
226
  }
222
227
 
223
- if (rowText.length > 0 && rowWidth + unit.width > safeWidth) {
228
+ // Only the first emitted row honors firstLineWidth; later rows use maxWidth.
229
+ const limit = rows.length === 0 ? safeFirstWidth : safeWidth;
230
+ if (rowText.length > 0 && rowWidth + unit.width > limit) {
224
231
  rows.push({
225
232
  text: rowText,
226
233
  start: rowStart,
@@ -252,8 +259,12 @@ export function wrapTextRows(text: string, maxWidth: number): WrappedTextRow[] {
252
259
  }];
253
260
  }
254
261
 
255
- export function wrapPlainText(text: string, maxWidth: number): string[] {
256
- return wrapTextRows(text, maxWidth).map((row) => row.text);
262
+ export function wrapPlainText(
263
+ text: string,
264
+ maxWidth: number,
265
+ firstLineWidth: number = maxWidth,
266
+ ): string[] {
267
+ return wrapTextRows(text, maxWidth, firstLineWidth).map((row) => row.text);
257
268
  }
258
269
 
259
270
  export function wrapCommandText(text: string, maxWidth: number): string[] {
@@ -1,8 +1,8 @@
1
1
  import React from "react";
2
2
  import { Box, Text } from "ink";
3
- import { getUsableShellWidth } from "./layout.js";
4
- import { wrapPlainText } from "./textLayout.js";
5
- import { useTheme } from "./theme.js";
3
+ import { getUsableShellWidth } from "../layout.js";
4
+ import { wrapPlainText } from "../render/textLayout.js";
5
+ import { useTheme } from "../theme.js";
6
6
 
7
7
  interface ActionRequiredBlockProps {
8
8
  cols: number;
@@ -1,21 +1,21 @@
1
1
  import React, { memo, useDeferredValue, useMemo } from "react";
2
2
  import { Box, Text } from "ink";
3
- import type { AssistantEvent, RunEvent } from "../session/types.js";
4
- import { getAssistantContent } from "../session/types.js";
5
- import { MemoizedRenderMessage } from "./Markdown.js";
6
- import { getUsableShellWidth } from "./layout.js";
7
- import { useTheme } from "./theme.js";
8
- import { wrapPlainText } from "./textLayout.js";
9
- import { RUN_OUTPUT_TRUNCATION_NOTICE } from "../session/chatLifecycle.js";
10
- import { sanitizeTerminalOutput } from "../core/terminal/terminalSanitize.js";
3
+ import type { AssistantEvent, RunEvent } from "../../session/types.js";
4
+ import { getAssistantContent } from "../../session/types.js";
5
+ import { MemoizedRenderMessage } from "../render/Markdown.js";
6
+ import { getUsableShellWidth } from "../layout.js";
7
+ import { useTheme } from "../theme.js";
8
+ import { wrapPlainText } from "../render/textLayout.js";
9
+ import { RUN_OUTPUT_TRUNCATION_NOTICE } from "../../session/chatLifecycle.js";
10
+ import { sanitizeTerminalOutput } from "../../core/terminal/terminalSanitize.js";
11
11
  import {
12
12
  sanitizeOutput,
13
13
  sanitizeStreamChunk,
14
14
  normalizeOutput,
15
15
  classifyOutput,
16
16
  formatForBox,
17
- } from "./outputPipeline.js";
18
- import { DashCard } from "./DashCard.js";
17
+ } from "../render/outputPipeline.js";
18
+ import { DashCard } from "../chrome/DashCard.js";
19
19
 
20
20
 
21
21
  function formatDuration(ms: number): string {
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  import { Box } from "ink";
3
- import type { CodexAuthState } from "../core/auth/codexAuth.js";
4
- import { getShellWidth, type Layout, type StartupHeaderMode } from "./layout.js";
3
+ import type { CodexAuthState } from "../../core/auth/codexAuth.js";
4
+ import { getShellWidth, type Layout, type StartupHeaderMode } from "../layout.js";
5
5
  import { buildIntroRenderItem, TimelineRowView } from "./Timeline.js";
6
6
  import type { TimelineRow } from "./timelineMeasure.js";
7
7
  import { buildTimelineSnapshot } from "./timelineMeasure.js";
@@ -1,9 +1,9 @@
1
1
  import React from "react";
2
2
  import { Box, Text } from "ink";
3
- import type { RunEvent } from "../session/types.js";
4
- import { clampVisualText, getUsableShellWidth } from "./layout.js";
5
- import { useTheme } from "./theme.js";
6
- import { DashCard } from "./DashCard.js";
3
+ import type { RunEvent } from "../../session/types.js";
4
+ import { clampVisualText, getUsableShellWidth } from "../layout.js";
5
+ import { useTheme } from "../theme.js";
6
+ import { DashCard } from "../chrome/DashCard.js";
7
7
  import {
8
8
  formatProgressBlockBodyLines,
9
9
  getProgressUpdateCount,