@logicseek/hello-cc 0.1.5 → 0.1.6
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/CHANGELOG.md +75 -0
- package/bin/hcc.mjs +1541 -3430
- package/docs/README.md +2 -0
- package/docs/README.zh-CN.md +3 -1
- package/docs/architecture-split-review.md +217 -0
- package/docs/architecture.md +385 -0
- package/docs/architecture.zh-CN.md +364 -0
- package/docs/layout-split-stack-audit.md +298 -0
- package/lib/automation.mjs +8 -0
- package/lib/cli-args.mjs +1 -1
- package/lib/cli-runtime.mjs +44 -0
- package/lib/coordination-state.mjs +218 -0
- package/lib/core/coordination/automation.mjs +222 -0
- package/lib/core/coordination/locks.mjs +46 -0
- package/lib/core/coordination/messages.mjs +116 -0
- package/lib/core/coordination/tasks.mjs +224 -0
- package/lib/core/coordination/teams.mjs +73 -0
- package/lib/core/coordination/timeline.mjs +187 -0
- package/lib/core/peers/bindings.mjs +83 -0
- package/lib/core/peers/format.mjs +13 -0
- package/lib/core/peers/identity.mjs +53 -0
- package/lib/core/peers/liveness.mjs +90 -0
- package/lib/core/peers/reconcile.mjs +238 -0
- package/lib/core/peers/session.mjs +14 -0
- package/lib/core/sessions/launch.mjs +89 -0
- package/lib/db/schema.mjs +324 -0
- package/lib/db/stores/peers.mjs +231 -0
- package/lib/db-schema.mjs +7 -324
- package/lib/errors.mjs +1 -7
- package/lib/handoff.mjs +26 -0
- package/lib/help.mjs +3 -0
- package/lib/integrations/hooks.mjs +171 -0
- package/lib/integrations/peers/identity.mjs +208 -0
- package/lib/integrations/peers/processes.mjs +151 -0
- package/lib/integrations/providers.mjs +282 -0
- package/lib/integrations/shims/script.mjs +384 -0
- package/lib/integrations/shims.mjs +106 -0
- package/lib/json-file.mjs +1 -14
- package/lib/locks.mjs +10 -0
- package/lib/messages.mjs +3 -0
- package/lib/package-meta.mjs +6 -24
- package/lib/peer-bindings.mjs +14 -0
- package/lib/peer-format.mjs +4 -0
- package/lib/peer-identity.mjs +15 -0
- package/lib/project-context.mjs +6 -0
- package/lib/project-registry.mjs +7 -0
- package/lib/provider-commands.mjs +13 -0
- package/lib/release/package-meta.mjs +31 -0
- package/lib/release/release-notes.mjs +40 -0
- package/lib/release-notes.mjs +6 -40
- package/lib/runtime/client.mjs +42 -0
- package/lib/runtime/paths.mjs +45 -0
- package/lib/runtime/project-context.mjs +32 -0
- package/lib/runtime/projects.mjs +76 -0
- package/lib/runtime/state.mjs +128 -0
- package/lib/runtime-client.mjs +3 -0
- package/lib/runtime-paths.mjs +11 -45
- package/lib/runtime-state.mjs +11 -0
- package/lib/session-launch.mjs +17 -0
- package/lib/setup.mjs +17 -293
- package/lib/shared/errors.mjs +7 -0
- package/lib/shared/json-file.mjs +14 -0
- package/lib/shim-script.mjs +1 -384
- package/lib/state-render.mjs +5 -0
- package/lib/task-cli.mjs +45 -0
- package/lib/task-liveness.mjs +8 -0
- package/lib/task-store.mjs +3 -0
- package/lib/team-planning.mjs +7 -0
- package/lib/terminal/tmux.mjs +372 -0
- package/lib/timeline.mjs +12 -0
- package/lib/tmux.mjs +18 -0
- package/lib/ui/help.mjs +419 -0
- package/lib/ui/state-render.mjs +61 -0
- package/lib/web/http.mjs +55 -0
- package/lib/web/peer-actions.mjs +270 -0
- package/lib/web/runtime.mjs +154 -0
- package/lib/web/ui-template.mjs +2055 -0
- package/lib/web-http.mjs +7 -55
- package/lib/web-peer-actions.mjs +3 -0
- package/lib/web-runtime.mjs +17 -108
- package/lib/web-ui-template.mjs +3 -1909
- package/package.json +1 -1
- package/scripts/github-release.mjs +2 -2
- package/scripts/regression.mjs +2468 -17
- package/scripts/release-notes.mjs +2 -2
package/lib/ui/help.mjs
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
export function createHelpFunctions(config = {}) {
|
|
2
|
+
const productName = config.productName || 'hello-cc';
|
|
3
|
+
const version = config.version || '';
|
|
4
|
+
const cliName = config.cliName || 'hcc';
|
|
5
|
+
const npmPackageName = config.npmPackageName || '@logicseek/hello-cc';
|
|
6
|
+
|
|
7
|
+
return {
|
|
8
|
+
helpMain() {
|
|
9
|
+
console.log(`${productName} ${version}
|
|
10
|
+
|
|
11
|
+
Project-local coordination bus for multiple Claude Code and Codex CLI sessions.
|
|
12
|
+
|
|
13
|
+
Usage:
|
|
14
|
+
${cliName} [--root DIR] [--db FILE] [--json] <command> [args]
|
|
15
|
+
|
|
16
|
+
Commands:
|
|
17
|
+
web Start coordination, shims, tmux, and browser console
|
|
18
|
+
up Initialize local coordination only
|
|
19
|
+
down Stop the running hello-cc web runtime
|
|
20
|
+
update Update the global npm install of hello-cc
|
|
21
|
+
uninstall Remove hooks, shims, and optional project data
|
|
22
|
+
init Initialize .hello-cc/mesh.db and guidance
|
|
23
|
+
register --peer ID Register or update a peer session identity
|
|
24
|
+
join --peer ID Register this shell and print eval-able env
|
|
25
|
+
env --peer ID Print eval-able HCC_PEER/HCC_ROOT/HCC_DB exports
|
|
26
|
+
heartbeat [--peer ID] Mark the current peer alive, optionally renew locks
|
|
27
|
+
peers List known peers
|
|
28
|
+
status [--peer ID] Show project coordination state
|
|
29
|
+
state [--peer ID] Show timeline and next coordination action
|
|
30
|
+
scan [--register] Detect existing Claude/Codex sessions
|
|
31
|
+
prompt --peer ID Print copy/paste session instructions
|
|
32
|
+
run --peer ID -- COMMAND Register a peer and run a command in this terminal
|
|
33
|
+
peer <subcommand> Start, attach, list, and stop tmux-backed peers
|
|
34
|
+
tmux gc [--yes] Clean stale DB-proven hcc-managed tmux sessions
|
|
35
|
+
inject PEER TEXT Write text into an attached terminal
|
|
36
|
+
ask PEER MESSAGE Send a direct work request to one peer
|
|
37
|
+
broadcast MESSAGE Send a work request to all peers
|
|
38
|
+
task <subcommand> Create, list, claim, update, finish tasks
|
|
39
|
+
team <subcommand> Plan, start, and inspect explicit task teams
|
|
40
|
+
msg <subcommand> Send, read, and ack messages
|
|
41
|
+
lock <subcommand> Acquire, renew, release, and list advisory locks
|
|
42
|
+
handoff <subcommand> Create and list handoffs
|
|
43
|
+
event tail Show recent coordination events
|
|
44
|
+
gc [--older-than DAYS] [--yes] Clean up stale peers, events, tasks, and buf files
|
|
45
|
+
Internal:
|
|
46
|
+
hook Hook entrypoint used by Claude/Codex
|
|
47
|
+
find-root Shim helper
|
|
48
|
+
which-real Shim helper
|
|
49
|
+
|
|
50
|
+
Environment:
|
|
51
|
+
HCC_ROOT Override project root
|
|
52
|
+
HCC_DB Override database path
|
|
53
|
+
HCC_PEER Default peer id; inferred automatically when absent
|
|
54
|
+
HCC_WEB_TOKEN Replace and save the stable web access token
|
|
55
|
+
`);
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
helpTask() {
|
|
59
|
+
console.log(`${cliName} task
|
|
60
|
+
|
|
61
|
+
Usage:
|
|
62
|
+
${cliName} task create --title TEXT [--body TEXT] [--from ID] [--to ID] [--priority N]
|
|
63
|
+
${cliName} task create --title TEXT --parent N [--team-role ROLE]
|
|
64
|
+
${cliName} task list [--status pending|claimed|running|review|blocked|done|abandoned] [--peer ID] [--all]
|
|
65
|
+
${cliName} task claim [--peer ID] --id N[,N] [--id N] [--ids N,N] [--force]
|
|
66
|
+
${cliName} task takeover [--peer ID] --id N --reason TEXT [--policy any|blocked|stale|blocked-or-stale] [--stale-after SECONDS]
|
|
67
|
+
${cliName} task next [--peer ID] [--force] [--count N]
|
|
68
|
+
${cliName} task update [--peer ID] --id N --status STATUS [--summary TEXT] [--body TEXT] [--to ID]
|
|
69
|
+
${cliName} task done [--peer ID] --id N --summary TEXT
|
|
70
|
+
|
|
71
|
+
Default task list shows all project tasks that are not done or abandoned.
|
|
72
|
+
--peer is an explicit filter; HCC_PEER does not hide other open tasks.
|
|
73
|
+
Messages use per-peer unread ack state; tasks do not.
|
|
74
|
+
task next returns your existing claimed/running/review/blocked task before
|
|
75
|
+
claiming another pending task. Use --force when intentionally taking additional
|
|
76
|
+
pending tasks; combine it with --count N for explicit batch claims.
|
|
77
|
+
Use task takeover when explicitly taking over a non-complete task from another
|
|
78
|
+
owner; it records the previous owner, requires a reason, and notifies them.
|
|
79
|
+
Use --policy blocked, stale, or blocked-or-stale to require an auditable
|
|
80
|
+
precondition before takeover. The default policy is any for backward
|
|
81
|
+
compatibility.
|
|
82
|
+
|
|
83
|
+
If --peer/--from and HCC_PEER are absent, hcc auto-joins the current terminal
|
|
84
|
+
as a stable project-local peer.
|
|
85
|
+
`);
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
helpTeam() {
|
|
89
|
+
console.log(`${cliName} team
|
|
90
|
+
|
|
91
|
+
Usage:
|
|
92
|
+
${cliName} team plan --from-task N [--item ROLE:TITLE] [--item PEER:ROLE:TITLE] [--workers A,B|codex:2,claude:1]
|
|
93
|
+
${cliName} team start --from-task N [--item ROLE:TITLE] [--item PEER:ROLE:TITLE] [--workers A,B|codex:2,claude:1] [--force]
|
|
94
|
+
${cliName} team status --task N
|
|
95
|
+
|
|
96
|
+
team plan is read-only. team start creates explicit child tasks under the parent
|
|
97
|
+
task and optionally assigns them to workers. It does not silently spawn model
|
|
98
|
+
processes or override the current-task rule.
|
|
99
|
+
workers may be explicit peer ids or kind counts such as codex:2,claude:1.
|
|
100
|
+
`);
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
helpState() {
|
|
104
|
+
console.log(`${cliName} state
|
|
105
|
+
|
|
106
|
+
Usage:
|
|
107
|
+
${cliName} state [--peer ID] [--resource PATH] [--scope SCOPE] [--intent read|review|work|write|stop|finish]
|
|
108
|
+
|
|
109
|
+
Shows the current collaboration timeline plus a machine-readable coordination
|
|
110
|
+
state machine. With --json, the response includes automation.next_action.argv so
|
|
111
|
+
agents can execute the suggested hcc command explicitly and leave an audit trail.
|
|
112
|
+
automation.current_task shows the peer's active claimed/running/review/blocked
|
|
113
|
+
task when one exists.
|
|
114
|
+
If a current task looks splittable, automation may suggest hcc team plan; this
|
|
115
|
+
is read-only until hcc team start is run explicitly.
|
|
116
|
+
With --intent read or --intent review, state treats resources as snapshot
|
|
117
|
+
inspection and does not suggest acquiring file locks. With write/work intents,
|
|
118
|
+
--scope lets agents coordinate independent regions of the same resource.
|
|
119
|
+
|
|
120
|
+
State does not execute coordination actions: it does not ack messages, claim
|
|
121
|
+
tasks, acquire locks, send messages, create handoffs, or mark tasks done. Opening
|
|
122
|
+
state may still perform normal SQLite schema maintenance for known project DBs.
|
|
123
|
+
`);
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
helpJoin() {
|
|
127
|
+
console.log(`${cliName} join
|
|
128
|
+
|
|
129
|
+
Usage:
|
|
130
|
+
eval "$(${cliName} join --peer ID [--kind codex|claude|shell|other] [--role ROLE])"
|
|
131
|
+
|
|
132
|
+
Examples:
|
|
133
|
+
eval "$(${cliName} join --peer codex-current --kind codex)"
|
|
134
|
+
${cliName} status
|
|
135
|
+
|
|
136
|
+
This registers the current shell as a peer and prints shell exports for
|
|
137
|
+
HCC_PEER, HCC_ROOT, and HCC_DB. A child CLI cannot mutate its parent shell
|
|
138
|
+
environment directly, so use eval to apply the exports to the current window.
|
|
139
|
+
`);
|
|
140
|
+
},
|
|
141
|
+
|
|
142
|
+
helpEnv() {
|
|
143
|
+
console.log(`${cliName} env
|
|
144
|
+
|
|
145
|
+
Usage:
|
|
146
|
+
eval "$(${cliName} env --peer ID)"
|
|
147
|
+
|
|
148
|
+
Examples:
|
|
149
|
+
eval "$(${cliName} env --peer codex-current)"
|
|
150
|
+
|
|
151
|
+
This only prints shell exports. Use hcc join when you also want to register
|
|
152
|
+
the peer in the project bus.
|
|
153
|
+
`);
|
|
154
|
+
},
|
|
155
|
+
|
|
156
|
+
helpMsg() {
|
|
157
|
+
console.log(`${cliName} msg
|
|
158
|
+
|
|
159
|
+
Usage:
|
|
160
|
+
${cliName} msg send [--from ID] [--to ID|all] --body TEXT [--task N] [--kind note|task|handoff]
|
|
161
|
+
${cliName} msg inbox [--peer ID] [--wait SEC] [--all] [--limit N]
|
|
162
|
+
${cliName} msg ack [--peer ID] --id N
|
|
163
|
+
${cliName} msg reply [--from ID] --id N --body TEXT [--to ID] [--kind reply]
|
|
164
|
+
${cliName} msg thread --id N [--limit N]
|
|
165
|
+
|
|
166
|
+
If --peer/--from and HCC_PEER are absent, hcc auto-joins the current terminal.
|
|
167
|
+
Use msg reply when answering a message so the response stays in the same thread.
|
|
168
|
+
`);
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
helpAsk() {
|
|
172
|
+
console.log(`${cliName} ask
|
|
173
|
+
|
|
174
|
+
Usage:
|
|
175
|
+
${cliName} ask PEER MESSAGE [--from ID] [--task N] [--inject]
|
|
176
|
+
${cliName} ask --to PEER --body TEXT [--from ID] [--task N] [--inject]
|
|
177
|
+
|
|
178
|
+
Examples:
|
|
179
|
+
${cliName} ask claude-a "Please review task #3."
|
|
180
|
+
${cliName} ask --to codex-b --body "Can you run the router tests?" --task 3
|
|
181
|
+
${cliName} ask claude-a "Please review task #3." --inject
|
|
182
|
+
`);
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
helpBroadcast() {
|
|
186
|
+
console.log(`${cliName} broadcast
|
|
187
|
+
|
|
188
|
+
Usage:
|
|
189
|
+
${cliName} broadcast MESSAGE [--from ID] [--task N] [--inject]
|
|
190
|
+
${cliName} broadcast --body TEXT [--from ID] [--task N] [--inject]
|
|
191
|
+
|
|
192
|
+
Example:
|
|
193
|
+
${cliName} broadcast "Pause edits under src/router until lock clears."
|
|
194
|
+
`);
|
|
195
|
+
},
|
|
196
|
+
|
|
197
|
+
helpInject() {
|
|
198
|
+
console.log(`${cliName} inject
|
|
199
|
+
|
|
200
|
+
Usage:
|
|
201
|
+
${cliName} inject PEER TEXT [--no-enter]
|
|
202
|
+
${cliName} inject --peer PEER --body TEXT [--no-enter]
|
|
203
|
+
|
|
204
|
+
Examples:
|
|
205
|
+
${cliName} inject codex-a "hcc msg inbox --peer codex-a"
|
|
206
|
+
${cliName} inject claude-a "Please review task #3."
|
|
207
|
+
|
|
208
|
+
This works for peers attached to the running hcc web runtime, including
|
|
209
|
+
tmux-backed local terminals, attached tmux panes, and shim-launched tmux
|
|
210
|
+
terminals.
|
|
211
|
+
`);
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
helpPeer() {
|
|
215
|
+
console.log(`${cliName} peer
|
|
216
|
+
|
|
217
|
+
Usage:
|
|
218
|
+
${cliName} peer list
|
|
219
|
+
${cliName} peer start PEER [--kind codex|claude|shell] [--role ROLE] [--cwd DIR] [--resume ID|NAME]
|
|
220
|
+
${cliName} peer start PEER --kind codex --last
|
|
221
|
+
${cliName} peer start PEER --kind claude --continue
|
|
222
|
+
${cliName} peer start PEER [--kind codex|claude|shell] [--role ROLE] [--cwd DIR] -- COMMAND [ARGS...]
|
|
223
|
+
${cliName} peer attach PEER [--pane PANE] [--kind codex|claude|shell] [--role ROLE] [--cwd DIR]
|
|
224
|
+
${cliName} peer stop PEER
|
|
225
|
+
|
|
226
|
+
Examples:
|
|
227
|
+
${cliName} peer start codex-a --kind codex -- codex
|
|
228
|
+
${cliName} peer start codex-a --kind codex --resume 00000000-0000-0000-0000-000000000000
|
|
229
|
+
${cliName} peer start codex-a --kind codex --last
|
|
230
|
+
${cliName} peer start claude-a --kind claude -- claude
|
|
231
|
+
${cliName} peer start claude-a --kind claude --resume 00000000-0000-0000-0000-000000000000
|
|
232
|
+
${cliName} peer start claude-a --kind claude --continue
|
|
233
|
+
${cliName} peer attach codex-a --pane %1
|
|
234
|
+
${cliName} peer stop codex-a
|
|
235
|
+
|
|
236
|
+
Start hcc web first. peer start creates a local tmux-backed terminal by default.
|
|
237
|
+
The web runtime attaches to that terminal; hcc down stops only the web runtime
|
|
238
|
+
and leaves the tmux terminal alive. peer attach imports an existing tmux pane.
|
|
239
|
+
If --pane is omitted, peer attach uses the current tmux pane when available.
|
|
240
|
+
Use --force only when intentionally overriding a provider-session or pane binding.
|
|
241
|
+
`);
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
helpTmux() {
|
|
245
|
+
console.log(`${cliName} tmux
|
|
246
|
+
|
|
247
|
+
Usage:
|
|
248
|
+
${cliName} tmux gc [--peer ID] [--older-than DAYS] [--dry-run] [--yes]
|
|
249
|
+
|
|
250
|
+
Examples:
|
|
251
|
+
${cliName} tmux gc
|
|
252
|
+
${cliName} tmux gc --older-than 14 --yes
|
|
253
|
+
|
|
254
|
+
tmux gc only considers sessions that are proven by this project's database:
|
|
255
|
+
peer_bindings.transport must be tmux, runtime_target must point at a live tmux
|
|
256
|
+
pane, and the actual tmux session name must match hello-cc's managed session
|
|
257
|
+
name for that peer and project root. It can also remove old sessions recorded by
|
|
258
|
+
tmux.session.rebind_cleanup_failed events after a successful provider rebind.
|
|
259
|
+
|
|
260
|
+
It skips sessions with attached tmux clients, sessions still managed by the
|
|
261
|
+
running Web runtime, sessions with a different HCC_ROOT tmux marker,
|
|
262
|
+
non-matching session names, and sessions newer than --older-than. The default is
|
|
263
|
+
a dry-run; deletion requires --yes.
|
|
264
|
+
`);
|
|
265
|
+
},
|
|
266
|
+
|
|
267
|
+
helpGc() {
|
|
268
|
+
console.log(`${cliName} gc
|
|
269
|
+
|
|
270
|
+
Usage:
|
|
271
|
+
${cliName} gc [--older-than DAYS] [--yes]
|
|
272
|
+
|
|
273
|
+
Examples:
|
|
274
|
+
${cliName} gc
|
|
275
|
+
${cliName} gc --older-than 14 --yes
|
|
276
|
+
|
|
277
|
+
Clean stale coordination database records and old buffer files for the current
|
|
278
|
+
project. By default this is a dry-run; deletion requires --yes.
|
|
279
|
+
`);
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
helpLock() {
|
|
283
|
+
console.log(`${cliName} lock
|
|
284
|
+
|
|
285
|
+
Usage:
|
|
286
|
+
${cliName} lock acquire [--peer ID] --resource PATH [--scope SCOPE] [--task N] [--ttl SEC] [--reason TEXT]
|
|
287
|
+
${cliName} lock renew [--peer ID] --resource PATH [--scope SCOPE] [--ttl SEC]
|
|
288
|
+
${cliName} lock release [--peer ID] --resource PATH [--scope SCOPE] [--force]
|
|
289
|
+
${cliName} lock list [--all]
|
|
290
|
+
|
|
291
|
+
Omit --scope to lock the whole resource. Different scopes on the same resource
|
|
292
|
+
can be held concurrently, but a whole-resource lock conflicts with every scope.
|
|
293
|
+
`);
|
|
294
|
+
},
|
|
295
|
+
|
|
296
|
+
helpHandoff() {
|
|
297
|
+
console.log(`${cliName} handoff
|
|
298
|
+
|
|
299
|
+
Usage:
|
|
300
|
+
${cliName} handoff create [--from ID] --summary TEXT [--task N] [--to ID] [--changed-files JSON_OR_CSV] [--tests TEXT] [--risks TEXT]
|
|
301
|
+
${cliName} handoff list [--task N] [--limit N]
|
|
302
|
+
`);
|
|
303
|
+
},
|
|
304
|
+
|
|
305
|
+
helpEvent() {
|
|
306
|
+
console.log(`${cliName} event
|
|
307
|
+
|
|
308
|
+
Usage:
|
|
309
|
+
${cliName} event tail [--limit N]
|
|
310
|
+
`);
|
|
311
|
+
},
|
|
312
|
+
|
|
313
|
+
helpRun() {
|
|
314
|
+
console.log(`${cliName} run
|
|
315
|
+
|
|
316
|
+
Usage:
|
|
317
|
+
${cliName} run --peer ID --kind codex|claude|shell --role ROLE -- COMMAND [ARGS...]
|
|
318
|
+
|
|
319
|
+
Examples:
|
|
320
|
+
${cliName} run --peer codex-a --kind codex --role peer -- codex
|
|
321
|
+
${cliName} run --peer claude-a --kind claude --role peer -- claude
|
|
322
|
+
|
|
323
|
+
This keeps the CLI in your current terminal while injecting HCC_PEER,
|
|
324
|
+
HCC_ROOT, and HCC_DB so the session can use the shared bus.
|
|
325
|
+
Use hcc peer start, hcc web, or the installed claude/codex shims when the
|
|
326
|
+
session should also be browser-controllable.
|
|
327
|
+
`);
|
|
328
|
+
},
|
|
329
|
+
|
|
330
|
+
helpUp() {
|
|
331
|
+
console.log(`${cliName} up
|
|
332
|
+
|
|
333
|
+
Usage:
|
|
334
|
+
${cliName} up [--no-discover] [--no-guidance]
|
|
335
|
+
|
|
336
|
+
Examples:
|
|
337
|
+
${cliName} up
|
|
338
|
+
|
|
339
|
+
This initializes the project-local coordination bus, writes bounded guidance,
|
|
340
|
+
installs Claude/Codex hooks when missing, and registers currently detected
|
|
341
|
+
sessions. It does not start the browser terminal console.
|
|
342
|
+
|
|
343
|
+
Run hcc web for the default full experience: local coordination, hooks, shims,
|
|
344
|
+
tmux-backed terminal sessions, and browser control.
|
|
345
|
+
`);
|
|
346
|
+
},
|
|
347
|
+
|
|
348
|
+
helpDown() {
|
|
349
|
+
console.log(`${cliName} down
|
|
350
|
+
|
|
351
|
+
Usage:
|
|
352
|
+
${cliName} down
|
|
353
|
+
|
|
354
|
+
Stops the web runtime started by hcc web for this project.
|
|
355
|
+
`);
|
|
356
|
+
},
|
|
357
|
+
|
|
358
|
+
helpUpdate() {
|
|
359
|
+
console.log(`${cliName} update
|
|
360
|
+
|
|
361
|
+
Usage:
|
|
362
|
+
${cliName} update [--tag TAG] [--registry URL] [--dry-run]
|
|
363
|
+
|
|
364
|
+
Examples:
|
|
365
|
+
${cliName} update
|
|
366
|
+
${cliName} update --tag latest
|
|
367
|
+
${cliName} update --dry-run
|
|
368
|
+
|
|
369
|
+
Updates the global npm install by running:
|
|
370
|
+
npm install -g ${npmPackageName}@TAG
|
|
371
|
+
|
|
372
|
+
The default TAG is latest.
|
|
373
|
+
`);
|
|
374
|
+
},
|
|
375
|
+
|
|
376
|
+
helpUninstall() {
|
|
377
|
+
console.log(`${cliName} uninstall
|
|
378
|
+
|
|
379
|
+
Usage:
|
|
380
|
+
${cliName} uninstall [--purge --yes]
|
|
381
|
+
|
|
382
|
+
Stops the current project runtime and removes user-level hello-cc integrations:
|
|
383
|
+
Claude/Codex hooks and claude/codex shims.
|
|
384
|
+
|
|
385
|
+
With --purge --yes, also removes current project data:
|
|
386
|
+
.hello-cc/
|
|
387
|
+
hello-cc blocks from CLAUDE.md and AGENTS.md
|
|
388
|
+
`);
|
|
389
|
+
},
|
|
390
|
+
|
|
391
|
+
helpWeb() {
|
|
392
|
+
console.log(`${cliName} web
|
|
393
|
+
|
|
394
|
+
Usage:
|
|
395
|
+
${cliName} web [--host HOST] [--port N] [--token TEXT] [--local] [--no-token] [--no-discover] [--no-guidance]
|
|
396
|
+
|
|
397
|
+
Examples:
|
|
398
|
+
${cliName} web
|
|
399
|
+
HCC_WEB_TOKEN='long-token' ${cliName} web --port 8787
|
|
400
|
+
${cliName} web --local
|
|
401
|
+
|
|
402
|
+
This is the default one-command entrypoint. It prepares local coordination,
|
|
403
|
+
installs Claude/Codex hooks and shims, ensures tmux is available, starts the
|
|
404
|
+
browser terminal console as a background runtime, prints the URL, PID, runtime
|
|
405
|
+
file, and log file, then returns the terminal to you.
|
|
406
|
+
|
|
407
|
+
By default, the web runtime listens on 0.0.0.0 and uses a saved token,
|
|
408
|
+
generating one on first use. Use HCC_WEB_TOKEN or --token to replace the saved
|
|
409
|
+
token, --local to bind only to 127.0.0.1, or --no-token only for a trusted
|
|
410
|
+
local/test environment.
|
|
411
|
+
|
|
412
|
+
After hcc web, plain claude/codex commands started from a new shell are wrapped
|
|
413
|
+
as local tmux-backed terminals. Existing ordinary terminals can communicate
|
|
414
|
+
through the bus, but cannot be visually attached unless they were started under
|
|
415
|
+
tmux/screen or a hello-cc shim.
|
|
416
|
+
`);
|
|
417
|
+
}
|
|
418
|
+
};
|
|
419
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { table } from '../format.mjs';
|
|
2
|
+
import { uniqueList } from '../core/coordination/timeline.mjs';
|
|
3
|
+
|
|
4
|
+
function iso(ts) {
|
|
5
|
+
if (!ts) return '';
|
|
6
|
+
return new Date(ts * 1000).toISOString().replace(/\.\d{3}Z$/, '.000Z');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function renderStatusSummary(s, peer = null) {
|
|
10
|
+
const taskSummary = s.tasks.length ? s.tasks.map((r) => `${r.status}:${r.n}`).join(', ') : 'none';
|
|
11
|
+
return [
|
|
12
|
+
`root: ${s.root}`,
|
|
13
|
+
`db: ${s.db}`,
|
|
14
|
+
`peers: active=${s.active_peers}, stale=${s.stale_peers}`,
|
|
15
|
+
`tasks: ${taskSummary}`,
|
|
16
|
+
`locks: active=${s.active_locks}`,
|
|
17
|
+
peer ? `inbox(${peer}): unread=${s.unread}` : null,
|
|
18
|
+
'',
|
|
19
|
+
'recent events:',
|
|
20
|
+
table(s.recent_events, [
|
|
21
|
+
{ label: 'id', value: (r) => `#${r.id}` },
|
|
22
|
+
{ label: 'type', value: (r) => r.type },
|
|
23
|
+
{ label: 'actor', value: (r) => r.actor || '' },
|
|
24
|
+
{ label: 'task', value: (r) => r.task_id ? `#${r.task_id}` : '' },
|
|
25
|
+
{ label: 'time', value: (r) => iso(r.created_at) }
|
|
26
|
+
])
|
|
27
|
+
].filter((line) => line !== null).join('\n');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function normalizeStateResources(values) {
|
|
31
|
+
const list = Array.isArray(values) ? values : [values];
|
|
32
|
+
return uniqueList(list.flatMap((value) => String(value || '').split(',').map((part) => part.trim())));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function renderStateSummary(data, peer, opts = {}) {
|
|
36
|
+
const timelineLimit = Number(opts.timelineLimit || 8);
|
|
37
|
+
const automation = data.automation;
|
|
38
|
+
const lines = [
|
|
39
|
+
`root: ${data.root}`,
|
|
40
|
+
`peer: ${peer}`,
|
|
41
|
+
automation.current_task ? `current task: #${automation.current_task.id} ${automation.current_task.status} ${automation.current_task.title}` : null,
|
|
42
|
+
`phase: ${automation.phase}`,
|
|
43
|
+
`next: ${automation.next_action.command || automation.next_action.kind}`,
|
|
44
|
+
`why: ${automation.next_action.reason}`
|
|
45
|
+
].filter(Boolean);
|
|
46
|
+
if (automation.finish_actions.length) {
|
|
47
|
+
lines.push('', 'finish actions:');
|
|
48
|
+
lines.push(...automation.finish_actions.map((action) => `- ${action.command}`));
|
|
49
|
+
}
|
|
50
|
+
if (automation.warnings.length) {
|
|
51
|
+
lines.push('', 'warnings:');
|
|
52
|
+
lines.push(...automation.warnings.map((warning) => `- ${warning}`));
|
|
53
|
+
}
|
|
54
|
+
if (data.timeline.length) {
|
|
55
|
+
lines.push('', 'timeline:');
|
|
56
|
+
for (const item of data.timeline.slice(-timelineLimit)) {
|
|
57
|
+
lines.push(`- ${iso(item.ts)} ${item.source}:${item.source_id} ${item.title}${item.text ? ` — ${item.text}` : ''}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return lines.join('\n');
|
|
61
|
+
}
|
package/lib/web/http.mjs
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { CliError } from '../shared/errors.mjs';
|
|
3
|
+
|
|
4
|
+
function readRequestBody(req, limit = 1024 * 1024) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
let body = '';
|
|
7
|
+
req.on('data', (chunk) => {
|
|
8
|
+
body += chunk;
|
|
9
|
+
if (body.length > limit) {
|
|
10
|
+
reject(new CliError('REQUEST_TOO_LARGE', 'Request body is too large'));
|
|
11
|
+
req.destroy();
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
req.on('end', () => resolve(body));
|
|
15
|
+
req.on('error', reject);
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function readJsonRequest(req) {
|
|
20
|
+
const body = await readRequestBody(req);
|
|
21
|
+
if (!body.trim()) return {};
|
|
22
|
+
return JSON.parse(body);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function sendHttp(res, status, contentType, body) {
|
|
26
|
+
res.writeHead(status, {
|
|
27
|
+
'Content-Type': contentType,
|
|
28
|
+
'Cache-Control': 'no-store'
|
|
29
|
+
});
|
|
30
|
+
res.end(body);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function sendJson(res, status, body) {
|
|
34
|
+
sendHttp(res, status, 'application/json; charset=utf-8', JSON.stringify(body, null, 2));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function sendFile(res, filePath, contentType) {
|
|
38
|
+
try {
|
|
39
|
+
const body = fs.readFileSync(filePath);
|
|
40
|
+
res.writeHead(200, {
|
|
41
|
+
'Content-Type': contentType,
|
|
42
|
+
'Cache-Control': 'no-store'
|
|
43
|
+
});
|
|
44
|
+
res.end(body);
|
|
45
|
+
} catch {
|
|
46
|
+
sendJson(res, 404, { ok: false, error: { code: 'NOT_FOUND', message: 'Asset not found' } });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function authOk(url, req, token) {
|
|
51
|
+
if (!token) return true;
|
|
52
|
+
const queryToken = url.searchParams.get('token');
|
|
53
|
+
const auth = req.headers.authorization || '';
|
|
54
|
+
return queryToken === token || auth === `Bearer ${token}`;
|
|
55
|
+
}
|