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/PROJECT-SUMMARY.md +104 -24
- package/README.md +403 -14
- package/bun.lock +22 -1
- package/manage_opencode_projects.py +71 -66
- package/package.json +7 -3
- package/src/bin/opencode-manager.ts +133 -3
- package/src/cli/backup.ts +324 -0
- package/src/cli/commands/chat.ts +336 -0
- package/src/cli/commands/projects.ts +238 -0
- package/src/cli/commands/sessions.ts +520 -0
- package/src/cli/commands/tokens.ts +180 -0
- package/src/cli/commands/tui.ts +36 -0
- package/src/cli/errors.ts +259 -0
- package/src/cli/formatters/json.ts +184 -0
- package/src/cli/formatters/ndjson.ts +71 -0
- package/src/cli/formatters/table.ts +837 -0
- package/src/cli/index.ts +209 -0
- package/src/cli/output.ts +661 -0
- package/src/cli/resolvers.ts +274 -0
- package/src/lib/clipboard.ts +37 -0
- package/src/lib/opencode-data-provider.ts +685 -0
- package/src/lib/opencode-data-sqlite.ts +1973 -0
- package/src/lib/opencode-data.ts +380 -1
- package/src/lib/search.ts +170 -0
- package/src/{opencode-tui.tsx → tui/app.tsx} +739 -105
- package/src/tui/args.ts +92 -0
- package/src/tui/index.tsx +46 -0
- package/tsconfig.json +1 -1
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@
|
|
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
|
|
2
|
+
"""Launch the OpenCode metadata manager (TUI or CLI).
|
|
3
3
|
|
|
4
|
-
This wrapper keeps the previous entry point name but
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
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
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
def
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
+
"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/
|
|
24
|
-
"dev": "bun --watch src/
|
|
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
+
})
|