ubume 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/README.md +88 -0
  2. package/bin/codexa-local-harness-bridge.js +3 -0
  3. package/bin/codexa.js +28 -0
  4. package/bin/ubume-local-harness-bridge.js +325 -0
  5. package/bin/ubume.js +398 -0
  6. package/package.json +66 -0
  7. package/src/app.tsx +5759 -0
  8. package/src/commands/handler.ts +889 -0
  9. package/src/config/appVersion.ts +69 -0
  10. package/src/config/buildInfo.ts +3 -0
  11. package/src/config/launchArgs.ts +196 -0
  12. package/src/config/layeredConfig.ts +853 -0
  13. package/src/config/legacyEnv.ts +16 -0
  14. package/src/config/persistence.ts +377 -0
  15. package/src/config/runtimeConfig.ts +558 -0
  16. package/src/config/settings.ts +405 -0
  17. package/src/config/toml-serialize.ts +109 -0
  18. package/src/config/trustStore.ts +84 -0
  19. package/src/config/updateCheckCache.ts +85 -0
  20. package/src/core/README.md +55 -0
  21. package/src/core/agent/loop.ts +464 -0
  22. package/src/core/agent/protocol.ts +345 -0
  23. package/src/core/agent/tools.ts +423 -0
  24. package/src/core/auth/codexAuth.ts +359 -0
  25. package/src/core/codex/codexExecArgs.ts +166 -0
  26. package/src/core/codex/codexLaunch.ts +163 -0
  27. package/src/core/codex/codexPrompt.ts +429 -0
  28. package/src/core/debug/inputDebug.ts +51 -0
  29. package/src/core/debug/localStreamDebug.ts +50 -0
  30. package/src/core/debug/modelStateDebug.ts +35 -0
  31. package/src/core/executables/antigravityExecutable.ts +48 -0
  32. package/src/core/executables/claudeExecutable.ts +63 -0
  33. package/src/core/executables/codexExecutable.ts +160 -0
  34. package/src/core/executables/executableResolver.ts +164 -0
  35. package/src/core/executables/geminiExecutable.ts +78 -0
  36. package/src/core/models/codexCapabilities.ts +97 -0
  37. package/src/core/models/codexModelCapabilities.ts +624 -0
  38. package/src/core/models/codexModelsCacheSeed.ts +153 -0
  39. package/src/core/models/modelSpecs.ts +303 -0
  40. package/src/core/models/providerModelCache.ts +94 -0
  41. package/src/core/perf/profiler.ts +125 -0
  42. package/src/core/perf/renderDebug.ts +398 -0
  43. package/src/core/process/CommandRunner.ts +280 -0
  44. package/src/core/process/processValidation.ts +111 -0
  45. package/src/core/providerLauncher/launcher.ts +220 -0
  46. package/src/core/providerLauncher/registry.ts +354 -0
  47. package/src/core/providerLauncher/types.ts +95 -0
  48. package/src/core/providerLauncher/workspaceConfig.ts +487 -0
  49. package/src/core/providerRuntime/anthropic.ts +580 -0
  50. package/src/core/providerRuntime/antigravity.ts +500 -0
  51. package/src/core/providerRuntime/capabilityProfile.ts +383 -0
  52. package/src/core/providerRuntime/claudeCodeDiscovery.ts +724 -0
  53. package/src/core/providerRuntime/claudeCodeDiscoveryDebug.ts +55 -0
  54. package/src/core/providerRuntime/codexaCupy.ts +97 -0
  55. package/src/core/providerRuntime/codexaNative.ts +425 -0
  56. package/src/core/providerRuntime/contextMetadata.ts +397 -0
  57. package/src/core/providerRuntime/gemini.ts +789 -0
  58. package/src/core/providerRuntime/lmstudio.ts +118 -0
  59. package/src/core/providerRuntime/local.ts +770 -0
  60. package/src/core/providerRuntime/localHarness/runtime.ts +1090 -0
  61. package/src/core/providerRuntime/localOutputBudget.ts +17 -0
  62. package/src/core/providerRuntime/mistralVibe.ts +667 -0
  63. package/src/core/providerRuntime/models.ts +175 -0
  64. package/src/core/providerRuntime/reasoning.ts +20 -0
  65. package/src/core/providerRuntime/registry.ts +286 -0
  66. package/src/core/providerRuntime/types.ts +142 -0
  67. package/src/core/providerRuntime/unsloth.ts +216 -0
  68. package/src/core/providers/codexJsonStream.ts +305 -0
  69. package/src/core/providers/codexSubprocess.ts +378 -0
  70. package/src/core/providers/codexTranscript.ts +695 -0
  71. package/src/core/providers/openaiNative.ts +13 -0
  72. package/src/core/providers/registry.ts +21 -0
  73. package/src/core/providers/types.ts +91 -0
  74. package/src/core/shared/attachments.ts +101 -0
  75. package/src/core/shared/cleanupFastFail.ts +67 -0
  76. package/src/core/shared/clipboard.ts +24 -0
  77. package/src/core/shared/clipboardImage.ts +111 -0
  78. package/src/core/shared/githubDiagnostics.ts +222 -0
  79. package/src/core/shared/hollowResponseFormat.ts +39 -0
  80. package/src/core/terminal/clearFrameBoundary.ts +852 -0
  81. package/src/core/terminal/frameLock.ts +110 -0
  82. package/src/core/terminal/inkRenderReset.ts +123 -0
  83. package/src/core/terminal/startupClear.ts +20 -0
  84. package/src/core/terminal/terminalCapabilities.ts +100 -0
  85. package/src/core/terminal/terminalControl.ts +169 -0
  86. package/src/core/terminal/terminalSanitize.ts +147 -0
  87. package/src/core/terminal/terminalTitle.ts +400 -0
  88. package/src/core/version/channel.ts +27 -0
  89. package/src/core/version/packageManager.ts +119 -0
  90. package/src/core/version/updateCheck.ts +203 -0
  91. package/src/core/workspace/appData.ts +107 -0
  92. package/src/core/workspace/conversationStore.ts +335 -0
  93. package/src/core/workspace/launchContext.ts +259 -0
  94. package/src/core/workspace/planStorage.ts +135 -0
  95. package/src/core/workspace/projectInstructions.ts +54 -0
  96. package/src/core/workspace/scratchDir.ts +64 -0
  97. package/src/core/workspace/workspaceActivity.ts +384 -0
  98. package/src/core/workspace/workspaceGuard.ts +377 -0
  99. package/src/core/workspace/workspaceRoot.ts +47 -0
  100. package/src/exec.ts +73 -0
  101. package/src/headless/execArgs.ts +296 -0
  102. package/src/headless/execRunner.ts +304 -0
  103. package/src/index.tsx +270 -0
  104. package/src/legacyEnvBootstrap.ts +5 -0
  105. package/src/session/appSession.ts +771 -0
  106. package/src/session/chatLifecycle.ts +994 -0
  107. package/src/session/conversation.ts +107 -0
  108. package/src/session/liveRenderScheduler.ts +214 -0
  109. package/src/session/persistedResponse.ts +93 -0
  110. package/src/session/planFlow.ts +159 -0
  111. package/src/session/planTranscript.ts +19 -0
  112. package/src/session/promptRunSchedule.ts +26 -0
  113. package/src/session/types.ts +234 -0
  114. package/src/test/runtimeTestUtils.ts +14 -0
  115. package/src/types/react-dom.d.ts +3 -0
  116. package/src/ui/chrome/ActivityBars.tsx +68 -0
  117. package/src/ui/chrome/ActivityIndicator.tsx +58 -0
  118. package/src/ui/chrome/AnimatedStatusText.tsx +69 -0
  119. package/src/ui/chrome/AppShell.tsx +474 -0
  120. package/src/ui/chrome/BottomComposer.tsx +1135 -0
  121. package/src/ui/chrome/DashCard.tsx +82 -0
  122. package/src/ui/chrome/RunFooter.tsx +65 -0
  123. package/src/ui/chrome/RuntimeStatusBar.tsx +108 -0
  124. package/src/ui/chrome/Spinner.tsx +25 -0
  125. package/src/ui/chrome/TopHeader.tsx +439 -0
  126. package/src/ui/chrome/UpdateAvailableCard.tsx +42 -0
  127. package/src/ui/chrome/busyStatusAnimation.ts +11 -0
  128. package/src/ui/input/commandNormalize.ts +66 -0
  129. package/src/ui/input/focus.ts +73 -0
  130. package/src/ui/input/imageAttachments.ts +18 -0
  131. package/src/ui/input/inputBuffer.ts +203 -0
  132. package/src/ui/input/pastedContent.ts +75 -0
  133. package/src/ui/input/rawArrowKeys.ts +28 -0
  134. package/src/ui/input/slashCommands.ts +43 -0
  135. package/src/ui/input/useStdinRawModeLease.ts +23 -0
  136. package/src/ui/layout.ts +560 -0
  137. package/src/ui/panels/AttachmentImportPanel.tsx +131 -0
  138. package/src/ui/panels/AuthPanel.tsx +149 -0
  139. package/src/ui/panels/BackendPicker.tsx +28 -0
  140. package/src/ui/panels/ModePicker.tsx +31 -0
  141. package/src/ui/panels/ModelPicker.tsx +31 -0
  142. package/src/ui/panels/ModelPickerScreen.tsx +761 -0
  143. package/src/ui/panels/ModelReasoningPicker.tsx +458 -0
  144. package/src/ui/panels/Panel.tsx +51 -0
  145. package/src/ui/panels/PermissionsPanel.tsx +78 -0
  146. package/src/ui/panels/PlanActionPicker.tsx +187 -0
  147. package/src/ui/panels/ProviderPicker.tsx +753 -0
  148. package/src/ui/panels/ProviderSetupPrompt.tsx +52 -0
  149. package/src/ui/panels/ReasoningPicker.tsx +46 -0
  150. package/src/ui/panels/ResumePicker.tsx +90 -0
  151. package/src/ui/panels/SelectionPanel.tsx +138 -0
  152. package/src/ui/panels/SettingsPanel.tsx +156 -0
  153. package/src/ui/panels/TextEntryPanel.tsx +139 -0
  154. package/src/ui/panels/ThemePicker.tsx +32 -0
  155. package/src/ui/panels/ToolApprovalPanel.tsx +46 -0
  156. package/src/ui/panels/UpdatePromptPanel.tsx +236 -0
  157. package/src/ui/panels/responsivePickerViewport.ts +64 -0
  158. package/src/ui/render/Markdown.tsx +331 -0
  159. package/src/ui/render/diffRenderer.ts +116 -0
  160. package/src/ui/render/logoVariants.ts +113 -0
  161. package/src/ui/render/modeDisplay.ts +52 -0
  162. package/src/ui/render/outputPipeline.ts +64 -0
  163. package/src/ui/render/runtimeDisplay.ts +128 -0
  164. package/src/ui/render/terminalAnswerFormat.ts +128 -0
  165. package/src/ui/render/textLayout.ts +392 -0
  166. package/src/ui/theme.tsx +274 -0
  167. package/src/ui/themeFlow.ts +41 -0
  168. package/src/ui/timeline/ActionRequiredBlock.tsx +38 -0
  169. package/src/ui/timeline/AgentBlock.tsx +130 -0
  170. package/src/ui/timeline/StaticIntroItem.tsx +54 -0
  171. package/src/ui/timeline/ThinkingBlock.tsx +100 -0
  172. package/src/ui/timeline/Timeline.tsx +1410 -0
  173. package/src/ui/timeline/TranscriptShell.tsx +302 -0
  174. package/src/ui/timeline/TurnGroup.tsx +673 -0
  175. package/src/ui/timeline/layoutListWindow.ts +145 -0
  176. package/src/ui/timeline/liveViewportWindow.ts +68 -0
  177. package/src/ui/timeline/progressEntries.ts +156 -0
  178. package/src/ui/timeline/runActivityView.ts +37 -0
  179. package/src/ui/timeline/staticTranscriptCache.ts +174 -0
  180. package/src/ui/timeline/streamCoalesce.ts +53 -0
  181. package/src/ui/timeline/timelineMeasure.ts +3273 -0
  182. package/src/ui/useThrottledValue.ts +31 -0
