Skip to content

Commit 04c43eb

Browse files
committed
refactor: replace OpenTUI git submodule with TypeScript symlink script
Remove git submodule dependency on packages/opentui and replace with a cross-platform TypeScript postinstall script that creates symlinks to OpenTUI packages. This improves monorepo management and eliminates submodule complexity. Changes: - Remove .gitmodules and packages/opentui submodule - Add scripts/setup-cli-symlinks.ts for automated symlink creation - Update postinstall script in package.json - Add preserveSymlinks: false to cli/tsconfig.json - Document new setup approach in cli/knowledge.md 🤖 Generated with Codebuff Co-Authored-By: Codebuff <noreply@codebuff.com>
1 parent a2212c5 commit 04c43eb

File tree

8 files changed

+255
-428
lines changed

8 files changed

+255
-428
lines changed

.gitmodules

Lines changed: 0 additions & 4 deletions
This file was deleted.

bun.lock

Lines changed: 127 additions & 415 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/knowledge.md

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,22 @@
2020

2121
OpenTUI expects plain text content or the `content` prop - it does not handle JSX expressions within text elements.
2222

23+
## Screen Mode and TODO List Positioning
24+
25+
The CLI chat interface adapts its layout based on terminal dimensions:
26+
27+
### Screen Modes
28+
- **Full-screen**: width ≥ 70 AND height ≥ 30
29+
- **Wide-screen**: width ≥ 70 AND height < 30
30+
- **Tall-screen**: width < 70 AND height ≥ 30
31+
- **Small-screen**: width < 70 AND height < 30
32+
33+
### TODO List Positioning
34+
- **Right side**: Full-screen and wide-screen modes (when there's sufficient horizontal space)
35+
- **Top**: Tall-screen and small-screen modes (when terminal is narrow)
36+
37+
The TODO list automatically repositions based on available space to ensure optimal visibility and usability.
38+
2339
### Text Styling Components Must Be Wrapped in `<text>`
2440

2541
All text styling components (`<strong>`, `<em>`, `<span>`, etc.) **MUST** be nested inside a `<text>` component. They cannot be returned directly from render functions.
@@ -70,7 +86,7 @@ function renderMarkdown(content: string) {
7086
**Text Modifiers** (must be inside `<text>`):
7187
- `<span>` - Generic inline styling
7288
- `<strong>` and `<b>` - Bold text
73-
- `<em>` and `<i>` - Italic text
89+
- `<em>` and `<i>` - Italic text
7490
- `<u>` - Underlined text
7591
- `<br>` - Line break
7692

@@ -309,7 +325,7 @@ const ShimmerText = ({ text }) => {
309325
3. Can cause reconciliation errors when the component updates
310326
4. Goes against React's composition principles
311327

312-
**Best practice:**
328+
**Best practice:**
313329
- Child components that render styled text should return Fragments with `<span>` elements
314330
- Parent components are responsible for providing the `<text>` wrapper
315331
- This follows React's pattern of "dumb" presentational components
@@ -399,3 +415,22 @@ The bug occurred when tool toggles were rendered. Agent toggles worked fine, but
399415
```
400416

401417
**Key lesson:** Any component that receives content from `renderMarkdown()` or `renderStreamingMarkdown()` MUST wrap it in a `<text>` element, even if the content might be ReactNode. The Fragment can contain raw strings that need the text wrapper to be valid.
418+
419+
## Monorepo Setup: OpenTUI Dependencies
420+
421+
The CLI workspace depends on `@opentui/core` and `@opentui/react` from GitHub. These packages are not automatically built when running `bun install` in the CLI workspace.
422+
423+
**Solution**: A cross-platform TypeScript postinstall script creates symlinks from `cli/node_modules/@opentui/*` to the built packages.
424+
425+
- Script location: `scripts/setup-cli-symlinks.ts`
426+
- Language: TypeScript (runs with Bun, works on Windows/Mac/Linux)
427+
- Runs automatically after `bun install` at the root level
428+
- Automatically builds OpenTUI packages if not already built
429+
- Creates symlinks within OpenTUI monorepo so packages can find each other
430+
- Creates symlinks in CLI workspace for: `@opentui/core`, `@opentui/react`, `@opentui/core-darwin-arm64`
431+
432+
**Benefits over bash:**
433+
- Cross-platform (Windows, Mac, Linux)
434+
- Better error handling with TypeScript
435+
- Uses Node.js 'junction' symlinks which work universally
436+
- Automatically cleans up existing symlinks before creating new ones

cli/package.json

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,14 @@
2929
},
3030
"dependencies": {
3131
"@codebuff/sdk": "workspace:*",
32-
"@opentui/react": "file:../packages/opentui/packages/react",
33-
"@opentui/core": "file:../packages/opentui/packages/core",
32+
"@opentui/core": "github:CodebuffAI/opentui#codebuff/custom",
33+
"@opentui/core-darwin-arm64": "0.1.26",
34+
"@opentui/react": "github:CodebuffAI/opentui#codebuff/custom",
3435
"react": "^19.0.0",
3536
"remark-parse": "^11.0.0",
36-
"unified": "^11.0.0"
37+
"unified": "^11.0.0",
38+
"react-reconciler": "^0.32.0",
39+
"yoga-layout": "^3.2.1"
3740
},
3841
"devDependencies": {
3942
"@types/bun": "^1.2.11",

cli/tsconfig.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@
1111
"declaration": true,
1212
"declarationMap": true,
1313
"esModuleInterop": true,
14-
"skipLibCheck": true
14+
"skipLibCheck": true,
15+
"preserveSymlinks": false
1516
},
1617
"include": ["src/**/*"],
1718
"exclude": ["node_modules", "dist"]

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,14 @@
1010
"npm-app",
1111
"web",
1212
"packages/*",
13-
"packages/opentui/packages/*",
1413
"scripts",
1514
"evals",
1615
"sdk",
1716
".agents",
1817
"cli"
1918
],
2019
"scripts": {
21-
"postinstall": "git submodule update --init --recursive packages/opentui",
20+
"postinstall": "bun scripts/setup-cli-symlinks.ts",
2221
"dev": "bash scripts/dev.sh",
2322
"start-db": "bun --cwd common db:start",
2423
"start-bin": "bun --cwd npm-app start-bin",

packages/opentui

Lines changed: 0 additions & 1 deletion
This file was deleted.

scripts/setup-cli-symlinks.ts

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
#!/usr/bin/env bun
2+
3+
import { existsSync, mkdirSync, symlinkSync, rmSync } from 'fs'
4+
import { join } from 'path'
5+
import { execSync } from 'child_process'
6+
7+
const ROOT_DIR = process.cwd()
8+
const CLI_DIR = join(ROOT_DIR, 'cli')
9+
const ROOT_NODE_MODULES = join(ROOT_DIR, 'node_modules')
10+
11+
console.log('Setting up OpenTUI symlinks for CLI workspace...')
12+
13+
// Check if source packages exist (they're in packages/ subdirectories)
14+
const corePackagePath = join(ROOT_NODE_MODULES, '@opentui/core/packages/core')
15+
if (!existsSync(corePackagePath)) {
16+
console.error('⚠️ Warning: OpenTUI packages not found in root node_modules')
17+
console.error(
18+
'Please ensure "bun install" completed successfully at the root level',
19+
)
20+
process.exit(1)
21+
}
22+
23+
// Build OpenTUI packages if not already built
24+
const coreDistPath = join(corePackagePath, 'dist')
25+
if (!existsSync(coreDistPath)) {
26+
console.log('Building OpenTUI packages (this may take a moment)...')
27+
try {
28+
execSync('bun run build', {
29+
cwd: join(ROOT_NODE_MODULES, '@opentui/core'),
30+
stdio: 'ignore',
31+
})
32+
} catch (error) {
33+
console.warn('Build failed, but continuing anyway...')
34+
}
35+
}
36+
37+
function createSymlink(target: string, linkPath: string) {
38+
try {
39+
// Remove existing symlink or directory if it exists
40+
if (existsSync(linkPath)) {
41+
rmSync(linkPath, { recursive: true, force: true })
42+
}
43+
symlinkSync(target, linkPath, 'junction') // 'junction' works on both Windows and Unix
44+
} catch (error) {
45+
console.warn(`Failed to create symlink ${linkPath}:`, error)
46+
}
47+
}
48+
49+
// Create symlinks in the root node_modules so packages can find each other
50+
const opentunCoreNodeModules = join(
51+
ROOT_NODE_MODULES,
52+
'@opentui/core/node_modules/@opentui',
53+
)
54+
const opentuiReactNodeModules = join(
55+
ROOT_NODE_MODULES,
56+
'@opentui/react/node_modules/@opentui',
57+
)
58+
59+
mkdirSync(opentunCoreNodeModules, { recursive: true })
60+
mkdirSync(opentuiReactNodeModules, { recursive: true })
61+
62+
const corePackage = join(ROOT_NODE_MODULES, '@opentui/core/packages/core')
63+
const reactPackage = join(ROOT_NODE_MODULES, '@opentui/react/packages/react')
64+
65+
createSymlink(corePackage, join(opentunCoreNodeModules, 'core'))
66+
createSymlink(corePackage, join(opentuiReactNodeModules, 'core'))
67+
createSymlink(reactPackage, join(opentuiReactNodeModules, 'react'))
68+
69+
// Create the @opentui directory in CLI's node_modules
70+
const cliOpentuiDir = join(CLI_DIR, 'node_modules/@opentui')
71+
mkdirSync(cliOpentuiDir, { recursive: true })
72+
73+
// Create symlinks in CLI workspace
74+
createSymlink(corePackage, join(cliOpentuiDir, 'core'))
75+
createSymlink(reactPackage, join(cliOpentuiDir, 'react'))
76+
77+
const darwinArm64 = join(ROOT_NODE_MODULES, '@opentui/core-darwin-arm64')
78+
if (existsSync(darwinArm64)) {
79+
createSymlink(darwinArm64, join(cliOpentuiDir, 'core-darwin-arm64'))
80+
}
81+
82+
console.log('✅ OpenTUI symlinks created successfully')

0 commit comments

Comments
 (0)