@golba98/codexa 1.0.1 → 1.0.3

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 (234) hide show
  1. package/README.md +391 -94
  2. package/package.json +14 -8
  3. package/src/app.tsx +196 -63
  4. package/src/commands/handler.ts +9 -4
  5. package/src/config/buildInfo.ts +3 -0
  6. package/src/config/launchArgs.ts +10 -0
  7. package/src/config/layeredConfig.ts +0 -0
  8. package/src/config/persistence.ts +51 -0
  9. package/src/config/runtimeConfig.ts +4 -0
  10. package/src/config/settings.ts +6 -13
  11. package/src/config/toml-serialize.ts +0 -0
  12. package/src/config/trustStore.ts +0 -0
  13. package/src/config/updateCheckCache.ts +63 -0
  14. package/src/core/attachments.ts +0 -0
  15. package/src/core/auth/codexAuth.ts +0 -0
  16. package/src/core/channel.ts +23 -0
  17. package/src/core/cleanupFastFail.ts +0 -0
  18. package/src/core/clipboard.ts +0 -0
  19. package/src/core/codex.ts +0 -0
  20. package/src/core/codexExecArgs.ts +0 -0
  21. package/src/core/codexLaunch.ts +0 -0
  22. package/src/core/codexPrompt.ts +0 -0
  23. package/src/core/executables/claudeExecutable.ts +0 -0
  24. package/src/core/executables/codexExecutable.ts +1 -0
  25. package/src/core/executables/executableResolver.ts +61 -43
  26. package/src/core/executables/geminiExecutable.ts +0 -0
  27. package/src/core/githubDiagnostics.ts +0 -0
  28. package/src/core/hollowResponseFormat.ts +0 -0
  29. package/src/core/inputDebug.ts +0 -0
  30. package/src/core/launchContext.ts +33 -33
  31. package/src/core/models/codexCapabilities.ts +0 -0
  32. package/src/core/models/codexModelCapabilities.ts +0 -0
  33. package/src/core/models/modelSpecs.ts +0 -0
  34. package/src/core/perf/profiler.ts +0 -0
  35. package/src/core/perf/renderDebug.ts +0 -0
  36. package/src/core/planStorage.ts +0 -0
  37. package/src/core/process/CommandRunner.ts +0 -0
  38. package/src/core/process/processValidation.ts +9 -5
  39. package/src/core/projectInstructions.ts +0 -0
  40. package/src/core/providerLauncher/launcher.ts +59 -42
  41. package/src/core/providerLauncher/registry.ts +0 -0
  42. package/src/core/providerLauncher/types.ts +0 -0
  43. package/src/core/providerLauncher/workspaceConfig.ts +0 -0
  44. package/src/core/providerRuntime/anthropic.ts +0 -0
  45. package/src/core/providerRuntime/capabilityProfile.ts +0 -0
  46. package/src/core/providerRuntime/claudeCodeDiscovery.ts +0 -0
  47. package/src/core/providerRuntime/contextMetadata.ts +0 -0
  48. package/src/core/providerRuntime/gemini.ts +0 -0
  49. package/src/core/providerRuntime/lmstudio.ts +0 -0
  50. package/src/core/providerRuntime/local.ts +0 -0
  51. package/src/core/providerRuntime/models.ts +0 -0
  52. package/src/core/providerRuntime/reasoning.ts +0 -0
  53. package/src/core/providerRuntime/registry.ts +0 -0
  54. package/src/core/providerRuntime/types.ts +0 -0
  55. package/src/core/providers/codexJsonStream.ts +0 -0
  56. package/src/core/providers/codexSubprocess.ts +0 -0
  57. package/src/core/providers/codexTranscript.ts +0 -0
  58. package/src/core/providers/openaiNative.ts +0 -0
  59. package/src/core/providers/registry.ts +0 -0
  60. package/src/core/providers/types.ts +0 -0
  61. package/src/core/terminal/startupClear.ts +20 -0
  62. package/src/core/terminal/terminalCapabilities.ts +0 -0
  63. package/src/core/terminal/terminalControl.ts +0 -0
  64. package/src/core/terminal/terminalSanitize.ts +0 -0
  65. package/src/core/terminal/terminalSelection.ts +0 -0
  66. package/src/core/terminal/terminalTitle.ts +0 -0
  67. package/src/core/updateCheck.ts +188 -0
  68. package/src/core/workspaceActivity.ts +0 -0
  69. package/src/core/workspaceGuard.ts +0 -0
  70. package/src/core/workspaceRoot.ts +0 -0
  71. package/src/exec.ts +0 -0
  72. package/src/headless/execArgs.ts +1 -0
  73. package/src/headless/execRunner.ts +0 -0
  74. package/src/index.tsx +13 -9
  75. package/src/session/appSession.ts +0 -0
  76. package/src/session/chatLifecycle.ts +0 -0
  77. package/src/session/liveRenderScheduler.ts +0 -0
  78. package/src/session/planFlow.ts +0 -0
  79. package/src/session/planTranscript.ts +0 -0
  80. package/src/session/promptRunSchedule.ts +0 -0
  81. package/src/session/types.ts +2 -1
  82. package/src/test/runtimeTestUtils.ts +0 -0
  83. package/src/types/react-dom.d.ts +0 -0
  84. package/src/ui/ActionRequiredBlock.tsx +5 -5
  85. package/src/ui/ActivityBars.tsx +3 -3
  86. package/src/ui/ActivityIndicator.tsx +6 -6
  87. package/src/ui/AgentBlock.tsx +6 -6
  88. package/src/ui/AnimatedStatusText.tsx +1 -1
  89. package/src/ui/AppShell.tsx +13 -5
  90. package/src/ui/AttachmentImportPanel.tsx +8 -8
  91. package/src/ui/AuthPanel.tsx +20 -20
  92. package/src/ui/BackendPicker.tsx +0 -0
  93. package/src/ui/BottomComposer.tsx +117 -78
  94. package/src/ui/DashCard.tsx +3 -3
  95. package/src/ui/Markdown.tsx +17 -17
  96. package/src/ui/ModePicker.tsx +0 -0
  97. package/src/ui/ModelPicker.tsx +0 -0
  98. package/src/ui/ModelPickerScreen.tsx +10 -10
  99. package/src/ui/ModelReasoningPicker.tsx +14 -14
  100. package/src/ui/Panel.tsx +3 -3
  101. package/src/ui/PermissionsPanel.tsx +0 -0
  102. package/src/ui/PlanActionPicker.tsx +6 -6
  103. package/src/ui/PlanReviewPanel.tsx +8 -8
  104. package/src/ui/ProviderPicker.tsx +18 -18
  105. package/src/ui/ReasoningPicker.tsx +0 -0
  106. package/src/ui/RunFooter.tsx +3 -3
  107. package/src/ui/SelectionPanel.tsx +4 -4
  108. package/src/ui/SettingsPanel.tsx +8 -8
  109. package/src/ui/Spinner.tsx +1 -1
  110. package/src/ui/StaticIntroItem.tsx +0 -0
  111. package/src/ui/StaticTranscriptItem.tsx +0 -0
  112. package/src/ui/TextEntryPanel.tsx +10 -10
  113. package/src/ui/ThemePicker.tsx +0 -0
  114. package/src/ui/ThinkingBlock.tsx +8 -8
  115. package/src/ui/Timeline.tsx +21 -17
  116. package/src/ui/TopHeader.tsx +421 -257
  117. package/src/ui/TurnGroup.tsx +31 -31
  118. package/src/ui/UpdateAvailableCard.tsx +41 -0
  119. package/src/ui/UpdatePromptPanel.tsx +203 -0
  120. package/src/ui/busyStatusAnimation.ts +0 -0
  121. package/src/ui/commandNormalize.ts +0 -0
  122. package/src/ui/diffRenderer.ts +0 -0
  123. package/src/ui/focus.ts +3 -0
  124. package/src/ui/inputBuffer.ts +0 -0
  125. package/src/ui/layout.ts +1 -1
  126. package/src/ui/logoVariants.ts +107 -0
  127. package/src/ui/modeDisplay.ts +12 -12
  128. package/src/ui/outputPipeline.ts +0 -0
  129. package/src/ui/progressEntries.ts +0 -0
  130. package/src/ui/runActivityView.ts +0 -0
  131. package/src/ui/runtimeDisplay.ts +100 -0
  132. package/src/ui/slashCommands.ts +0 -0
  133. package/src/ui/terminalAnswerFormat.ts +0 -0
  134. package/src/ui/textLayout.ts +0 -0
  135. package/src/ui/theme.tsx +274 -395
  136. package/src/ui/themeFlow.ts +0 -0
  137. package/src/ui/timelineMeasure.ts +29 -19
  138. package/src/ui/useThrottledValue.ts +0 -0
  139. package/scripts/audit-codexa-capabilities.mjs +0 -466
  140. package/scripts/smoke-terminal-bench.mjs +0 -35
  141. package/src/appRenderStability.test.ts +0 -131
  142. package/src/commands/handler.test.ts +0 -643
  143. package/src/config/launchArgs.test.ts +0 -158
  144. package/src/config/layeredConfig.test.ts +0 -143
  145. package/src/config/persistence.test.ts +0 -110
  146. package/src/config/runtimeConfig.test.ts +0 -218
  147. package/src/config/settings.test.ts +0 -155
  148. package/src/config/trustStore.test.ts +0 -29
  149. package/src/core/attachments.test.ts +0 -155
  150. package/src/core/auth/codexAuth.test.ts +0 -68
  151. package/src/core/cleanupFastFail.test.ts +0 -76
  152. package/src/core/codexExecArgs.test.ts +0 -195
  153. package/src/core/codexLaunch.test.ts +0 -205
  154. package/src/core/codexPrompt.test.ts +0 -252
  155. package/src/core/executables/codexExecutable.test.ts +0 -212
  156. package/src/core/executables/executableResolver.test.ts +0 -129
  157. package/src/core/executables/geminiExecutable.test.ts +0 -116
  158. package/src/core/executables/pathSanityScan.test.ts +0 -47
  159. package/src/core/githubDiagnostics.test.ts +0 -92
  160. package/src/core/hollowResponseFormat.test.ts +0 -58
  161. package/src/core/launchContext.test.ts +0 -157
  162. package/src/core/models/codexCapabilities.test.ts +0 -45
  163. package/src/core/models/codexModelCapabilities.test.ts +0 -246
  164. package/src/core/models/modelSpecs.test.ts +0 -283
  165. package/src/core/perf/renderDebug.test.ts +0 -230
  166. package/src/core/planStorage.test.ts +0 -143
  167. package/src/core/process/CommandRunner.test.ts +0 -105
  168. package/src/core/projectInstructions.test.ts +0 -50
  169. package/src/core/providerLauncher/launcher.test.ts +0 -238
  170. package/src/core/providerLauncher/registry.test.ts +0 -324
  171. package/src/core/providerLauncher/workspaceConfig.test.ts +0 -638
  172. package/src/core/providerRuntime/anthropic.test.ts +0 -1120
  173. package/src/core/providerRuntime/capabilityProfile.test.ts +0 -311
  174. package/src/core/providerRuntime/contextMetadata.test.ts +0 -468
  175. package/src/core/providerRuntime/gemini.test.ts +0 -437
  176. package/src/core/providerRuntime/lmstudio.test.ts +0 -168
  177. package/src/core/providerRuntime/local.test.ts +0 -787
  178. package/src/core/providerRuntime/registry.test.ts +0 -233
  179. package/src/core/providers/codexJsonStream.test.ts +0 -148
  180. package/src/core/providers/codexSubprocess.test.ts +0 -68
  181. package/src/core/providers/codexTranscript.test.ts +0 -284
  182. package/src/core/terminal/terminalCapabilities.test.ts +0 -93
  183. package/src/core/terminal/terminalControl.test.ts +0 -75
  184. package/src/core/terminal/terminalSanitize.test.ts +0 -22
  185. package/src/core/terminal/terminalSelection.test.ts +0 -42
  186. package/src/core/terminal/terminalTitle.test.ts +0 -328
  187. package/src/core/workspaceActivity.test.ts +0 -163
  188. package/src/core/workspaceGuard.test.ts +0 -151
  189. package/src/core/workspaceRoot.test.ts +0 -23
  190. package/src/exec.test.ts +0 -13
  191. package/src/headless/execArgs.test.ts +0 -147
  192. package/src/headless/execRunner.test.ts +0 -434
  193. package/src/index.test.tsx +0 -618
  194. package/src/session/appSession.test.ts +0 -897
  195. package/src/session/chatLifecycle.test.ts +0 -64
  196. package/src/session/liveRenderScheduler.test.ts +0 -201
  197. package/src/session/planFlow.test.ts +0 -103
  198. package/src/session/planTranscript.test.ts +0 -65
  199. package/src/session/promptRunSchedule.test.ts +0 -36
  200. package/src/ui/ActivityIndicator.test.tsx +0 -58
  201. package/src/ui/AgentBlock.test.ts +0 -6
  202. package/src/ui/AnimatedStatusText.test.ts +0 -16
  203. package/src/ui/AppShell.test.tsx +0 -1739
  204. package/src/ui/AttachmentImportPanel.test.tsx +0 -204
  205. package/src/ui/BottomComposer.test.ts +0 -674
  206. package/src/ui/CodexLogo.tsx +0 -55
  207. package/src/ui/Markdown.test.ts +0 -157
  208. package/src/ui/ModelPickerProviderScope.test.tsx +0 -411
  209. package/src/ui/ModelPickerScreen.test.tsx +0 -99
  210. package/src/ui/ModelPickerState.test.tsx +0 -151
  211. package/src/ui/ModelReasoningPicker.test.tsx +0 -447
  212. package/src/ui/PlanReviewPanel.test.tsx +0 -267
  213. package/src/ui/PromptCardBorder.test.tsx +0 -161
  214. package/src/ui/ProviderPicker.test.tsx +0 -289
  215. package/src/ui/ProviderShortcut.test.tsx +0 -143
  216. package/src/ui/SettingsPanel.test.tsx +0 -233
  217. package/src/ui/Timeline.test.ts +0 -2067
  218. package/src/ui/TimelineNavigation.test.tsx +0 -201
  219. package/src/ui/TopHeader.test.tsx +0 -239
  220. package/src/ui/TurnGroup.test.tsx +0 -365
  221. package/src/ui/busyStatusAnimation.test.ts +0 -30
  222. package/src/ui/commandNormalize.test.ts +0 -142
  223. package/src/ui/diffRenderer.test.ts +0 -102
  224. package/src/ui/focusFlow.test.tsx +0 -1098
  225. package/src/ui/inputBuffer.test.ts +0 -151
  226. package/src/ui/layout.test.ts +0 -145
  227. package/src/ui/modeDisplay.test.ts +0 -42
  228. package/src/ui/runActivityView.test.ts +0 -89
  229. package/src/ui/runLifecycleView.test.tsx +0 -237
  230. package/src/ui/statusRenderIsolation.test.tsx +0 -654
  231. package/src/ui/terminalAnswerFormat.test.ts +0 -19
  232. package/src/ui/textLayout.test.ts +0 -18
  233. package/src/ui/themeFlow.test.ts +0 -53
  234. package/src/ui/timelineMeasureCache.test.ts +0 -986
