opencode-manager 0.3.1 → 0.4.1

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.
package/bun.lock CHANGED
@@ -7,9 +7,12 @@
7
7
  "dependencies": {
8
8
  "@opentui/core": "^0.1.44",
9
9
  "@opentui/react": "^0.1.44",
10
+ "commander": "^12.0.0",
11
+ "fast-fuzzy": "^1.12.0",
10
12
  "react": "^19.0.0",
11
13
  },
12
14
  "devDependencies": {
15
+ "@types/bun": "^1.3.6",
13
16
  "@types/node": "^22.8.5",
14
17
  "@types/react": "^19.0.0",
15
18
  "typescript": "^5.6.3",
@@ -93,6 +96,8 @@
93
96
 
94
97
  "@tokenizer/token": ["@tokenizer/token@0.3.0", "", {}, "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="],
95
98
 
99
+ "@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
100
+
96
101
  "@types/node": ["@types/node@22.19.0", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-xpr/lmLPQEj+TUnHmR+Ab91/glhJvsqcjB+yY0Ix9GO70H6Lb4FHH5GeqdOE5btAx7eIMwuHkp4H2MSkLcqWbA=="],
97
102
 
98
103
  "@types/react": ["@types/react@19.2.2", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA=="],
@@ -113,6 +118,8 @@
113
118
 
114
119
  "bun-ffi-structs": ["bun-ffi-structs@0.1.2", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-Lh1oQAYHDcnesJauieA4UNkWGXY9hYck7OA5IaRwE3Bp6K2F2pJSNYqq+hIy7P3uOvo3km3oxS8304g5gDMl/w=="],
115
120
 
121
+ "bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
122
+
116
123
  "bun-webgpu": ["bun-webgpu@0.1.4", "", { "dependencies": { "@webgpu/types": "^0.1.60" }, "optionalDependencies": { "bun-webgpu-darwin-arm64": "^0.1.4", "bun-webgpu-darwin-x64": "^0.1.4", "bun-webgpu-linux-x64": "^0.1.4", "bun-webgpu-win32-x64": "^0.1.4" } }, "sha512-Kw+HoXl1PMWJTh9wvh63SSRofTA8vYBFCw0XEP1V1fFdQEDhI8Sgf73sdndE/oDpN/7CMx0Yv/q8FCvO39ROMQ=="],
117
124
 
118
125
  "bun-webgpu-darwin-arm64": ["bun-webgpu-darwin-arm64@0.1.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eDgLN9teKTfmvrCqgwwmWNsNszxYs7IZdCqk0S1DCarvMhr4wcajoSBlA/nQA0/owwLduPTS8xxCnQp4/N/gDg=="],
@@ -123,6 +130,8 @@
123
130
 
124
131
  "bun-webgpu-win32-x64": ["bun-webgpu-win32-x64@0.1.4", "", { "os": "win32", "cpu": "x64" }, "sha512-Z5yAK28xrcm8Wb5k7TZ8FJKpOI/r+aVCRdlHYAqI2SDJFN3nD4mJs900X6kNVmG/xFzb5yOuKVYWGg+6ZXWbyA=="],
125
132
 
133
+ "commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
134
+
126
135
  "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="],
127
136
 
128
137
  "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
@@ -131,10 +140,14 @@
131
140
 
132
141
  "exif-parser": ["exif-parser@0.1.12", "", {}, "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw=="],
133
142
 
143
+ "fast-fuzzy": ["fast-fuzzy@1.12.0", "", { "dependencies": { "graphemesplit": "^2.4.1" } }, "sha512-sXxGgHS+ubYpsdLnvOvJ9w5GYYZrtL9mkosG3nfuD446ahvoWEsSKBP7ieGmWIKVLnaxRDgUJkZMdxRgA2Ni+Q=="],
144
+
134
145
  "file-type": ["file-type@16.5.4", "", { "dependencies": { "readable-web-to-node-stream": "^3.0.0", "strtok3": "^6.2.4", "token-types": "^4.1.1" } }, "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw=="],
135
146
 
136
147
  "gifwrap": ["gifwrap@0.10.1", "", { "dependencies": { "image-q": "^4.0.0", "omggif": "^1.0.10" } }, "sha512-2760b1vpJHNmLzZ/ubTtNnEx5WApN/PYWJvXvgS+tL1egTTthayFYIQQNi136FLEDcN/IyEY2EcGpIITD6eYUw=="],
137
148
 
149
+ "graphemesplit": ["graphemesplit@2.6.0", "", { "dependencies": { "js-base64": "^3.6.0", "unicode-trie": "^2.0.0" } }, "sha512-rG9w2wAfkpg0DILa1pjnjNfucng3usON360shisqIMUBw/87pojcBSrHmeE4UwryAuBih7g8m1oilf5/u8EWdQ=="],
150
+
138
151
  "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
139
152
 
140
153
  "image-q": ["image-q@4.0.0", "", { "dependencies": { "@types/node": "16.9.1" } }, "sha512-PfJGVgIfKQJuq3s0tTDOKtztksibuUEbJQIYT3by6wctQo+Rdlh7ef4evJ5NCdxY4CfMbvFkocEwbl4BF8RlJw=="],
@@ -143,11 +156,13 @@
143
156
 
144
157
  "jpeg-js": ["jpeg-js@0.4.4", "", {}, "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg=="],
145
158
 
159
+ "js-base64": ["js-base64@3.7.8", "", {}, "sha512-hNngCeKxIUQiEUN3GPJOkz4wF/YvdUdbNL9hsBcMQTkKzboD7T/q3OYOuuPZLUE6dBxSGpwhk5mwuDud7JVAow=="],
160
+
146
161
  "mime": ["mime@3.0.0", "", { "bin": "cli.js" }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="],
147
162
 
148
163
  "omggif": ["omggif@1.0.10", "", {}, "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw=="],
149
164
 
150
- "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
165
+ "pako": ["pako@0.2.9", "", {}, "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="],
151
166
 
152
167
  "parse-bmfont-ascii": ["parse-bmfont-ascii@1.0.6", "", {}, "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA=="],
153
168
 
@@ -189,6 +204,8 @@
189
204
 
190
205
  "three": ["three@0.177.0", "", {}, "sha512-EiXv5/qWAaGI+Vz2A+JfavwYCMdGjxVsrn3oBwllUoqYeaBO75J63ZfyaQKoiLrqNHoTlUc6PFgMXnS0kI45zg=="],
191
206
 
207
+ "tiny-inflate": ["tiny-inflate@1.0.3", "", {}, "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw=="],
208
+
192
209
  "tinycolor2": ["tinycolor2@1.6.0", "", {}, "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw=="],
193
210
 
194
211
  "token-types": ["token-types@4.2.1", "", { "dependencies": { "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" } }, "sha512-6udB24Q737UD/SDsKAHI9FCRP7Bqc9D/MQUV02ORQg5iskjtLJlZJNdN4kKtcdtwCeWIwIHDGaUsTsCCAa8sFQ=="],
@@ -197,6 +214,8 @@
197
214
 
198
215
  "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
199
216
 
217
+ "unicode-trie": ["unicode-trie@2.0.0", "", { "dependencies": { "pako": "^0.2.5", "tiny-inflate": "^1.0.0" } }, "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ=="],
218
+
200
219
  "utif2": ["utif2@4.1.0", "", { "dependencies": { "pako": "^1.0.11" } }, "sha512-+oknB9FHrJ7oW7A2WZYajOcv4FcDR4CfoGB0dPNfxbi4GO05RRnFmt5oa23+9w32EanrYcSJWspUiJkLMs+37w=="],
201
220
 
202
221
  "web-tree-sitter": ["web-tree-sitter@0.25.10", "", { "peerDependencies": { "@types/emscripten": "^1.40.0" }, "optionalPeers": ["@types/emscripten"] }, "sha512-Y09sF44/13XvgVKgO2cNDw5rGk6s26MgoZPXLESvMXeefBf7i6/73eFurre0IsTW6E14Y0ArIzhUMmjoc7xyzA=="],
@@ -214,5 +233,7 @@
214
233
  "image-q/@types/node": ["@types/node@16.9.1", "", {}, "sha512-QpLcX9ZSsq3YYUUnD3nFDY8H7wctAhQj/TFKL8Ya8v5fMm3CFXxo8zStsLAl780ltoYoo1WvKUVGBQK+1ifr7g=="],
215
234
 
216
235
  "pixelmatch/pngjs": ["pngjs@6.0.0", "", {}, "sha512-TRzzuFRRmEoSW/p1KVAmiOgPco2Irlah+bGFCeNfJXxxYGwSw7YwAOAcd7X28K/m5bjBWKsC29KyoMfHbypayg=="],
236
+
237
+ "utif2/pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="],
217
238
  }
218
239
  }
@@ -1,93 +1,98 @@
1
1
  #!/usr/bin/env python3
2
- """Launch the OpenCode metadata TUI built with OpenTUI.
2
+ """Launch the OpenCode metadata manager (TUI or CLI).
3
3
 
4
- This wrapper keeps the previous entry point name but simply shells out to the
5
- new Bun-powered React TUI located under ``src/opencode-tui.tsx``. Use
6
- ``manage_opencode_projects.py -- --help`` to see the TUI's runtime help text.
4
+ This wrapper keeps the previous entry point name but shells out to the
5
+ Bun-powered entrypoint at ``src/bin/opencode-manager.ts``. The routing logic
6
+ detects CLI subcommands (projects, sessions, chat, tokens) and passes them
7
+ directly, otherwise defaults to the TUI.
8
+
9
+ Usage:
10
+ manage_opencode_projects.py # Launch TUI (default)
11
+ manage_opencode_projects.py projects list # CLI: list projects
12
+ manage_opencode_projects.py sessions list # CLI: list sessions
13
+ manage_opencode_projects.py -- --help # Show TUI help
7
14
  """
8
15
 
9
16
  from __future__ import annotations
10
17
 
11
- import argparse
12
18
  import shutil
13
19
  import subprocess
14
20
  import sys
15
21
  from pathlib import Path
16
22
  from typing import Sequence
17
23
 
18
- DEFAULT_ROOT = Path.home() / ".local" / "share" / "opencode"
24
+ # CLI subcommands that route to the CLI module instead of TUI
25
+ CLI_SUBCOMMANDS = frozenset({"projects", "sessions", "chat", "tokens"})
26
+
19
27
  PROJECT_DIR = Path(__file__).resolve().parent
20
28
 
21
29
 
22
- def parse_args(argv: Sequence[str] | None = None) -> argparse.Namespace:
23
- parser = argparse.ArgumentParser(
24
- description="Open the interactive OpenCode metadata manager TUI",
25
- epilog=(
26
- "Examples:\n"
27
- " manage_opencode_projects.py\n"
28
- " Launch the TUI using the default metadata root.\n\n"
29
- " manage_opencode_projects.py --root /tmp/opencode\n"
30
- " Launch the TUI against a different storage directory.\n\n"
31
- " manage_opencode_projects.py -- --help\n"
32
- " Show the TUI's built-in CLI help output.\n"
33
- ),
34
- )
35
- parser.add_argument(
36
- "--root",
37
- type=Path,
38
- default=DEFAULT_ROOT,
39
- help="Metadata root to inspect (defaults to ~/.local/share/opencode)",
40
- )
41
- parser.add_argument(
42
- "--bun",
43
- type=Path,
44
- default=None,
45
- help="Optional path to the bun executable if it's not on PATH",
46
- )
47
- parser.add_argument(
48
- "tui_args",
49
- nargs=argparse.REMAINDER,
50
- help=(
51
- "Additional arguments forwarded to the TUI after '--'. For example: "
52
- "manage_opencode_projects.py -- --help"
53
- ),
54
- )
55
- return parser.parse_args(argv)
56
-
57
-
58
- def find_bun(explicit: Path | None) -> str:
59
- if explicit:
60
- return str(explicit)
30
+ def find_bun(explicit_path: str | None = None) -> str:
31
+ """Locate bun executable, preferring explicit path if provided."""
32
+ if explicit_path:
33
+ return explicit_path
61
34
  bun_path = shutil.which("bun")
62
35
  if bun_path:
63
36
  return bun_path
64
- raise SystemExit("bun executable not found. Please install Bun to run the TUI.")
65
-
66
-
67
- def launch_tui(root: Path, bun_exe: str, extra_args: Sequence[str]) -> int:
68
- # Normalize passthrough args: drop leading "--" if present
69
- if extra_args and len(extra_args) > 0 and extra_args[0] == "--":
70
- extra_args = extra_args[1:]
71
- cmd = [
72
- bun_exe,
73
- "run",
74
- "tui",
75
- "--",
76
- "--root",
77
- str(root.expanduser()),
78
- ]
79
- if extra_args:
80
- cmd.extend(extra_args)
37
+ raise SystemExit("bun executable not found. Please install Bun.")
38
+
39
+
40
+ def is_cli_subcommand(args: Sequence[str]) -> bool:
41
+ """Check if the first non-flag argument is a CLI subcommand."""
42
+ for arg in args:
43
+ if arg.startswith("-"):
44
+ # Skip flags like --bun, --root, etc.
45
+ continue
46
+ # First positional argument determines routing
47
+ return arg in CLI_SUBCOMMANDS
48
+ return False
49
+
81
50
 
51
+ def run_entrypoint(bun_exe: str, args: Sequence[str]) -> int:
52
+ """Run the main entrypoint with given arguments.
53
+
54
+ The TypeScript entrypoint handles all routing internally:
55
+ - CLI subcommands (projects, sessions, chat, tokens) → CLI module
56
+ - Everything else → TUI
57
+ """
58
+ # Normalize: drop leading "--" if present (legacy passthrough syntax)
59
+ args_list = list(args)
60
+ if args_list and args_list[0] == "--":
61
+ args_list = args_list[1:]
62
+
63
+ cmd = [bun_exe, "src/bin/opencode-manager.ts"] + args_list
82
64
  process = subprocess.run(cmd, cwd=PROJECT_DIR)
83
65
  return process.returncode
84
66
 
85
67
 
86
68
  def main(argv: Sequence[str] | None = None) -> int:
87
- args = parse_args(argv)
88
- bun_exe = find_bun(args.bun)
89
- extra_args = list(args.tui_args or [])
90
- return launch_tui(args.root, bun_exe, extra_args)
69
+ """Main entry point.
70
+
71
+ Parses minimal wrapper-level options (--bun) and forwards everything
72
+ else to the TypeScript entrypoint which handles TUI/CLI routing.
73
+ """
74
+ if argv is None:
75
+ argv = sys.argv[1:]
76
+
77
+ args = list(argv)
78
+ bun_exe_path: str | None = None
79
+
80
+ # Extract --bun option if present (wrapper-level option only)
81
+ filtered_args: list[str] = []
82
+ i = 0
83
+ while i < len(args):
84
+ if args[i] == "--bun" and i + 1 < len(args):
85
+ bun_exe_path = args[i + 1]
86
+ i += 2
87
+ elif args[i].startswith("--bun="):
88
+ bun_exe_path = args[i].split("=", 1)[1]
89
+ i += 1
90
+ else:
91
+ filtered_args.append(args[i])
92
+ i += 1
93
+
94
+ bun_exe = find_bun(bun_exe_path)
95
+ return run_entrypoint(bun_exe, filtered_args)
91
96
 
92
97
 
93
98
  if __name__ == "__main__":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-manager",
3
- "version": "0.3.1",
3
+ "version": "0.4.1",
4
4
  "description": "Terminal UI for inspecting OpenCode metadata stores.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -20,8 +20,9 @@
20
20
  "LICENSE"
21
21
  ],
22
22
  "scripts": {
23
- "tui": "bun src/opencode-tui.tsx",
24
- "dev": "bun --watch src/opencode-tui.tsx",
23
+ "tui": "bun src/tui/index.tsx",
24
+ "dev": "bun --watch src/tui/index.tsx",
25
+ "test": "bun test",
25
26
  "typecheck": "bunx tsc --noEmit",
26
27
  "prepublishOnly": "bun run typecheck"
27
28
  },
@@ -45,9 +46,12 @@
45
46
  "dependencies": {
46
47
  "@opentui/core": "^0.1.44",
47
48
  "@opentui/react": "^0.1.44",
49
+ "commander": "^12.0.0",
50
+ "fast-fuzzy": "^1.12.0",
48
51
  "react": "^19.0.0"
49
52
  },
50
53
  "devDependencies": {
54
+ "@types/bun": "^1.3.6",
51
55
  "@types/node": "^22.8.5",
52
56
  "@types/react": "^19.0.0",
53
57
  "typescript": "^5.6.3"
@@ -1,4 +1,134 @@
1
1
  #!/usr/bin/env bun
2
- // Bun-native CLI entry that simply boots the TUI module.
3
- // Keeping this file tiny lets `bun x opencode-manager` launch instantly without extra bundling.
4
- import "../opencode-tui"
2
+ /**
3
+ * Main CLI entrypoint for opencode-manager.
4
+ *
5
+ * Routes between TUI and CLI modes based on provided subcommands:
6
+ * - No subcommand → shows help
7
+ * - "tui" subcommand → launches TUI
8
+ * - CLI subcommands (projects, sessions, chat, tokens) → launches CLI
9
+ *
10
+ * Uses dynamic imports to keep initial load fast and avoid loading
11
+ * unused modules.
12
+ */
13
+
14
+ // Known CLI subcommands that should route to the CLI module
15
+ const CLI_SUBCOMMANDS = new Set([
16
+ "projects",
17
+ "sessions",
18
+ "chat",
19
+ "tokens",
20
+ ])
21
+
22
+ // Subcommands that explicitly request TUI
23
+ const TUI_SUBCOMMANDS = new Set(["tui"])
24
+
25
+ // Version from package.json
26
+ const VERSION = "0.4.0"
27
+
28
+ function printHelp(): void {
29
+ console.log(`opencode-manager v${VERSION}
30
+
31
+ Inspect and manage OpenCode metadata stores via TUI or CLI.
32
+
33
+ USAGE:
34
+ opencode-manager [command] [options]
35
+
36
+ MODES:
37
+ tui Launch interactive TUI (terminal user interface)
38
+ <command> Run CLI command (see below)
39
+
40
+ CLI COMMANDS:
41
+ projects list List all projects
42
+ projects delete Delete a project's metadata
43
+
44
+ sessions list List sessions (optionally filter by project)
45
+ sessions delete Delete a session's metadata
46
+ sessions rename Rename a session
47
+ sessions move Move a session to another project
48
+ sessions copy Copy a session to another project
49
+
50
+ chat list List messages in a session
51
+ chat show Show a specific message
52
+ chat search Search chat content across sessions
53
+
54
+ tokens session Show token usage for a session
55
+ tokens project Show token usage for a project
56
+ tokens global Show global token usage
57
+
58
+ OPTIONS:
59
+ --help, -h Show this help
60
+ --version, -v Show version
61
+
62
+ EXAMPLES:
63
+ opencode-manager tui # Launch TUI
64
+ opencode-manager projects list --format json # List projects as JSON
65
+ opencode-manager sessions list --project X # List sessions for project
66
+ opencode-manager chat search --query "error" # Search chat content
67
+
68
+ For detailed help on any command:
69
+ opencode-manager <command> --help
70
+ opencode-manager <command> <subcommand> --help
71
+ `)
72
+ }
73
+
74
+ function printVersion(): void {
75
+ console.log(VERSION)
76
+ }
77
+
78
+ async function main(): Promise<void> {
79
+ const args = process.argv.slice(2)
80
+ const firstArg = args[0]
81
+
82
+ // Handle no args, --help, or -h → show help
83
+ if (!firstArg || firstArg === "--help" || firstArg === "-h") {
84
+ printHelp()
85
+ return
86
+ }
87
+
88
+ // Handle --version or -v
89
+ if (firstArg === "--version" || firstArg === "-v") {
90
+ printVersion()
91
+ return
92
+ }
93
+
94
+ // Determine routing based on first argument
95
+ const isCliSubcommand = CLI_SUBCOMMANDS.has(firstArg)
96
+ const isTuiSubcommand = TUI_SUBCOMMANDS.has(firstArg)
97
+
98
+ if (isCliSubcommand) {
99
+ // Route to CLI module (dynamically imported)
100
+ // Using string path to avoid TypeScript errors before CLI module exists
101
+ const cliModulePath = "../cli/index"
102
+ try {
103
+ const cliModule = await import(cliModulePath)
104
+ if (typeof cliModule.runCLI !== "function") {
105
+ throw new Error("CLI module missing runCLI export")
106
+ }
107
+ await cliModule.runCLI(args)
108
+ } catch (error) {
109
+ const errCode = (error as NodeJS.ErrnoException).code
110
+ const errMessage = (error as Error).message
111
+ if (errCode === "ERR_MODULE_NOT_FOUND" || errMessage.includes("Cannot find module")) {
112
+ console.error(`CLI module not yet implemented. Subcommand: ${firstArg}`)
113
+ console.error("Run without subcommand to launch TUI, or use --help for usage.")
114
+ process.exit(1)
115
+ }
116
+ throw error
117
+ }
118
+ } else if (isTuiSubcommand) {
119
+ // Explicit TUI request - strip "tui" subcommand so TUI args parser doesn't see it
120
+ const tuiArgs = args.slice(1)
121
+ const { bootstrap } = await import("../tui/index")
122
+ await bootstrap(tuiArgs)
123
+ } else {
124
+ // Unknown command
125
+ console.error(`Unknown command: ${firstArg}\n`)
126
+ printHelp()
127
+ process.exit(1)
128
+ }
129
+ }
130
+
131
+ main().catch((error) => {
132
+ console.error(error)
133
+ process.exit(1)
134
+ })