@golba98/codexa 1.0.2 → 1.0.4

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 (206) hide show
  1. package/README.md +396 -100
  2. package/bin/codexa.js +62 -144
  3. package/package.json +14 -8
  4. package/src/app.tsx +596 -306
  5. package/src/commands/handler.ts +6 -6
  6. package/src/config/buildInfo.ts +2 -2
  7. package/src/config/layeredConfig.ts +1 -1
  8. package/src/config/persistence.ts +10 -0
  9. package/src/config/runtimeConfig.ts +1 -1
  10. package/src/config/settings.ts +8 -16
  11. package/src/config/trustStore.ts +1 -1
  12. package/src/config/updateCheckCache.ts +19 -1
  13. package/src/core/README.md +52 -0
  14. package/src/core/agent/loop.ts +282 -0
  15. package/src/core/agent/protocol.ts +211 -0
  16. package/src/core/agent/tools.ts +414 -0
  17. package/src/core/{codexExecArgs.ts → codex/codexExecArgs.ts} +2 -2
  18. package/src/core/{codexLaunch.ts → codex/codexLaunch.ts} +3 -3
  19. package/src/core/{codexPrompt.ts → codex/codexPrompt.ts} +3 -3
  20. package/src/core/debug/modelStateDebug.ts +34 -0
  21. package/src/core/executables/antigravityExecutable.ts +48 -0
  22. package/src/core/executables/codexExecutable.ts +1 -0
  23. package/src/core/executables/executableResolver.ts +65 -43
  24. package/src/core/perf/renderDebug.ts +10 -6
  25. package/src/core/process/processValidation.ts +9 -5
  26. package/src/core/providerLauncher/launcher.ts +59 -42
  27. package/src/core/providerLauncher/registry.ts +30 -14
  28. package/src/core/providerLauncher/types.ts +11 -9
  29. package/src/core/providerLauncher/workspaceConfig.ts +41 -26
  30. package/src/core/providerRuntime/anthropic.ts +7 -1
  31. package/src/core/providerRuntime/antigravity.ts +305 -0
  32. package/src/core/providerRuntime/claudeCodeDiscovery.ts +268 -22
  33. package/src/core/providerRuntime/claudeCodeDiscoveryDebug.ts +55 -0
  34. package/src/core/providerRuntime/contextMetadata.ts +12 -24
  35. package/src/core/providerRuntime/local.ts +129 -51
  36. package/src/core/providerRuntime/models.ts +22 -11
  37. package/src/core/providerRuntime/registry.ts +58 -31
  38. package/src/core/providerRuntime/types.ts +19 -14
  39. package/src/core/providers/codexSubprocess.ts +2 -2
  40. package/src/core/providers/types.ts +1 -1
  41. package/src/core/{attachments.ts → shared/attachments.ts} +27 -4
  42. package/src/core/{cleanupFastFail.ts → shared/cleanupFastFail.ts} +1 -1
  43. package/src/core/{hollowResponseFormat.ts → shared/hollowResponseFormat.ts} +1 -1
  44. package/src/core/terminal/clearFrameBoundary.ts +814 -0
  45. package/src/core/terminal/frameLock.ts +109 -0
  46. package/src/core/terminal/inkRenderReset.ts +123 -0
  47. package/src/core/terminal/terminalControl.ts +22 -0
  48. package/src/core/terminal/terminalTitle.ts +16 -102
  49. package/src/core/version/channel.ts +23 -0
  50. package/src/core/version/updateCheck.ts +193 -0
  51. package/src/core/{launchContext.ts → workspace/launchContext.ts} +32 -39
  52. package/src/core/{planStorage.ts → workspace/planStorage.ts} +2 -2
  53. package/src/core/{workspaceGuard.ts → workspace/workspaceGuard.ts} +97 -13
  54. package/src/headless/execRunner.ts +2 -2
  55. package/src/index.tsx +43 -89
  56. package/src/session/appSession.ts +10 -7
  57. package/src/session/chatLifecycle.ts +1 -1
  58. package/src/session/liveRenderScheduler.ts +1 -1
  59. package/src/session/types.ts +3 -2
  60. package/src/ui/ActionRequiredBlock.tsx +5 -5
  61. package/src/ui/ActivityBars.tsx +3 -3
  62. package/src/ui/ActivityIndicator.tsx +6 -6
  63. package/src/ui/AgentBlock.tsx +6 -6
  64. package/src/ui/AnimatedStatusText.tsx +1 -1
  65. package/src/ui/AppShell.tsx +670 -719
  66. package/src/ui/AttachmentImportPanel.tsx +8 -8
  67. package/src/ui/AuthPanel.tsx +20 -20
  68. package/src/ui/BottomComposer.tsx +158 -118
  69. package/src/ui/DashCard.tsx +3 -3
  70. package/src/ui/Markdown.tsx +17 -17
  71. package/src/ui/ModelPickerScreen.tsx +222 -42
  72. package/src/ui/ModelReasoningPicker.tsx +15 -15
  73. package/src/ui/Panel.tsx +3 -3
  74. package/src/ui/PlanActionPicker.tsx +6 -6
  75. package/src/ui/PlanReviewPanel.tsx +9 -9
  76. package/src/ui/ProviderPicker.tsx +735 -321
  77. package/src/ui/RunFooter.tsx +3 -3
  78. package/src/ui/RuntimeStatusBar.tsx +108 -0
  79. package/src/ui/SelectionPanel.tsx +8 -4
  80. package/src/ui/SettingsPanel.tsx +9 -9
  81. package/src/ui/Spinner.tsx +1 -1
  82. package/src/ui/TextEntryPanel.tsx +11 -11
  83. package/src/ui/ThinkingBlock.tsx +8 -8
  84. package/src/ui/Timeline.tsx +1625 -1472
  85. package/src/ui/TopHeader.tsx +437 -293
  86. package/src/ui/TranscriptShell.tsx +322 -0
  87. package/src/ui/TurnGroup.tsx +33 -33
  88. package/src/ui/UpdateAvailableCard.tsx +41 -0
  89. package/src/ui/UpdatePromptPanel.tsx +197 -0
  90. package/src/ui/focus.ts +3 -0
  91. package/src/ui/layout.ts +299 -25
  92. package/src/ui/layoutListWindow.ts +145 -0
  93. package/src/ui/logoVariants.ts +103 -0
  94. package/src/ui/modeDisplay.ts +12 -12
  95. package/src/ui/runtimeDisplay.ts +112 -0
  96. package/src/ui/textLayout.ts +15 -4
  97. package/src/ui/theme.tsx +274 -395
  98. package/src/ui/timelineMeasure.ts +218 -136
  99. package/scripts/audit-codexa-capabilities.mjs +0 -466
  100. package/scripts/gen-build-info.mjs +0 -33
  101. package/scripts/smoke-terminal-bench.mjs +0 -35
  102. package/src/appRenderStability.test.ts +0 -131
  103. package/src/commands/handler.test.ts +0 -655
  104. package/src/config/launchArgs.test.ts +0 -189
  105. package/src/config/layeredConfig.test.ts +0 -143
  106. package/src/config/persistence.test.ts +0 -114
  107. package/src/config/runtimeConfig.test.ts +0 -218
  108. package/src/config/settings.test.ts +0 -155
  109. package/src/config/trustStore.test.ts +0 -29
  110. package/src/core/attachments.test.ts +0 -155
  111. package/src/core/auth/codexAuth.test.ts +0 -68
  112. package/src/core/cleanupFastFail.test.ts +0 -76
  113. package/src/core/codex.ts +0 -124
  114. package/src/core/codexExecArgs.test.ts +0 -195
  115. package/src/core/codexLaunch.test.ts +0 -205
  116. package/src/core/codexPrompt.test.ts +0 -252
  117. package/src/core/executables/codexExecutable.test.ts +0 -212
  118. package/src/core/executables/executableResolver.test.ts +0 -129
  119. package/src/core/executables/geminiExecutable.test.ts +0 -116
  120. package/src/core/executables/pathSanityScan.test.ts +0 -47
  121. package/src/core/githubDiagnostics.test.ts +0 -92
  122. package/src/core/hollowResponseFormat.test.ts +0 -58
  123. package/src/core/launchContext.test.ts +0 -157
  124. package/src/core/models/codexCapabilities.test.ts +0 -45
  125. package/src/core/models/codexModelCapabilities.test.ts +0 -246
  126. package/src/core/models/modelSpecs.test.ts +0 -283
  127. package/src/core/perf/renderDebug.test.ts +0 -230
  128. package/src/core/planStorage.test.ts +0 -143
  129. package/src/core/process/CommandRunner.test.ts +0 -105
  130. package/src/core/projectInstructions.test.ts +0 -50
  131. package/src/core/providerLauncher/launcher.test.ts +0 -238
  132. package/src/core/providerLauncher/registry.test.ts +0 -324
  133. package/src/core/providerLauncher/workspaceConfig.test.ts +0 -638
  134. package/src/core/providerRuntime/anthropic.test.ts +0 -1120
  135. package/src/core/providerRuntime/capabilityProfile.test.ts +0 -311
  136. package/src/core/providerRuntime/contextMetadata.test.ts +0 -468
  137. package/src/core/providerRuntime/gemini.test.ts +0 -437
  138. package/src/core/providerRuntime/lmstudio.test.ts +0 -168
  139. package/src/core/providerRuntime/local.test.ts +0 -787
  140. package/src/core/providerRuntime/registry.test.ts +0 -233
  141. package/src/core/providers/codexJsonStream.test.ts +0 -148
  142. package/src/core/providers/codexSubprocess.test.ts +0 -68
  143. package/src/core/providers/codexTranscript.test.ts +0 -284
  144. package/src/core/terminal/startupClear.test.ts +0 -55
  145. package/src/core/terminal/terminalCapabilities.test.ts +0 -93
  146. package/src/core/terminal/terminalControl.test.ts +0 -75
  147. package/src/core/terminal/terminalSanitize.test.ts +0 -22
  148. package/src/core/terminal/terminalSelection.test.ts +0 -42
  149. package/src/core/terminal/terminalTitle.test.ts +0 -328
  150. package/src/core/updateCheck.test.ts +0 -194
  151. package/src/core/updateCheck.ts +0 -172
  152. package/src/core/workspaceActivity.test.ts +0 -163
  153. package/src/core/workspaceGuard.test.ts +0 -151
  154. package/src/core/workspaceRoot.test.ts +0 -23
  155. package/src/exec.test.ts +0 -13
  156. package/src/headless/execArgs.test.ts +0 -147
  157. package/src/headless/execRunner.test.ts +0 -436
  158. package/src/index.test.tsx +0 -620
  159. package/src/session/appSession.test.ts +0 -897
  160. package/src/session/chatLifecycle.test.ts +0 -64
  161. package/src/session/liveRenderScheduler.test.ts +0 -201
  162. package/src/session/planFlow.test.ts +0 -103
  163. package/src/session/planTranscript.test.ts +0 -65
  164. package/src/session/promptRunSchedule.test.ts +0 -36
  165. package/src/ui/ActivityIndicator.test.tsx +0 -58
  166. package/src/ui/AgentBlock.test.ts +0 -6
  167. package/src/ui/AnimatedStatusText.test.ts +0 -16
  168. package/src/ui/AppShell.test.tsx +0 -1776
  169. package/src/ui/AttachmentImportPanel.test.tsx +0 -204
  170. package/src/ui/BottomComposer.test.ts +0 -674
  171. package/src/ui/CodexLogo.tsx +0 -55
  172. package/src/ui/Markdown.test.ts +0 -157
  173. package/src/ui/ModelPickerProviderScope.test.tsx +0 -411
  174. package/src/ui/ModelPickerScreen.test.tsx +0 -99
  175. package/src/ui/ModelPickerState.test.tsx +0 -151
  176. package/src/ui/ModelReasoningPicker.test.tsx +0 -447
  177. package/src/ui/PlanReviewPanel.test.tsx +0 -267
  178. package/src/ui/PromptCardBorder.test.tsx +0 -161
  179. package/src/ui/ProviderPicker.test.tsx +0 -289
  180. package/src/ui/ProviderShortcut.test.tsx +0 -143
  181. package/src/ui/SettingsPanel.test.tsx +0 -233
  182. package/src/ui/StaticTranscriptItem.tsx +0 -56
  183. package/src/ui/Timeline.test.ts +0 -2067
  184. package/src/ui/TimelineNavigation.test.tsx +0 -201
  185. package/src/ui/TopHeader.test.tsx +0 -254
  186. package/src/ui/TurnGroup.test.tsx +0 -365
  187. package/src/ui/busyStatusAnimation.test.ts +0 -30
  188. package/src/ui/commandNormalize.test.ts +0 -142
  189. package/src/ui/diffRenderer.test.ts +0 -102
  190. package/src/ui/focusFlow.test.tsx +0 -1098
  191. package/src/ui/inputBuffer.test.ts +0 -151
  192. package/src/ui/layout.test.ts +0 -146
  193. package/src/ui/modeDisplay.test.ts +0 -42
  194. package/src/ui/runActivityView.test.ts +0 -89
  195. package/src/ui/runLifecycleView.test.tsx +0 -237
  196. package/src/ui/statusRenderIsolation.test.tsx +0 -654
  197. package/src/ui/terminalAnswerFormat.test.ts +0 -19
  198. package/src/ui/textLayout.test.ts +0 -18
  199. package/src/ui/themeFlow.test.ts +0 -53
  200. package/src/ui/timelineMeasureCache.test.ts +0 -986
  201. /package/src/core/{inputDebug.ts → debug/inputDebug.ts} +0 -0
  202. /package/src/core/{clipboard.ts → shared/clipboard.ts} +0 -0
  203. /package/src/core/{githubDiagnostics.ts → shared/githubDiagnostics.ts} +0 -0
  204. /package/src/core/{projectInstructions.ts → workspace/projectInstructions.ts} +0 -0
  205. /package/src/core/{workspaceActivity.ts → workspace/workspaceActivity.ts} +0 -0
  206. /package/src/core/{workspaceRoot.ts → workspace/workspaceRoot.ts} +0 -0