package/bin/ubume.js ADDED
@@ -0,0 +1,398 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn } from "child_process";
4
+ import { appendFileSync, mkdirSync, readFileSync } from "fs";
5
+ import { homedir } from "os";
6
+ import { dirname, join } from "path";
7
+ import { fileURLToPath } from "url";
8
+
9
+ process.title = "UBUME";
10
+
11
+ // Pre-rename CODEXA_* variables keep working: copy each into its unset UBUME_* name.
12
+ // The Codexa model family keeps its CODEXA_NATIVE_/CUPY_/NUMPY_ names.
13
+ for (const [key, value] of Object.entries(process.env)) {
14
+ if (!key.startsWith("CODEXA_") || /^CODEXA_(NATIVE|CUPY|NUMPY)_/.test(key)) continue;
15
+ const ubumeKey = `UBUME_${key.slice("CODEXA_".length)}`;
16
+ if (process.env[ubumeKey] === undefined) process.env[ubumeKey] = value;
17
+ }
18
+
19
+ function resolveLauncherDebugDir() {
20
+ const dataDir = process.env.UBUME_DATA_DIR?.trim()
21
+ || process.env.CODEXA_DATA_DIR?.trim()
22
+ || (process.platform === "win32"
23
+ ? join(process.env.LOCALAPPDATA?.trim() || process.env.APPDATA?.trim() || join(homedir(), "AppData", "Local"), "Ubume")
24
+ : process.platform === "darwin"
25
+ ? join(homedir(), "Library", "Application Support", "Ubume")
26
+ : join(process.env.XDG_DATA_HOME?.trim() || join(homedir(), ".local", "share"), "ubume"));
27
+ return join(dataDir, "debug");
28
+ }
29
+
30
+ const currentFile = fileURLToPath(import.meta.url);
31
+ const packageRoot = dirname(dirname(currentFile));
32
+ const forwardArgs = process.argv.slice(2);
33
+ const workspaceRoot = process.cwd();
34
+ const launcherStartTimeMs = Number(process.env.UBUME_EXEC_TIMING_EPOCH_MS || process.env.CODEXA_EXEC_TIMING_EPOCH_MS) || Date.now();
35
+ let launcherPreviousElapsedMs = 0;
36
+ let intendedTerminalTitle = "Ubume";
37
+
38
+ function writeRenderDebugRecord(kind, fields) {
39
+ if (
40
+ process.env.UBUME_RENDER_DEBUG !== "1"
41
+ && process.env.UBUME_DEBUG_RENDER !== "1"
42
+ && process.env.CODEXA_RENDER_DEBUG !== "1"
43
+ && process.env.CODEXA_DEBUG_RENDER !== "1"
44
+ ) {
45
+ return;
46
+ }
47
+
48
+ try {
49
+ const debugDir = resolveLauncherDebugDir();
50
+ mkdirSync(debugDir, { recursive: true });
51
+ appendFileSync(
52
+ process.env.UBUME_RENDER_DEBUG_FILE?.trim()
53
+ || process.env.CODEXA_RENDER_DEBUG_FILE?.trim()
54
+ || join(debugDir, "render-status.log"),
55
+ JSON.stringify({
56
+ ts: Date.now(),
57
+ pid: process.pid,
58
+ sessionId: `launcher-${Date.now()}-${process.pid}`,
59
+ kind,
60
+ ...fields,
61
+ }) + "\n",
62
+ "utf8",
63
+ );
64
+ } catch {
65
+ // Debug logging must never disturb launcher startup.
66
+ }
67
+ }
68
+
69
+ function debugLaunch(message, fields = {}) {
70
+ if (process.env.UBUME_DEBUG_LAUNCH !== "1" && process.env.CODEXA_DEBUG_LAUNCH !== "1") {
71
+ return;
72
+ }
73
+
74
+ const serializedFields = Object.entries(fields)
75
+ .map(([key, value]) => `${key}=${JSON.stringify(value)}`)
76
+ .join(" ");
77
+ process.stderr.write(`[ubume:launch] ${message}${serializedFields ? ` ${serializedFields}` : ""}\n`);
78
+ }
79
+
80
+ function writeTerminalTitleDebugRecord(fields) {
81
+ if (process.env.UBUME_DEBUG_TERMINAL_TITLE !== "1" && process.env.CODEXA_DEBUG_TERMINAL_TITLE !== "1") {
82
+ return;
83
+ }
84
+
85
+ try {
86
+ const debugDir = resolveLauncherDebugDir();
87
+ mkdirSync(debugDir, { recursive: true });
88
+ appendFileSync(
89
+ process.env.UBUME_TERMINAL_TITLE_DEBUG_FILE?.trim()
90
+ || process.env.CODEXA_TERMINAL_TITLE_DEBUG_FILE?.trim()
91
+ || process.env.UBUME_RENDER_DEBUG_FILE?.trim()
92
+ || process.env.CODEXA_RENDER_DEBUG_FILE?.trim()
93
+ || join(debugDir, "render-status.log"),
94
+ JSON.stringify({
95
+ ts: Date.now(),
96
+ pid: process.pid,
97
+ lifecycleState: "launcher",
98
+ ...fields,
99
+ }) + "\n",
100
+ "utf8",
101
+ );
102
+ } catch {
103
+ // Debug logging must never disturb launcher startup.
104
+ }
105
+ }
106
+
107
+ function sanitizeTerminalTitle(title) {
108
+ return String(title ?? "")
109
+ .replace(/[\u0000-\u001f\u007f]/g, " ")
110
+ .replace(/\x1b/g, "")
111
+ .trim();
112
+ }
113
+
114
+ function normalizeTerminalTitle(title) {
115
+ const cleanTitle = sanitizeTerminalTitle(title);
116
+ if (!cleanTitle || /^[a-zA-Z]:[\\/]/.test(cleanTitle) || /^\\\\/.test(cleanTitle)) {
117
+ return "Ubume";
118
+ }
119
+ return cleanTitle;
120
+ }
121
+
122
+ function recordIntendedTitle(reason, force = true) {
123
+ writeTerminalTitleDebugRecord({
124
+ event: "ubumeTitleSkipped",
125
+ source: `bin/ubume.js:${reason}`,
126
+ title: intendedTerminalTitle,
127
+ reason,
128
+ force,
129
+ });
130
+ writeRenderDebugRecord("title", {
131
+ event: "launcherTitleSkipped",
132
+ source: `bin/ubume.js:${reason}`,
133
+ title: intendedTerminalTitle,
134
+ reason,
135
+ force,
136
+ });
137
+ }
138
+
139
+ function hasFlag(args, longFlag, shortFlag) {
140
+ for (const arg of args) {
141
+ if (arg === "--") return false;
142
+ if (arg === longFlag || arg === shortFlag) return true;
143
+ }
144
+ return false;
145
+ }
146
+
147
+ function readPackageVersion() {
148
+ try {
149
+ const raw = readFileSync(join(packageRoot, "package.json"), "utf8");
150
+ const parsed = JSON.parse(raw);
151
+ return typeof parsed.version === "string" && parsed.version.trim()
152
+ ? parsed.version.trim()
153
+ : null;
154
+ } catch {
155
+ return null;
156
+ }
157
+ }
158
+
159
+ function printHelp() {
160
+ const version = readPackageVersion();
161
+ const versionLine = version ? `ubume ${version}` : "ubume";
162
+ console.log(`${versionLine}
163
+
164
+ Usage:
165
+ ubume
166
+ ubume "explain this repo"
167
+ ubume exec "print the current directory"
168
+ ubume --headless-benchmark "print the current directory"
169
+ ubume [options] [prompt]
170
+
171
+ Options:
172
+ -h, --help Show this help text and exit.
173
+ -v, --version Show the installed Ubume version and exit.
174
+ --headless-benchmark
175
+ Run Ubume through the headless benchmark path.
176
+ --profile <name> Load a profile from config.
177
+ -m, --model <name> Select the model for this launch.
178
+ --reasoning <effort>
179
+ Reasoning effort for ubume exec: none, minimal, low, medium, high, xhigh.
180
+ -c, --config <key=val> Override a runtime config value.
181
+
182
+ Inside Ubume:
183
+ /help Show interactive commands.
184
+ /model Open model selection.
185
+ /mode Open mode selection.
186
+ /exit Exit the interactive UI.
187
+ `);
188
+ }
189
+
190
+ const isHeadlessExec = forwardArgs[0] === "exec";
191
+ const isHeadlessBenchmark = forwardArgs[0] === "--headless-benchmark";
192
+ const isHeadlessMode = isHeadlessExec || isHeadlessBenchmark;
193
+ const execTimingEnabled = isHeadlessMode
194
+ && (
195
+ process.env.UBUME_EXEC_TIMING === "1"
196
+ || process.env.CODEXA_EXEC_TIMING === "1"
197
+ || forwardArgs.includes("--timing")
198
+ || forwardArgs.includes("--benchmark-diagnostics")
199
+ );
200
+ const parentStdinIsTTY = Boolean(process.stdin.isTTY);
201
+ const parentStdoutIsTTY = Boolean(process.stdout.isTTY);
202
+ const parentStderrIsTTY = Boolean(process.stderr.isTTY);
203
+ const parentHasTTY = parentStdinIsTTY && parentStdoutIsTTY;
204
+
205
+ function markExecTiming(phase, fields = {}) {
206
+ if (!execTimingEnabled) return;
207
+ const elapsedMs = Date.now() - launcherStartTimeMs;
208
+ const deltaMs = elapsedMs - launcherPreviousElapsedMs;
209
+ launcherPreviousElapsedMs = elapsedMs;
210
+ const formattedFields = Object.entries(fields)
211
+ .map(([key, value]) => {
212
+ const serialized = Array.isArray(value) || typeof value === "string"
213
+ ? JSON.stringify(value)
214
+ : String(value);
215
+ return `${key}=${serialized}`;
216
+ })
217
+ .join(" ");
218
+ process.stderr.write(`[ubume exec timing] phase=${phase} elapsed_ms=${elapsedMs} delta_ms=${deltaMs}${formattedFields ? ` ${formattedFields}` : ""}\n`);
219
+ }
220
+
221
+ markExecTiming("launcher_start", { pid: process.pid });
222
+
223
+ if (!isHeadlessMode && parentHasTTY) {
224
+ intendedTerminalTitle = normalizeTerminalTitle(
225
+ process.env.UBUME_INITIAL_TERMINAL_TITLE
226
+ || process.env.CODEXA_INITIAL_TERMINAL_TITLE
227
+ || "Ubume",
228
+ );
229
+ recordIntendedTitle("launcher-startup-title-disabled");
230
+ }
231
+
232
+ if (!isHeadlessMode && hasFlag(forwardArgs, "--help", "-h")) {
233
+ printHelp();
234
+ process.exit(0);
235
+ }
236
+
237
+ if (!isHeadlessMode && hasFlag(forwardArgs, "--version", "-v")) {
238
+ console.log(readPackageVersion() ?? "unknown");
239
+ process.exit(0);
240
+ }
241
+
242
+ /**
243
+ * Filters out terminal mouse reporting escape sequences from stdin data.
244
+ * Prevents SGR mouse clicks and scroll events from leaking into the TUI app.
245
+ */
246
+ function createMouseFilter() {
247
+ let buffer = "";
248
+ let timer = null;
249
+
250
+ return (data) => {
251
+ buffer += data;
252
+
253
+ // Clear existing timer
254
+ if (timer) clearTimeout(timer);
255
+
256
+ // Check for complete SGR mouse sequences: ESC [ < button ; x ; y M/m
257
+ // Also handle scroll events: button 64/96 (scroll up), 65/97 (scroll down)
258
+ const sgrMouseRegex = /\x1b\[<[0-9]+;[0-9]+;[0-9]+[Mm]/g;
259
+ let hasFullSequence = sgrMouseRegex.test(buffer);
260
+
261
+ // If we have a complete sequence, filter and emit immediately
262
+ if (hasFullSequence) {
263
+ buffer = buffer.replace(sgrMouseRegex, "");
264
+ return buffer;
265
+ }
266
+
267
+ // If we have partial SGR start, wait for completion or timeout
268
+ if (/\x1b\[<[0-9]*;?[0-9]*;?[0-9]*$/.test(buffer)) {
269
+ timer = setTimeout(() => {
270
+ // Timeout: assume incomplete mouse sequence, just emit filtered buffer
271
+ timer = null;
272
+ }, 50);
273
+ return null; // Wait for more data
274
+ }
275
+
276
+ // Otherwise, emit immediately (normal input)
277
+ return buffer;
278
+ };
279
+ }
280
+
281
+ const bunExecutable = process.env.UBUME_BUN_EXECUTABLE?.trim()
282
+ || process.env.CODEXA_BUN_EXECUTABLE?.trim()
283
+ || (process.platform === "win32" ? "bun.exe" : "bun");
284
+
285
+ const appEntry = join(packageRoot, "src", "index.tsx");
286
+ const execEntry = join(packageRoot, "src", "exec.ts");
287
+ const bunEntry = isHeadlessMode ? execEntry : appEntry;
288
+ const bunForwardArgs = isHeadlessMode ? forwardArgs.slice(1) : forwardArgs;
289
+
290
+ // Detect if parent process has a real TTY
291
+ const childStdio = isHeadlessMode
292
+ ? ["ignore", "inherit", "inherit"]
293
+ : parentHasTTY
294
+ ? ["inherit", "inherit", "inherit"]
295
+ : ["pipe", "pipe", "pipe"];
296
+
297
+ debugLaunch("resolved launch mode", {
298
+ mode: isHeadlessMode ? "headless" : "interactive-ui",
299
+ stdinIsTTY: parentStdinIsTTY,
300
+ stdoutIsTTY: parentStdoutIsTTY,
301
+ stderrIsTTY: parentStderrIsTTY,
302
+ TERM: process.env.TERM,
303
+ WT_SESSION: process.env.WT_SESSION,
304
+ TERM_PROGRAM: process.env.TERM_PROGRAM,
305
+ argv: process.argv,
306
+ childStdio,
307
+ });
308
+
309
+ markExecTiming("bun_spawn_start", { executable: bunExecutable });
310
+ if (!isHeadlessMode && parentHasTTY) {
311
+ recordIntendedTitle("before-bun-spawn-disabled");
312
+ }
313
+
314
+ const child = spawn(
315
+ bunExecutable,
316
+ ["run", "--silent", bunEntry, ...(bunForwardArgs.length > 0 ? ["--", ...bunForwardArgs] : [])],
317
+ {
318
+ cwd: workspaceRoot,
319
+ stdio: childStdio,
320
+ env: {
321
+ ...process.env,
322
+ CODEX_WORKSPACE_ROOT: workspaceRoot,
323
+ UBUME_LAUNCH_KIND: "installed-bin",
324
+ UBUME_PACKAGE_ROOT: packageRoot,
325
+ UBUME_LAUNCHER_SCRIPT: currentFile,
326
+ UBUME_RELAUNCH_EXECUTABLE: process.execPath,
327
+ UBUME_RELAUNCH_ARGS: JSON.stringify([currentFile, ...forwardArgs]),
328
+ UBUME_PARENT_HAS_TTY: parentHasTTY ? "1" : "0",
329
+ UBUME_HEADLESS_BENCHMARK: isHeadlessBenchmark ? "1" : "0",
330
+ UBUME_EXEC_TIMING_EPOCH_MS: String(launcherStartTimeMs),
331
+ UBUME_INITIAL_TERMINAL_TITLE: intendedTerminalTitle,
332
+ },
333
+ },
334
+ );
335
+
336
+ child.once("spawn", () => {
337
+ if (!isHeadlessMode && parentHasTTY) {
338
+ recordIntendedTitle("after-bun-spawn-disabled");
339
+ }
340
+ });
341
+
342
+ if (!isHeadlessMode && !parentHasTTY) {
343
+ const mouseFilter = createMouseFilter();
344
+
345
+ process.stdin.on("data", (data) => {
346
+ const filtered = mouseFilter(data);
347
+ if (filtered) {
348
+ child.stdin.write(filtered);
349
+ }
350
+ });
351
+
352
+ process.stdin.on("end", () => {
353
+ child.stdin.end();
354
+ });
355
+
356
+ process.stdin.on("error", (error) => {
357
+ console.error(`stdin error: ${error.message}`);
358
+ });
359
+
360
+ child.stdin.on("error", (error) => {
361
+ // Ignore broken pipe errors
362
+ if (error.code !== "EPIPE") {
363
+ console.error(`child stdin error: ${error.message}`);
364
+ }
365
+ });
366
+ }
367
+
368
+ if (!isHeadlessMode) {
369
+ child.stdout?.on("data", (chunk) => {
370
+ process.stdout.write(chunk);
371
+ });
372
+ child.stderr?.on("data", (chunk) => {
373
+ process.stderr.write(chunk);
374
+ });
375
+ }
376
+
377
+ child.on("error", (error) => {
378
+ if (!isHeadlessMode && parentHasTTY) {
379
+ recordIntendedTitle("bun-spawn-error-disabled");
380
+ }
381
+ markExecTiming("bun_spawn_error", { message: error.message });
382
+ console.error(`Failed to launch Bun: ${error.message}`);
383
+ console.error("Bun is required to launch ubume. Install Bun, then run this command again.");
384
+ process.exit(1);
385
+ });
386
+
387
+ child.on("close", (code, signal) => {
388
+ if (!isHeadlessMode && parentHasTTY) {
389
+ recordIntendedTitle("bun-close-disabled");
390
+ }
391
+ markExecTiming("launcher_exit", { exit_code: code ?? 0, signal: signal ?? null });
392
+ if (signal) {
393
+ process.kill(process.pid, signal);
394
+ return;
395
+ }
396
+
397
+ process.exit(code ?? 0);
398
+ });
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "ubume",
3
+ "version": "0.1.0",
4
+ "description": "Terminal UI for Ubume — TypeScript + Bun + Ink",
5
+ "type": "module",
6
+ "bin": {
7
+ "ubume": "bin/ubume.js",
8
+ "codexa": "bin/codexa.js"
9
+ },
10
+ "scripts": {
11
+ "start": "bun run --silent src/index.tsx",
12
+ "dev": "bun --watch --silent src/index.tsx",
13
+ "dev:run": "node scripts/run-local-dev.mjs",
14
+ "install:dev-bin": "node scripts/install-local-dev-bin.mjs",
15
+ "debug:claude-models": "bun run --silent src/core/providerRuntime/claudeCodeDiscoveryDebug.ts",
16
+ "typecheck": "tsc --noEmit",
17
+ "test": "bun test --max-concurrency=1",
18
+ "audit:ubume-gap": "node scripts/audit-ubume-capabilities.mjs",
19
+ "audit:codexa-gap": "node scripts/audit-ubume-capabilities.mjs",
20
+ "smoke:terminal-bench": "node scripts/smoke-terminal-bench.mjs",
21
+ "gen-build-info": "node scripts/gen-build-info.mjs",
22
+ "build": "bun run gen-build-info && bun run typecheck",
23
+ "prepublishOnly": "npm run gen-build-info && npm run typecheck && bun test --max-concurrency=1"
24
+ },
25
+ "dependencies": {
26
+ "@deepseek-ai/dsh": "0.1.1-rc.2",
27
+ "@deepseek-ai/dsh-attachment-local": "0.1.1-rc.2",
28
+ "@deepseek-ai/dsh-llm": "0.1.1-rc.2",
29
+ "@deepseek-ai/dsh-scope": "0.1.1-rc.2",
30
+ "@deepseek-ai/dsh-sdk-protocol": "0.1.1-rc.2",
31
+ "@deepseek-ai/dsh-session": "0.1.1-rc.2",
32
+ "ink": "^7.1.0",
33
+ "ink-select-input": "^6.2.0",
34
+ "react": "^19.2.7",
35
+ "react-dom": "^19.2.7"
36
+ },
37
+ "devDependencies": {
38
+ "@types/node": "^26.1.1",
39
+ "@types/react": "^19.2.17",
40
+ "typescript": "^7.0.2"
41
+ },
42
+ "overrides": {
43
+ "ws": "^8.21.0"
44
+ },
45
+ "trustedDependencies": [
46
+ "@deepseek-ai/dsh-subprocess-local"
47
+ ],
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/golba98/ubume-cli.git"
51
+ },
52
+ "bugs": {
53
+ "url": "https://github.com/golba98/ubume-cli/issues"
54
+ },
55
+ "homepage": "https://github.com/golba98/ubume-cli#readme",
56
+ "publishConfig": {
57
+ "registry": "https://registry.npmjs.org"
58
+ },
59
+ "license": "MIT",
60
+ "files": [
61
+ "bin/",
62
+ "src/",
63
+ "!src/**/*.test.ts",
64
+ "!src/**/*.test.tsx"
65
+ ]
66
+ }