File without changes
@@ -25,6 +25,7 @@ import { selectVisibleRunActivity } from "./runActivityView.js";
25
25
  import { getTextUnits, getTextWidth, wrapPlainText, wrapCommandText, splitTextAtColumn } from "./textLayout.js";
26
26
  import type { RenderTimelineItem } from "./Timeline.js";
27
27
  import { normalizePlanReviewMarkdown } from "../core/planStorage.js";
28
+ import { selectLogoVariant, LOGO_LARGE_MIN_COLS } from "./logoVariants.js";
28
29
 
29
30
  // ─── Exported types ───────────────────────────────────────────────────────────
30
31
 
@@ -40,7 +41,10 @@ export type TimelineTone =
40
41
  | "borderSubtle"
41
42
  | "borderActive"
42
43
  | "panel"
43
- | "star";
44
+ | "star"
45
+ | "logoPrimary"
46
+ | "logoSecondary"
47
+ | "logoShadow";
44
48
 
45
49
  export interface TimelineRowSpan {
46
50
  text: string;
@@ -95,14 +99,8 @@ const MAX_VISIBLE_PROGRESS_ENTRIES = 3;
95
99
  const COMPACT_PROCESSING_BODY_LINE_CAP = 4;
96
100
  const COMPACT_STREAMING_TAIL_CAP = 6;
97
101
  const VISIBLE_THINKING_SOURCES = new Set(["reasoning", "todo"]);
98
- const INTRO_WORDMARK = [
99
- " ██████╗ ██████╗ ██████╗ ███████╗██╗ ██╗ █████╗ ",
100
- "██╔════╝██╔═══██╗██╔══██╗██╔════╝╚██╗██╔╝██╔══██╗",
101
- "██║ ██║ ██║██║ ██║█████╗ ╚███╔╝ ███████║",
102
- "██║ ██║ ██║██║ ██║██╔══╝ ██╔██╗ ██╔══██║",
103
- "╚██████╗╚██████╔╝██████╔╝███████╗██╔╝ ██╗██║ ██║",
104
- " ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝",
105
- ];
102
+ // Logo rows for the intro item — selected dynamically from logoVariants.ts so
103
+ // the dead-code intro path stays consistent with the live TopHeader rendering.
106
104
 
107
105
  // Matches sentence-ending punctuation followed (optionally after whitespace) by
108
106
  // a capital letter starting a new word. Requires [A-Z] to be followed by [a-z]
@@ -1562,9 +1560,10 @@ export function buildIntroRows(item: Extract<RenderTimelineItem, { type: "intro"
1562
1560
  }
1563
1561
 
1564
1562
  const logoRows = startupHeaderMode === "large"
1565
- ? INTRO_WORDMARK
1566
- : ["CODEXA"];
1567
- const logoWidth = logoRows.reduce((maxWidth, line) => Math.max(maxWidth, getTextWidth(line)), 0);
1563
+ ? selectLogoVariant(safeWidth)
1564
+ : selectLogoVariant(0); // text-only fallback for compact mode
1565
+ const effectiveLogoRows = logoRows.length > 0 ? logoRows : ["CODEXA"];
1566
+ const logoWidth = effectiveLogoRows.reduce((maxWidth, line) => Math.max(maxWidth, getTextWidth(line)), 0);
1568
1567
  const workspaceName = getWorkspaceDisplayName(intro.workspaceLabel);
1569
1568
  const metaLines = [
1570
1569
  `Codexa v${intro.version}`,
@@ -1577,19 +1576,25 @@ export function buildIntroRows(item: Extract<RenderTimelineItem, { type: "intro"
1577
1576
  && safeWidth >= logoWidth + gapWidth + widestMetaLine;
1578
1577
 
1579
1578
  if (canRenderSideBySide) {
1580
- const metaStartRow = Math.max(0, Math.floor((logoRows.length - metaLines.length) / 2));
1581
- const rowCount = Math.max(logoRows.length, metaStartRow + metaLines.length);
1579
+ const metaStartRow = Math.max(0, Math.floor((effectiveLogoRows.length - metaLines.length) / 2));
1580
+ const rowCount = Math.max(effectiveLogoRows.length, metaStartRow + metaLines.length);
1582
1581
  const metaWidth = Math.max(1, safeWidth - logoWidth - gapWidth);
1583
1582
 
1584
1583
  for (let rowIndex = 0; rowIndex < rowCount; rowIndex += 1) {
1585
- const logoLine = logoRows[rowIndex] ?? "";
1584
+ const logoLine = effectiveLogoRows[rowIndex] ?? "";
1586
1585
  const logoPadding = Math.max(0, logoWidth - getTextWidth(logoLine));
1587
1586
  const metaIndex = rowIndex - metaStartRow;
1588
1587
  const metaLine = metaIndex >= 0 && metaIndex < metaLines.length
1589
1588
  ? sanitizeTerminalOutput(metaLines[metaIndex]!)
1590
1589
  : "";
1590
+ let logoTone: TimelineTone = "logoPrimary";
1591
+ if (effectiveLogoRows.length === 6) {
1592
+ if (rowIndex === 2 || rowIndex === 3) logoTone = "logoSecondary";
1593
+ else if (rowIndex === 4 || rowIndex === 5) logoTone = "logoShadow";
1594
+ }
1595
+ // No bold on logo spans — bold on block/box-drawing chars causes spacing artifacts.
1591
1596
  const spans = [
1592
- createSpan(`${logoLine}${" ".repeat(logoPadding)}`, "accent", { bold: true }),
1597
+ createSpan(`${logoLine}${" ".repeat(logoPadding)}`, logoTone),
1593
1598
  createSpan(" ".repeat(gapWidth)),
1594
1599
  ];
1595
1600
 
@@ -1604,10 +1609,15 @@ export function buildIntroRows(item: Extract<RenderTimelineItem, { type: "intro"
1604
1609
  ));
1605
1610
  }
1606
1611
  } else {
1607
- logoRows.forEach((line, index) => {
1612
+ effectiveLogoRows.forEach((line, index) => {
1613
+ let logoTone: TimelineTone = "logoPrimary";
1614
+ if (effectiveLogoRows.length === 6) {
1615
+ if (index === 2 || index === 3) logoTone = "logoSecondary";
1616
+ else if (index === 4 || index === 5) logoTone = "logoShadow";
1617
+ }
1608
1618
  rows.push(createRow(
1609
1619
  `${item.key}-logo-${index}`,
1610
- [createSpan(clampVisualText(line, safeWidth), "accent", { bold: true })],
1620
+ [createSpan(clampVisualText(line, safeWidth), logoTone)],
1611
1621
  safeWidth,
1612
1622
  ));
1613
1623
  });
@@ -2965,7 +2975,7 @@ export function buildTimelineSnapshot(
2965
2975
  let builtRows: TimelineRow[];
2966
2976
 
2967
2977
  if (item.type === "intro") {
2968
- const cacheKey = `i:${item.key}:${innerWidth}:${item.intro.version}:${item.intro.layoutMode}:${item.intro.startupHeaderMode ?? ""}:${item.intro.authLabel}:${item.intro.workspaceLabel}`;
2978
+ const cacheKey = `i:${item.key}:${innerWidth}:${item.intro.version}:${item.intro.layoutMode}:${item.intro.startupHeaderMode ?? ""}:${item.intro.authLabel}:${item.intro.workspaceLabel}:v${LOGO_LARGE_MIN_COLS}`;
2969
2979
  const cached = _staticRowCache.get(cacheKey);
2970
2980
  if (cached) {
2971
2981
  renderDebug.traceEvent("timeline", "rowGeneration", {
File without changes
@@ -1,466 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * audit-codexa-capabilities.mjs
5
- *
6
- * Lightweight Codexa capability audit checker.
7
- * Performs static analysis on source to report feature availability.
8
- *
9
- * Usage: node scripts/audit-codexa-capabilities.mjs
10
- */
11
-
12
- import { existsSync, readFileSync } from "fs";
13
- import { join, resolve } from "path";
14
- import { fileURLToPath } from "url";
15
-
16
- const __dirname = import.meta.dirname ??
17
- resolve(new URL(import.meta.url).pathname, "..", "..");
18
-
19
- const repoRoot = resolve(__dirname, "..");
20
-
21
- const checks = {
22
- entrypoint() {
23
- const path = join(repoRoot, "bin", "codexa.js");
24
- const exists = existsSync(path);
25
- return {
26
- pass: exists,
27
- evidence: exists ? [path] : [],
28
- reason: exists
29
- ? "Entry wrapper spawns Bun runtime to execute app"
30
- : "Entry wrapper not found"
31
- };
32
- },
33
-
34
- cliHelpVersion() {
35
- const launchArgsPath = join(repoRoot, "src", "config", "launchArgs.ts");
36
- const launcherPath = join(repoRoot, "bin", "codexa.js");
37
- if (!existsSync(launchArgsPath) || !existsSync(launcherPath)) {
38
- return { pass: false, evidence: [], reason: "launch arg parser or launcher not found" };
39
- }
40
-
41
- const launchArgsContent = readFileSync(launchArgsPath, "utf-8");
42
- const launcherContent = readFileSync(launcherPath, "utf-8");
43
- const combined = `${launchArgsContent}\n${launcherContent}`;
44
- const hasHelp = /--help/.test(combined) && /["']-h["']/.test(combined);
45
- const hasVersion = /--version/.test(combined) && /["']-v["']/.test(combined);
46
- const exitsBeforeInk = /process\.exit\(0\)/.test(launcherContent) && !/render\(/.test(launcherContent);
47
- const readsPackageVersion = /package\.json/.test(launcherContent) && /version/.test(launcherContent);
48
-
49
- return {
50
- pass: hasHelp && hasVersion && exitsBeforeInk && readsPackageVersion,
51
- evidence: [launchArgsPath, launcherPath],
52
- reason: hasHelp && hasVersion && exitsBeforeInk && readsPackageVersion
53
- ? "--help/-h and --version/-v exit from launcher before Ink and version reads package.json"
54
- : `Missing evidence: ${[
55
- hasHelp ? null : "help flags",
56
- hasVersion ? null : "version flags",
57
- exitsBeforeInk ? null : "early exit before Ink",
58
- readsPackageVersion ? null : "package.json version read",
59
- ].filter(Boolean).join(", ")}`
60
- };
61
- },
62
-
63
- initialPromptArgument() {
64
- const path = join(repoRoot, "src", "config", "launchArgs.ts");
65
- const appPath = join(repoRoot, "src", "app.tsx");
66
-
67
- if (!existsSync(path) || !existsSync(appPath)) {
68
- return { pass: false, evidence: [], reason: "Required files not found" };
69
- }
70
-
71
- const launchContent = readFileSync(path, "utf-8");
72
- const appContent = readFileSync(appPath, "utf-8");
73
-
74
- const hasExtraction = /initialPrompt/.test(launchContent) && /promptArgs/.test(launchContent);
75
- const hasUsage = /launchArgs\.initialPrompt/.test(appContent)
76
- && /initialPromptSubmittedRef/.test(appContent)
77
- && /startPromptRun\(initialPrompt,\s*initialPrompt\)/.test(appContent);
78
-
79
- return {
80
- pass: hasExtraction && hasUsage,
81
- evidence: [path, appPath],
82
- reason: hasExtraction && hasUsage
83
- ? "Initial prompt support present"
84
- : "Passthrough args stored but not used for initial prompt"
85
- };
86
- },
87
-
88
- interactiveMode() {
89
- const indexPath = join(repoRoot, "src", "index.tsx");
90
- if (!existsSync(indexPath)) {
91
- return { pass: false, evidence: [], reason: "src/index.tsx not found" };
92
- }
93
-
94
- const content = readFileSync(indexPath, "utf-8");
95
- const hasTTY = /isTTY|TTY|isatty/.test(content);
96
- const hasInk = /render\(|<App/.test(content);
97
-
98
- return {
99
- pass: hasTTY && hasInk,
100
- evidence: [indexPath],
101
- reason: hasTTY && hasInk
102
- ? "Interactive mode with TTY detection and Ink UI present"
103
- : "TTY or UI setup missing"
104
- };
105
- },
106
-
107
- modelPicker() {
108
- const paths = [
109
- join(repoRoot, "src", "ui", "ModelPicker.tsx"),
110
- join(repoRoot, "src", "config", "settings.ts")
111
- ];
112
-
113
- const exist = paths.filter(p => existsSync(p));
114
- const hasModels = exist.some(p =>
115
- /AVAILABLE_MODELS/.test(readFileSync(p, "utf-8"))
116
- );
117
-
118
- return {
119
- pass: exist.length === 2 && hasModels,
120
- evidence: exist,
121
- reason: exist.length === 2 && hasModels
122
- ? "Model picker UI and model enumeration present"
123
- : `Missing components: ${exist.length}/2`
124
- };
125
- },
126
-
127
- configLoading() {
128
- const paths = [
129
- join(repoRoot, "src", "config", "layeredConfig.ts"),
130
- join(repoRoot, "src", "config", "persistence.ts"),
131
- join(repoRoot, "src", "config", "runtimeConfig.ts")
132
- ];
133
-
134
- const exist = paths.filter(p => existsSync(p));
135
- return {
136
- pass: exist.length === paths.length,
137
- evidence: exist,
138
- reason: exist.length === 3
139
- ? "Layered config resolution, persistence, and runtime config present"
140
- : `Found ${exist.length}/3 config modules`
141
- };
142
- },
143
-
144
- agentsmdSupport() {
145
- const loaderPath = join(repoRoot, "src", "core", "projectInstructions.ts");
146
- const appPath = join(repoRoot, "src", "app.tsx");
147
- const promptPath = join(repoRoot, "src", "core", "codexPrompt.ts");
148
- const providerPath = join(repoRoot, "src", "core", "providers", "codexSubprocess.ts");
149
-
150
- const paths = [loaderPath, appPath, promptPath, providerPath];
151
- const existing = paths.filter(p => existsSync(p));
152
- if (existing.length !== paths.length) {
153
- return {
154
- pass: false,
155
- evidence: existing,
156
- reason: `Found ${existing.length}/4 AGENTS.md support files`
157
- };
158
- }
159
-
160
- const loaderContent = readFileSync(loaderPath, "utf-8");
161
- const appContent = readFileSync(appPath, "utf-8");
162
- const promptContent = readFileSync(promptPath, "utf-8");
163
- const providerContent = readFileSync(providerPath, "utf-8");
164
- const discoversAgents = /AGENTS\.md/.test(loaderContent) && /\.codex/.test(loaderContent);
165
- const appLoadsAgents = /loadProjectInstructions/.test(appContent) && /projectInstructions/.test(appContent);
166
- const promptInjectsAgents = /Project instructions:/.test(promptContent) && /projectInstructions/.test(promptContent);
167
- const providerPassesAgents = /projectInstructions/.test(providerContent) && /buildCodexPrompt/.test(providerContent);
168
-
169
- return {
170
- pass: discoversAgents && appLoadsAgents && promptInjectsAgents && providerPassesAgents,
171
- evidence: paths,
172
- reason: discoversAgents && appLoadsAgents && promptInjectsAgents && providerPassesAgents
173
- ? "AGENTS.md/.codex/AGENTS.md discovery, app loading, and prompt injection present"
174
- : `Missing evidence: ${[
175
- discoversAgents ? null : "discovery",
176
- appLoadsAgents ? null : "app loading",
177
- promptInjectsAgents ? null : "prompt injection",
178
- providerPassesAgents ? null : "provider pass-through",
179
- ].filter(Boolean).join(", ")}`
180
- };
181
- },
182
-
183
- commandExecution() {
184
- const path = join(repoRoot, "src", "core", "process", "CommandRunner.ts");
185
- if (!existsSync(path)) {
186
- return { pass: false, evidence: [], reason: "CommandRunner.ts not found" };
187
- }
188
-
189
- const content = readFileSync(path, "utf-8");
190
- const hasRun = /runCommand|spawn/.test(content);
191
- const hasOutput = /stdout|stderr/.test(content);
192
-
193
- return {
194
- pass: hasRun && hasOutput,
195
- evidence: [path],
196
- reason: hasRun && hasOutput
197
- ? "Shell command execution with output capture present"
198
- : "Command execution incomplete"
199
- };
200
- },
201
-
202
- fileEditingLayer() {
203
- const paths = [
204
- join(repoRoot, "src", "core", "workspaceActivity.ts"),
205
- join(repoRoot, "src", "core", "workspaceGuard.ts")
206
- ];
207
-
208
- const exist = paths.filter(p => existsSync(p));
209
- const content = exist.map(p => readFileSync(p, "utf-8")).join("");
210
- const hasTracking = /createWorkspaceActivityTracker|modified|created|deleted/.test(content);
211
- const hasGuard = /isPathInsideWorkspace|workspaceGuard/.test(content);
212
-
213
- return {
214
- pass: exist.length === 2 && hasTracking && hasGuard,
215
- evidence: exist,
216
- reason: hasTracking && hasGuard
217
- ? "File activity tracking and workspace guard present"
218
- : exist.length < 2 ? "Activity or guard module missing" : "Tracking/guard logic incomplete"
219
- };
220
- },
221
-
222
- diffRenderer() {
223
- const rendererPath = join(repoRoot, "src", "ui", "diffRenderer.ts");
224
- const testPath = join(repoRoot, "src", "ui", "diffRenderer.test.ts");
225
- const markdownPath = join(repoRoot, "src", "ui", "Markdown.tsx");
226
- const timelinePath = join(repoRoot, "src", "ui", "timelineMeasure.ts");
227
- const paths = [rendererPath, testPath, markdownPath, timelinePath];
228
- const existing = paths.filter(p => existsSync(p));
229
-
230
- if (existing.length !== paths.length) {
231
- return {
232
- pass: false,
233
- evidence: existing,
234
- reason: `Found ${existing.length}/4 diff renderer files/integrations`
235
- };
236
- }
237
-
238
- const rendererContent = readFileSync(rendererPath, "utf-8");
239
- const testContent = readFileSync(testPath, "utf-8");
240
- const markdownContent = readFileSync(markdownPath, "utf-8");
241
- const timelineContent = readFileSync(timelinePath, "utf-8");
242
- const exportsUtility = /export\s+function\s+isUnifiedDiff/.test(rendererContent)
243
- && /export\s+function\s+renderUnifiedDiff/.test(rendererContent)
244
- && /export\s+function\s+maybeRenderDiff/.test(rendererContent)
245
- && /DiffRenderLine/.test(rendererContent);
246
- const detectsUnifiedDiff = /DIFF_GIT_HEADER_PATTERN/.test(rendererContent)
247
- && /HUNK_HEADER_PATTERN/.test(rendererContent)
248
- && /OLD_FILE_HEADER_PATTERN/.test(rendererContent)
249
- && /NEW_FILE_HEADER_PATTERN/.test(rendererContent);
250
- const hasFocusedTests = /isUnifiedDiff/.test(testContent)
251
- && /renderUnifiedDiff/.test(testContent)
252
- && /ANSI|control/i.test(testContent)
253
- && /normal text|normal prose/i.test(testContent);
254
- const markdownIntegrated = /diffRenderer/.test(markdownContent) && /maybeRenderDiff/.test(markdownContent);
255
- const timelineIntegrated = /diffRenderer/.test(timelineContent) && /maybeRenderDiff/.test(timelineContent);
256
-
257
- return {
258
- pass: exportsUtility && detectsUnifiedDiff && hasFocusedTests && markdownIntegrated && timelineIntegrated,
259
- evidence: paths,
260
- reason: exportsUtility && detectsUnifiedDiff && hasFocusedTests && markdownIntegrated && timelineIntegrated
261
- ? "Unified diff renderer utility, tests, and UI integrations present"
262
- : `Missing evidence: ${[
263
- exportsUtility ? null : "utility exports",
264
- detectsUnifiedDiff ? null : "unified diff detection",
265
- hasFocusedTests ? null : "focused tests",
266
- markdownIntegrated ? null : "Markdown integration",
267
- timelineIntegrated ? null : "timeline integration",
268
- ].filter(Boolean).join(", ")}`
269
- };
270
- },
271
-
272
- approvalSandboxLogic() {
273
- const paths = [
274
- join(repoRoot, "src", "core", "workspaceGuard.ts"),
275
- join(repoRoot, "src", "config", "runtimeConfig.ts")
276
- ];
277
-
278
- const exist = paths.filter(p => existsSync(p));
279
- const content = exist.map(p => readFileSync(p, "utf-8")).join("");
280
- const hasSandbox = /sandbox|approval|permission|writable.*root/.test(content);
281
-
282
- return {
283
- pass: exist.length === 2 && hasSandbox,
284
- evidence: exist,
285
- reason: hasSandbox
286
- ? "Sandbox configuration and approval logic present"
287
- : "Approval or sandbox logic missing"
288
- };
289
- },
290
-
291
- sessionHistoryPersistence() {
292
- const paths = [
293
- join(repoRoot, "src", "session", "types.ts"),
294
- join(repoRoot, "src", "session", "appSession.ts"),
295
- join(repoRoot, "src", "config", "persistence.ts")
296
- ];
297
-
298
- const exist = paths.filter(p => existsSync(p));
299
- return {
300
- pass: exist.length === 3,
301
- evidence: exist,
302
- reason: exist.length === 3
303
- ? "Session event types, state management, and persistence present"
304
- : `Found ${exist.length}/3 session modules`
305
- };
306
- },
307
-
308
- debugLogging() {
309
- const debugPath = join(repoRoot, "src", "core", "inputDebug.ts");
310
- const envPath = join(repoRoot, "bin", "codexa.js");
311
-
312
- const debugExists = existsSync(debugPath);
313
- const envContent = existsSync(envPath) ? readFileSync(envPath, "utf-8") : "";
314
- const hasEnvDebug = /CODEXA_DEBUG|debug/.test(envContent);
315
-
316
- return {
317
- pass: debugExists || hasEnvDebug,
318
- evidence: [debugExists ? debugPath : envPath].filter(p => existsSync(p)),
319
- reason: debugExists && hasEnvDebug
320
- ? "Debug logging module and environment variable support present"
321
- : debugExists || hasEnvDebug
322
- ? "Partial debug support"
323
- : "Debug logging not found"
324
- };
325
- },
326
-
327
- windowsPowerShellHandling() {
328
- const launcherPath = join(repoRoot, "bin", "codexa.js");
329
- if (!existsSync(launcherPath)) {
330
- return { pass: false, evidence: [], reason: "Launcher not found" };
331
- }
332
-
333
- const content = readFileSync(launcherPath, "utf-8");
334
- const hasWinDetect = /win32|platform|windows/i.test(content);
335
- const hasExeResolution = /bun\.exe|bun\.cmd|shell|spawn/.test(content);
336
-
337
- return {
338
- pass: hasWinDetect && hasExeResolution,
339
- evidence: [launcherPath],
340
- reason: hasWinDetect && hasExeResolution
341
- ? "Windows platform detection and executable resolution present"
342
- : "Windows-specific handling incomplete"
343
- };
344
- },
345
-
346
- resizeHandling() {
347
- const indexPath = join(repoRoot, "src", "index.tsx");
348
- if (!existsSync(indexPath)) {
349
- return { pass: false, evidence: [], reason: "src/index.tsx not found" };
350
- }
351
-
352
- const content = readFileSync(indexPath, "utf-8");
353
- const hasResize = /resize|onResize|RESIZE/.test(content);
354
- const hasRepaint = /repaint|recalculate|calculateLayout/.test(content);
355
-
356
- return {
357
- pass: hasResize && hasRepaint,
358
- evidence: [indexPath],
359
- reason: hasResize && hasRepaint
360
- ? "Terminal resize event handling and UI recalculation present"
361
- : "Resize handling incomplete"
362
- };
363
- },
364
-
365
- streamingHandler() {
366
- const paths = [
367
- join(repoRoot, "src", "core", "providers", "codexSubprocess.ts"),
368
- join(repoRoot, "src", "core", "codex.ts")
369
- ];
370
-
371
- const exist = paths.filter(p => existsSync(p));
372
- const content = exist.map(p => readFileSync(p, "utf-8")).join("");
373
- const hasStreaming = /stream|emit|chunk|line|onLine/.test(content);
374
-
375
- return {
376
- pass: exist.length > 0 && hasStreaming,
377
- evidence: exist,
378
- reason: hasStreaming
379
- ? "Streaming response handler present"
380
- : "Streaming implementation not found"
381
- };
382
- },
383
-
384
- interruptCancelHandling() {
385
- const indexPath = join(repoRoot, "src", "index.tsx");
386
- const appPath = join(repoRoot, "src", "app.tsx");
387
-
388
- const paths = [indexPath, appPath].filter(p => existsSync(p));
389
- const content = paths.map(p => readFileSync(p, "utf-8")).join("");
390
- const hasSignals = /SIGINT|SIGTERM|signal|cancel/.test(content);
391
- const hasCancel = /cancel|abort|kill/.test(content);
392
-
393
- return {
394
- pass: hasSignals && hasCancel,
395
- evidence: paths,
396
- reason: hasSignals && hasCancel
397
- ? "Signal handling and cancellation logic present"
398
- : "Interrupt/cancel handling incomplete"
399
- };
400
- }
401
- };
402
-
403
- function statusSymbol(pass) {
404
- return pass ? "✓" : "✗";
405
- }
406
-
407
- function statusLabel(pass) {
408
- return pass ? "PASS" : "MISSING";
409
- }
410
-
411
- function formatName(name) {
412
- return name
413
- .replace(/([A-Z])/g, " $1")
414
- .replace(/^./, str => str.toUpperCase());
415
- }
416
-
417
- console.log("\n" + "=".repeat(80));
418
- console.log("CODEXA CAPABILITY AUDIT REPORT");
419
- console.log("=".repeat(80));
420
- console.log(`Repository: ${repoRoot}\n`);
421
-
422
- const results = [];
423
-
424
- for (const [name, checkFn] of Object.entries(checks)) {
425
- const result = checkFn();
426
- results.push({ name, ...result });
427
-
428
- const symbol = statusSymbol(result.pass);
429
- const status = statusLabel(result.pass);
430
- const formattedName = formatName(name);
431
-
432
- console.log(`${symbol} ${status.padEnd(8)} | ${formattedName}`);
433
- console.log(` ${result.reason}`);
434
- if (result.evidence.length > 0) {
435
- console.log(` Evidence: ${result.evidence.map(e => e.replace(repoRoot, ".")).join(", ")}`);
436
- }
437
- console.log();
438
- }
439
-
440
- const total = results.length;
441
- const passed = results.filter(r => r.pass).length;
442
- const missing = total - passed;
443
- const pctComplete = Math.round((passed / total) * 100);
444
-
445
- console.log("=".repeat(80));
446
- console.log("CAPABILITY SUMMARY");
447
- console.log("=".repeat(80));
448
- console.log(`Total checks: ${total}`);
449
- console.log(`Passed: ${passed} (${pctComplete}%)`);
450
- console.log(`Missing/Partial: ${missing} (${100 - pctComplete}%)`);
451
- console.log();
452
-
453
- const missingFeatures = results.filter(r => !r.pass).map(r => formatName(r.name));
454
-
455
- if (missingFeatures.length > 0) {
456
- console.log("TOP MISSING FEATURES:");
457
- missingFeatures.forEach((f, i) => {
458
- console.log(` ${i + 1}. ${f}`);
459
- });
460
- console.log();
461
- }
462
-
463
- console.log("=".repeat(80));
464
- console.log();
465
-
466
- process.exit(passed === total ? 0 : 1);
@@ -1,35 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { spawn } from "node:child_process";
4
- import { dirname, join } from "node:path";
5
- import { fileURLToPath } from "node:url";
6
-
7
- const scriptDir = dirname(fileURLToPath(import.meta.url));
8
- const repoRoot = dirname(scriptDir);
9
- const binPath = join(repoRoot, "bin", "codexa.js");
10
- const prompt = "Print the current directory, list files, and stop.";
11
-
12
- const child = spawn(
13
- process.execPath,
14
- [binPath, "exec", prompt],
15
- {
16
- cwd: process.cwd(),
17
- stdio: "inherit",
18
- env: {
19
- ...process.env,
20
- },
21
- },
22
- );
23
-
24
- child.on("error", (error) => {
25
- console.error(`[smoke-terminal-bench] failed to launch: ${error.message}`);
26
- process.exit(1);
27
- });
28
-
29
- child.on("close", (code, signal) => {
30
- if (signal) {
31
- console.error(`[smoke-terminal-bench] terminated by ${signal}`);
32
- process.exit(1);
33
- }
34
- process.exit(code ?? 0);
35
- });