@@ -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,33 +0,0 @@
1
- #!/usr/bin/env node
2
- // Writes src/config/buildInfo.ts with package/build metadata.
3
- // Used by `bun run build` so the embed is up-to-date at install time.
4
-
5
- import { execSync } from "node:child_process";
6
- import { readFileSync, writeFileSync } from "node:fs";
7
- import { resolve, dirname } from "node:path";
8
- import { fileURLToPath } from "node:url";
9
-
10
- const __dirname = dirname(fileURLToPath(import.meta.url));
11
- const outFile = resolve(__dirname, "..", "src", "config", "buildInfo.ts");
12
- const packageFile = resolve(__dirname, "..", "package.json");
13
-
14
- let commit = "unknown";
15
- try {
16
- commit = execSync("git rev-parse HEAD", { encoding: "utf-8", timeout: 5000 }).trim();
17
- } catch {
18
- // Not a git repo or git unavailable — keep default.
19
- }
20
-
21
- let version = "0.0.0";
22
- try {
23
- const pkg = JSON.parse(readFileSync(packageFile, "utf-8"));
24
- if (typeof pkg.version === "string" && pkg.version.trim()) {
25
- version = pkg.version.trim();
26
- }
27
- } catch {
28
- // Keep default.
29
- }
30
-
31
- const content = `// Generated by scripts/gen-build-info.mjs — do not edit manually.\nexport const BUILD_COMMIT = ${JSON.stringify(commit)} as const;\nexport const BUILD_VERSION = ${JSON.stringify(version)} as const;\n`;
32
- writeFileSync(outFile, content, "utf-8");
33
- console.log(`[gen-build-info] BUILD_COMMIT = ${commit.slice(0, 8)} BUILD_VERSION = ${version}`);
@@ -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
- });
@@ -1,131 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { readFileSync } from "node:fs";
3
- import { dirname, join } from "node:path";
4
- import test from "node:test";
5
- import { fileURLToPath } from "node:url";
6
-
7
- const appSource = readFileSync(join(dirname(fileURLToPath(import.meta.url)), "app.tsx"), "utf8");
8
- const appShellSource = readFileSync(join(dirname(fileURLToPath(import.meta.url)), "ui", "AppShell.tsx"), "utf8");
9
- const composerSource = readFileSync(join(dirname(fileURLToPath(import.meta.url)), "ui", "BottomComposer.tsx"), "utf8");
10
- const launcherSource = readFileSync(join(dirname(fileURLToPath(import.meta.url)), "..", "bin", "codexa.js"), "utf8");
11
-
12
- test("App starts a terminal title guard during busy rendering", () => {
13
- assert.match(appSource, /refreshTerminalTitle\(\{[\s\S]*?debugEventName: "busy-guard"/);
14
- });
15
-
16
- test("App does not write terminal title OSC sequences while Ink is active", () => {
17
- assert.doesNotMatch(appSource, /\\x1b\]0;CODEXA/);
18
- assert.doesNotMatch(appSource, /\\x1b\]2;CODEXA/);
19
- });
20
-
21
- test("App root does not own the busy status animation frame", () => {
22
- assert.doesNotMatch(appSource, /busyStatusFrame/);
23
- assert.doesNotMatch(appSource, /useBusyStatusFrame/);
24
- assert.doesNotMatch(appSource, /BUSY_STATUS_FRAME_MS/);
25
- assert.doesNotMatch(composerSource, /busyStatusFrame/);
26
- });
27
-
28
- test("App mouse capture follows terminalMouseMode setting, defaults to off (selection mode)", () => {
29
- // mouseCapture is driven by the persisted terminalMouseMode. Default is "selection" so
30
- // mouseCapture=false by default — no SGR tracking. "wheel" mode enables SGR capture.
31
- assert.match(appSource, /const mouseCapture = \(mouseOverride \?\? \(terminalMouseMode === "wheel"\)\) && !isMouseIdle/);
32
- assert.doesNotMatch(appSource, /mouseOverride \?\? false/);
33
- });
34
-
35
- test("Workspace display changes do not force AppShell remounts or viewport clears", () => {
36
- assert.doesNotMatch(appSource, /workspaceLabelEpoch/);
37
- assert.doesNotMatch(appSource, /workspaceDisplayChange/);
38
- assert.doesNotMatch(appSource, /key=\{`app-shell-\$\{sessionState\.clearCount\}-/);
39
- });
40
-
41
- test("AppShell renders the header as live layout instead of static transcript output", () => {
42
- assert.match(appShellSource, /MemoizedTopHeader/);
43
- assert.doesNotMatch(appShellSource, /import \{[^}]*Static[^}]*\} from "ink"/);
44
- assert.doesNotMatch(appShellSource, /<Static\b/);
45
- assert.doesNotMatch(appShellSource, /StaticIntroItem/);
46
- assert.doesNotMatch(appShellSource, /session-intro/);
47
- });
48
-
49
- test("Settings panel workspace display save path does not append Settings transcript events", () => {
50
- const match = appSource.match(/const saveSettingsFromPanel = useCallback\(\(nextSettings: UserSettingValues\) => \{([\s\S]*?)\n \}, \[/);
51
- assert.ok(match, "saveSettingsFromPanel callback should exist");
52
- assert.doesNotMatch(match[1] ?? "", /appendSystemEvent\("Settings"/);
53
- });
54
-
55
- test("Settings panel terminal title save path still updates terminal title state", () => {
56
- assert.match(appSource, /if \(nextSettings\.terminalTitleMode !== terminalTitleMode\) \{\s*setTerminalTitleMode\(nextSettings\.terminalTitleMode\);/);
57
- assert.match(appSource, /setIntendedTerminalTitle\(terminalTitleLabel/);
58
- });
59
-
60
- test("Terminal title re-assertion effect is keyed by uiState and busy to recover from external overwrites", () => {
61
- const match = appSource.match(/useEffect\(\(\) => \{[\s\S]*?debugEventName: "busy-start"[\s\S]*?\}, \[([^\]]+)\]\);/);
62
- assert.ok(match, "terminal title re-assertion effect should exist");
63
- const deps = match[1] ?? "";
64
- assert.match(deps, /uiState\.kind/);
65
- assert.match(deps, /busy/);
66
- assert.match(deps, /terminalTitleMode/);
67
- assert.match(deps, /workspaceRoot/);
68
- });
69
-
70
- test("Terminal title update effect is keyed by terminal title label", () => {
71
- const match = appSource.match(/useEffect\(\(\) => \{\s*setIntendedTerminalTitle\(terminalTitleLabel[\s\S]*?\);\s*\}, \[([^\]]+)\]\);/);
72
- assert.ok(match, "terminal title update effect should exist");
73
- const deps = match[1] ?? "";
74
- assert.match(deps, /terminalTitleLabel/);
75
- });
76
-
77
- test("Prompt and shell busy paths force title before busy state dispatch", () => {
78
- const promptStart = appSource.indexOf('writeCurrentTerminalTitleBeforeStateChange("before-prompt-run-start")');
79
- const promptBusy = appSource.indexOf('type: "SUBMIT_PROMPT_RUN"');
80
- const shellStart = appSource.indexOf('writeCurrentTerminalTitleBeforeStateChange("before-shell-start")');
81
- const shellBusy = appSource.indexOf('dispatchSession({ type: "UI_ACTION", action: { type: "SHELL_STARTED"');
82
-
83
- assert.ok(promptStart >= 0, "prompt path should force title before busy");
84
- assert.ok(shellStart >= 0, "shell path should force title before busy");
85
- assert.ok(promptStart < promptBusy, "prompt title write must happen before busy dispatch");
86
- assert.ok(shellStart < shellBusy, "shell title write must happen before busy dispatch");
87
- });
88
-
89
- test("Terminal title cold-start sequence fires immediately on mount and retries at 50ms and 250ms", () => {
90
- assert.match(appSource, /beginColdStartSequence/);
91
- assert.doesNotMatch(appSource, /postMountTerminalTitleRefreshRef/);
92
- assert.doesNotMatch(appSource, /retryDelaysMs/);
93
- });
94
-
95
- test("Terminal title writes are centralized through the title helpers", () => {
96
- assert.doesNotMatch(appSource, /reassertTerminalTitle/);
97
- assert.match(appSource, /setIntendedTerminalTitle/);
98
- assert.match(appSource, /reassertIntendedTerminalTitle/);
99
- assert.match(appSource, /deriveTerminalTitle\(workspaceRoot, terminalTitleMode\)/);
100
- });
101
-
102
- test("App reasserts intended terminal title around child process lifecycle events", () => {
103
- assert.match(appSource, /onProcessLifecycle: \(event\) => \{\s*reassertIntendedTerminalTitle\(\{\s*reason: `codex-process-\$\{event\}`/);
104
- assert.match(appSource, /onProcessLifecycle: \(event\) => \{\s*reassertIntendedTerminalTitle\(\{\s*reason: `shell-process-\$\{event\}`/);
105
- });
106
-
107
- test("Installed launcher preserves inherited stdio for interactive TTY launches", () => {
108
- assert.match(launcherSource, /const parentHasTTY = parentStdinIsTTY && parentStdoutIsTTY/);
109
- assert.match(launcherSource, /if \(!isHeadlessMode && parentHasTTY\)/);
110
- assert.match(launcherSource, /parentHasTTY\s*\?\s*\["inherit", "inherit", "inherit"\]/);
111
- assert.match(launcherSource, /CODEXA_DEBUG_LAUNCH/);
112
- });
113
-
114
- test("Installed launcher asserts a safe title before Bun lifecycle boundaries", () => {
115
- const launchStartIndex = launcherSource.indexOf('markExecTiming("launcher_start"');
116
- const startupTitleIndex = launcherSource.indexOf('writeIntendedTitle("launcher-startup-title")');
117
- const helpIndex = launcherSource.indexOf('hasFlag(forwardArgs, "--help", "-h")');
118
- const beforeSpawnIndex = launcherSource.indexOf('writeIntendedTitle("before-bun-spawn")');
119
- const spawnIndex = launcherSource.indexOf("const child = spawn(");
120
-
121
- assert.ok(launchStartIndex >= 0);
122
- assert.ok(startupTitleIndex > launchStartIndex);
123
- assert.ok(startupTitleIndex < helpIndex, "launcher title should be asserted before help/version parsing can exit");
124
- assert.ok(beforeSpawnIndex >= 0 && spawnIndex > beforeSpawnIndex, "launcher title should be asserted before Bun spawn");
125
- assert.match(launcherSource, /CODEXA_INITIAL_TERMINAL_TITLE: intendedTerminalTitle/);
126
- assert.match(launcherSource, /child\.once\("spawn"[\s\S]*writeIntendedTitle\("after-bun-spawn"\)/);
127
- assert.match(launcherSource, /child\.on\("error"[\s\S]*writeIntendedTitle\("bun-spawn-error"\)/);
128
- assert.match(launcherSource, /child\.on\("close"[\s\S]*writeIntendedTitle\("bun-close"\)/);
129
- assert.match(launcherSource, /\/\^\[a-zA-Z\]:\[\\\\\/\]\//);
130
- assert.doesNotMatch(launcherSource, /intendedTerminalTitle\s*=\s*workspaceRoot/);
131
- });