@logicseek/hello-cc 0.1.0 → 0.1.2
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 +81 -0
- package/README.md +69 -378
- package/README.zh-CN.md +49 -325
- package/assets/logo.svg +84 -0
- package/bin/hcc.mjs +106 -11
- package/docs/README.md +22 -0
- package/docs/README.zh-CN.md +22 -0
- package/docs/commands.md +104 -0
- package/docs/commands.zh-CN.md +101 -0
- package/docs/guide.md +339 -0
- package/docs/guide.zh-CN.md +292 -0
- package/package.json +6 -2
- package/scripts/regression.mjs +78 -3
- package/scripts/release-notes.mjs +112 -0
package/bin/hcc.mjs
CHANGED
|
@@ -16,9 +16,10 @@ const _libDir = path.resolve(fileURLToPath(import.meta.url), '..', '..', 'lib');
|
|
|
16
16
|
async function loadDiscover() { return import(path.join(_libDir, 'discover.mjs')); }
|
|
17
17
|
async function loadSetup() { return import(path.join(_libDir, 'setup.mjs')); }
|
|
18
18
|
|
|
19
|
-
const VERSION = '0.1.
|
|
19
|
+
const VERSION = '0.1.2';
|
|
20
20
|
const PRODUCT_NAME = 'hello-cc';
|
|
21
21
|
const CLI_NAME = 'hcc';
|
|
22
|
+
const NPM_PACKAGE_NAME = '@logicseek/hello-cc';
|
|
22
23
|
const DEFAULT_LOCK_TTL = 900;
|
|
23
24
|
const ACTIVE_PEER_TTL = 600;
|
|
24
25
|
// Directory under .hello-cc/ for optional external PTY buffer files.
|
|
@@ -128,6 +129,14 @@ function parseOpts(args, spec = {}) {
|
|
|
128
129
|
return opts;
|
|
129
130
|
}
|
|
130
131
|
|
|
132
|
+
function wantsHelp(args) {
|
|
133
|
+
const stop = args.indexOf('--');
|
|
134
|
+
const scan = stop >= 0 ? args.slice(0, stop) : args;
|
|
135
|
+
const index = scan.findIndex((arg) => arg === '--help' || arg === '-h');
|
|
136
|
+
if (index < 0) return false;
|
|
137
|
+
return index <= 1 || !String(scan[index - 1] || '').startsWith('--');
|
|
138
|
+
}
|
|
139
|
+
|
|
131
140
|
function validateOpts(command, opts, allowed = []) {
|
|
132
141
|
const allowedSet = new Set(['_', ...allowed]);
|
|
133
142
|
for (const key of Object.keys(opts)) {
|
|
@@ -2289,7 +2298,7 @@ async function cmdPeers(ctx, args) {
|
|
|
2289
2298
|
|
|
2290
2299
|
async function cmdTask(ctx, args) {
|
|
2291
2300
|
const sub = args[0];
|
|
2292
|
-
if (!sub ||
|
|
2301
|
+
if (!sub || wantsHelp(args)) return helpTask();
|
|
2293
2302
|
if (sub === 'create') return taskCreate(ctx, args.slice(1));
|
|
2294
2303
|
if (sub === 'list') return taskList(ctx, args.slice(1));
|
|
2295
2304
|
if (sub === 'claim') return taskClaim(ctx, args.slice(1));
|
|
@@ -2467,7 +2476,7 @@ async function taskDone(ctx, args) {
|
|
|
2467
2476
|
|
|
2468
2477
|
async function cmdMsg(ctx, args) {
|
|
2469
2478
|
const sub = args[0];
|
|
2470
|
-
if (!sub ||
|
|
2479
|
+
if (!sub || wantsHelp(args)) return helpMsg();
|
|
2471
2480
|
if (sub === 'send') return msgSend(ctx, args.slice(1));
|
|
2472
2481
|
if (sub === 'inbox') return msgInbox(ctx, args.slice(1));
|
|
2473
2482
|
if (sub === 'ack') return msgAck(ctx, args.slice(1));
|
|
@@ -2598,7 +2607,7 @@ async function cmdInject(ctx, args) {
|
|
|
2598
2607
|
|
|
2599
2608
|
async function cmdPeer(ctx, args) {
|
|
2600
2609
|
const sub = args[0];
|
|
2601
|
-
if (!sub ||
|
|
2610
|
+
if (!sub || wantsHelp(args)) return helpPeer();
|
|
2602
2611
|
if (sub === 'list') return peerList(ctx, args.slice(1));
|
|
2603
2612
|
if (sub === 'start') return peerStart(ctx, args.slice(1));
|
|
2604
2613
|
if (sub === 'attach') return peerAttach(ctx, args.slice(1));
|
|
@@ -2748,7 +2757,7 @@ async function peerStop(ctx, args) {
|
|
|
2748
2757
|
|
|
2749
2758
|
async function cmdLock(ctx, args) {
|
|
2750
2759
|
const sub = args[0];
|
|
2751
|
-
if (!sub ||
|
|
2760
|
+
if (!sub || wantsHelp(args)) return helpLock();
|
|
2752
2761
|
if (sub === 'acquire') return lockAcquire(ctx, args.slice(1));
|
|
2753
2762
|
if (sub === 'release') return lockRelease(ctx, args.slice(1));
|
|
2754
2763
|
if (sub === 'renew') return lockRenew(ctx, args.slice(1));
|
|
@@ -2847,7 +2856,7 @@ async function lockList(ctx, args) {
|
|
|
2847
2856
|
|
|
2848
2857
|
async function cmdHandoff(ctx, args) {
|
|
2849
2858
|
const sub = args[0];
|
|
2850
|
-
if (!sub ||
|
|
2859
|
+
if (!sub || wantsHelp(args)) return helpHandoff();
|
|
2851
2860
|
if (sub === 'create') return handoffCreate(ctx, args.slice(1));
|
|
2852
2861
|
if (sub === 'list') return handoffList(ctx, args.slice(1));
|
|
2853
2862
|
throw new CliError('BAD_ARGS', `Unknown handoff command: ${sub}`);
|
|
@@ -2900,7 +2909,7 @@ async function handoffList(ctx, args) {
|
|
|
2900
2909
|
|
|
2901
2910
|
async function cmdEvent(ctx, args) {
|
|
2902
2911
|
const sub = args[0];
|
|
2903
|
-
if (!sub ||
|
|
2912
|
+
if (!sub || wantsHelp(args)) return helpEvent();
|
|
2904
2913
|
if (sub === 'tail') return eventTail(ctx, args.slice(1));
|
|
2905
2914
|
throw new CliError('BAD_ARGS', `Unknown event command: ${sub}`);
|
|
2906
2915
|
}
|
|
@@ -4107,7 +4116,7 @@ function webIndexHtml() {
|
|
|
4107
4116
|
// The server streams the tmux pane's raw output, so xterm renders
|
|
4108
4117
|
// incrementally (no reset/redraw → no flicker) and the program's own
|
|
4109
4118
|
// escape sequences carry the cursor.
|
|
4110
|
-
if (msg.type === 'snapshot') { term.write(msg.data || '', () => { term.scrollToBottom(); }); }
|
|
4119
|
+
if (msg.type === 'snapshot') { term.reset(); term.write(msg.data || '', () => { term.scrollToBottom(); }); }
|
|
4111
4120
|
if (msg.type === 'data') { term.write(msg.data || '', pinned ? () => { term.scrollToBottom(); } : undefined); }
|
|
4112
4121
|
if (msg.type === 'replace') { term.reset(); term.write(msg.data || '', pinned ? () => { term.scrollToBottom(); } : undefined); }
|
|
4113
4122
|
if (msg.type === 'exit') { refreshSessions().catch(console.error); }
|
|
@@ -4535,6 +4544,30 @@ async function cmdWeb(ctx, args, startMeta = {}) {
|
|
|
4535
4544
|
(payload.visible ? '[?25h' : '[?25l');
|
|
4536
4545
|
}
|
|
4537
4546
|
|
|
4547
|
+
function tmuxSnapshot(session) {
|
|
4548
|
+
const captured = tmuxCapturePane(session.pane);
|
|
4549
|
+
return captured + cursorEscape(tmuxCursorPayload(captured, tmuxCursorInfo(session.pane)));
|
|
4550
|
+
}
|
|
4551
|
+
|
|
4552
|
+
function refreshTmuxSnapshot(session) {
|
|
4553
|
+
if (session.type !== 'tmux' || !session.pane) return session.buffer || '';
|
|
4554
|
+
try {
|
|
4555
|
+
session.buffer = tmuxSnapshot(session);
|
|
4556
|
+
} catch {
|
|
4557
|
+
// Keep the previous buffer if the pane disappears during capture.
|
|
4558
|
+
}
|
|
4559
|
+
return session.buffer || '';
|
|
4560
|
+
}
|
|
4561
|
+
|
|
4562
|
+
function scheduleTmuxReplace(session) {
|
|
4563
|
+
if (session.type !== 'tmux' || !session.pane) return;
|
|
4564
|
+
if (session.replaceTimer) clearTimeout(session.replaceTimer);
|
|
4565
|
+
session.replaceTimer = setTimeout(() => {
|
|
4566
|
+
session.replaceTimer = null;
|
|
4567
|
+
broadcast(session, { type: 'replace', data: refreshTmuxSnapshot(session) });
|
|
4568
|
+
}, 80);
|
|
4569
|
+
}
|
|
4570
|
+
|
|
4538
4571
|
// Stream the tmux pane's RAW output (escape sequences and all) into the
|
|
4539
4572
|
// browser via `tmux pipe-pane`, so xterm.js renders incrementally — no
|
|
4540
4573
|
// screenshot-poll, no full-screen reset, no flicker — and the program's own
|
|
@@ -4547,8 +4580,7 @@ async function cmdWeb(ctx, args, startMeta = {}) {
|
|
|
4547
4580
|
// Capture the existing screen once for the initial paint; pipe-pane only
|
|
4548
4581
|
// forwards output produced after it is enabled.
|
|
4549
4582
|
try {
|
|
4550
|
-
|
|
4551
|
-
session.buffer = captured + cursorEscape(tmuxCursorPayload(captured, tmuxCursorInfo(session.pane)));
|
|
4583
|
+
session.buffer = tmuxSnapshot(session);
|
|
4552
4584
|
} catch {}
|
|
4553
4585
|
|
|
4554
4586
|
// Use a FIFO rather than an append-only regular file. The old file-backed
|
|
@@ -4612,6 +4644,7 @@ async function cmdWeb(ctx, args, startMeta = {}) {
|
|
|
4612
4644
|
|
|
4613
4645
|
function stopTmuxStream(session) {
|
|
4614
4646
|
if (session.streamPoller) { clearInterval(session.streamPoller); session.streamPoller = null; }
|
|
4647
|
+
if (session.replaceTimer) { clearTimeout(session.replaceTimer); session.replaceTimer = null; }
|
|
4615
4648
|
// Turn piping back off for this pane (no command toggles it off).
|
|
4616
4649
|
try { runTmux(['pipe-pane', '-t', session.pane]); } catch {}
|
|
4617
4650
|
if (session.streamFd !== null && session.streamFd !== undefined) {
|
|
@@ -5202,7 +5235,7 @@ async function cmdWeb(ctx, args, startMeta = {}) {
|
|
|
5202
5235
|
}
|
|
5203
5236
|
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
5204
5237
|
session.clients.add(ws);
|
|
5205
|
-
ws.send(JSON.stringify({ type: 'snapshot', data: session
|
|
5238
|
+
ws.send(JSON.stringify({ type: 'snapshot', data: refreshTmuxSnapshot(session) }));
|
|
5206
5239
|
ws.on('message', (raw) => {
|
|
5207
5240
|
try {
|
|
5208
5241
|
const msg = JSON.parse(String(raw));
|
|
@@ -5213,6 +5246,7 @@ async function cmdWeb(ctx, args, startMeta = {}) {
|
|
|
5213
5246
|
const cols = Math.max(20, Number.parseInt(msg.cols || 100, 10));
|
|
5214
5247
|
const rows = Math.max(8, Number.parseInt(msg.rows || 30, 10));
|
|
5215
5248
|
resizeSession(session, cols, rows);
|
|
5249
|
+
scheduleTmuxReplace(session);
|
|
5216
5250
|
}
|
|
5217
5251
|
} catch {
|
|
5218
5252
|
// Ignore malformed terminal frames.
|
|
@@ -5514,6 +5548,8 @@ Commands:
|
|
|
5514
5548
|
web Start coordination, shims, tmux, and browser console
|
|
5515
5549
|
up Initialize local coordination only
|
|
5516
5550
|
down Stop the running hello-cc web runtime
|
|
5551
|
+
update Update the global npm install of hello-cc
|
|
5552
|
+
uninstall Remove hooks, shims, and optional project data
|
|
5517
5553
|
init Initialize .hello-cc/mesh.db and guidance
|
|
5518
5554
|
register --peer ID Register or update a peer session identity
|
|
5519
5555
|
join --peer ID Register this shell and print eval-able env
|
|
@@ -5755,6 +5791,24 @@ Stops the web runtime started by hcc web for this project.
|
|
|
5755
5791
|
`);
|
|
5756
5792
|
}
|
|
5757
5793
|
|
|
5794
|
+
function helpUpdate() {
|
|
5795
|
+
console.log(`${CLI_NAME} update
|
|
5796
|
+
|
|
5797
|
+
Usage:
|
|
5798
|
+
${CLI_NAME} update [--tag TAG] [--registry URL] [--dry-run]
|
|
5799
|
+
|
|
5800
|
+
Examples:
|
|
5801
|
+
${CLI_NAME} update
|
|
5802
|
+
${CLI_NAME} update --tag latest
|
|
5803
|
+
${CLI_NAME} update --dry-run
|
|
5804
|
+
|
|
5805
|
+
Updates the global npm install by running:
|
|
5806
|
+
npm install -g ${NPM_PACKAGE_NAME}@TAG
|
|
5807
|
+
|
|
5808
|
+
The default TAG is latest.
|
|
5809
|
+
`);
|
|
5810
|
+
}
|
|
5811
|
+
|
|
5758
5812
|
function helpWeb() {
|
|
5759
5813
|
console.log(`${CLI_NAME} web
|
|
5760
5814
|
|
|
@@ -6078,6 +6132,46 @@ async function cmdSetup(ctx, args) {
|
|
|
6078
6132
|
log('\nDone. Run `hcc web` for the default coordinated terminal experience.');
|
|
6079
6133
|
}
|
|
6080
6134
|
|
|
6135
|
+
// ─── hcc update ──────────────────────────────────────────────────────────────
|
|
6136
|
+
|
|
6137
|
+
async function cmdUpdate(ctx, args) {
|
|
6138
|
+
if (args[0] === '--help' || args[0] === '-h') return helpUpdate();
|
|
6139
|
+
|
|
6140
|
+
const opts = parseOpts(args, { booleans: ['dry-run'] });
|
|
6141
|
+
validateOpts('update', opts, ['tag', 'registry', 'dry-run']);
|
|
6142
|
+
const tag = opts.tag || 'latest';
|
|
6143
|
+
if (!/^[A-Za-z0-9._+-]+$/.test(tag)) {
|
|
6144
|
+
throw new CliError('BAD_ARGS', '--tag must be an npm dist-tag or version');
|
|
6145
|
+
}
|
|
6146
|
+
|
|
6147
|
+
const packageSpec = `${NPM_PACKAGE_NAME}@${tag}`;
|
|
6148
|
+
const npmArgs = ['install', '-g', packageSpec];
|
|
6149
|
+
if (opts.registry) npmArgs.push('--registry', opts.registry);
|
|
6150
|
+
const command = shellCommand(['npm', ...npmArgs]);
|
|
6151
|
+
const data = { package: NPM_PACKAGE_NAME, tag, registry: opts.registry || null, command, dry_run: Boolean(opts['dry-run']) };
|
|
6152
|
+
|
|
6153
|
+
if (opts['dry-run']) {
|
|
6154
|
+
printResult(ctx, data, () => `would run: ${command}`);
|
|
6155
|
+
return;
|
|
6156
|
+
}
|
|
6157
|
+
|
|
6158
|
+
const result = spawnSync('npm', npmArgs, {
|
|
6159
|
+
encoding: ctx.json ? 'utf8' : undefined,
|
|
6160
|
+
stdio: ctx.json ? ['ignore', 'pipe', 'pipe'] : 'inherit'
|
|
6161
|
+
});
|
|
6162
|
+
if (result.error) {
|
|
6163
|
+
throw new CliError('UPDATE_FAILED', `npm update command failed: ${result.error.message}`);
|
|
6164
|
+
}
|
|
6165
|
+
if (result.status !== 0) {
|
|
6166
|
+
const output = ctx.json
|
|
6167
|
+
? [result.stdout, result.stderr].filter(Boolean).join('\n').trim()
|
|
6168
|
+
: '';
|
|
6169
|
+
throw new CliError('UPDATE_FAILED', output || `npm exited with status ${result.status}`);
|
|
6170
|
+
}
|
|
6171
|
+
|
|
6172
|
+
printResult(ctx, data, () => `updated ${packageSpec}`);
|
|
6173
|
+
}
|
|
6174
|
+
|
|
6081
6175
|
// ─── hcc uninstall ───────────────────────────────────────────────────────────
|
|
6082
6176
|
|
|
6083
6177
|
async function cmdUninstall(ctx, args) {
|
|
@@ -6301,6 +6395,7 @@ async function dispatch(ctx, rest) {
|
|
|
6301
6395
|
if (command === '--version' || command === 'version') return console.log(VERSION);
|
|
6302
6396
|
if (command === 'up') return cmdUp(ctx, args);
|
|
6303
6397
|
if (command === 'down') return cmdDown(ctx, args);
|
|
6398
|
+
if (command === 'update') return cmdUpdate(ctx, args);
|
|
6304
6399
|
if (command === 'uninstall') return cmdUninstall(ctx, args);
|
|
6305
6400
|
if (command === 'init') return cmdInit(ctx, args);
|
|
6306
6401
|
if (command === 'register') return cmdRegister(ctx, args);
|
package/docs/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# hello-cc Documentation
|
|
2
|
+
|
|
3
|
+
Start with the [project README](../README.md) when you only need the project
|
|
4
|
+
summary and first command. Use these docs when you need more detail.
|
|
5
|
+
|
|
6
|
+
## User Docs
|
|
7
|
+
|
|
8
|
+
- [User Guide](guide.md): install, start, Web console, coordination semantics,
|
|
9
|
+
workflow, stable peer identity, and environment behavior.
|
|
10
|
+
- [Command Reference](commands.md): compact list of public commands and the
|
|
11
|
+
intended use of each command group.
|
|
12
|
+
- [Changelog](../CHANGELOG.md): release notes for published versions.
|
|
13
|
+
- Release notes: run `npm run release:check` before publishing, and use
|
|
14
|
+
`npm run release:notes` to print the current changelog section for GitHub
|
|
15
|
+
Release notes.
|
|
16
|
+
|
|
17
|
+
## Design And Implementation
|
|
18
|
+
|
|
19
|
+
- [Design Notes](design.md): product boundary, project boundary, capability
|
|
20
|
+
levels, coordination semantics, and provider-session binding.
|
|
21
|
+
- [Implementation Notes](implementation.md): architecture, protocol, command
|
|
22
|
+
surface, stack, shim behavior, and implementation plan.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# hello-cc 文档
|
|
2
|
+
|
|
3
|
+
只需要项目简介和第一条命令时,先看[仓库 README](../README.zh-CN.md)。
|
|
4
|
+
需要更多细节时,再看下面的文档。
|
|
5
|
+
|
|
6
|
+
## 用户文档
|
|
7
|
+
|
|
8
|
+
- [用户指南](guide.zh-CN.md):安装、启动、Web 控制台、协作语义、工作流、稳定
|
|
9
|
+
peer 身份和环境变量行为。
|
|
10
|
+
- [命令参考](commands.zh-CN.md):公共命令的紧凑清单,以及每组命令的用途。
|
|
11
|
+
- [更新日志](../CHANGELOG.md):已发布版本的 release notes。
|
|
12
|
+
- 发行说明:发布前运行 `npm run release:check`,用
|
|
13
|
+
`npm run release:notes` 输出当前版本的 changelog 小节,作为 GitHub
|
|
14
|
+
Release 描述。
|
|
15
|
+
|
|
16
|
+
## 设计和实现
|
|
17
|
+
|
|
18
|
+
- [设计说明](design.md):产品边界、项目边界、能力层级、协作语义和 provider
|
|
19
|
+
session 绑定。
|
|
20
|
+
- [实现说明](implementation.md):架构、协议、命令面、技术栈、shim 行为和实现计划。
|
|
21
|
+
|
|
22
|
+
设计和实现文档目前只有英文版。
|
package/docs/commands.md
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# hello-cc Command Reference
|
|
2
|
+
|
|
3
|
+
Use `hcc --help` for the current top-level command list. Most subcommands also
|
|
4
|
+
accept `--help`, for example `hcc update --help`, `hcc peer --help`, and
|
|
5
|
+
`hcc task --help`.
|
|
6
|
+
|
|
7
|
+
## Install Maintenance
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
hcc update [--tag TAG] [--registry URL] [--dry-run]
|
|
11
|
+
hcc uninstall [--purge --yes]
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`hcc update` updates the global npm install. `hcc uninstall` removes local
|
|
15
|
+
hooks and shims; add `--purge --yes` only when you also want to remove the
|
|
16
|
+
current project's `.hello-cc` data and guidance blocks.
|
|
17
|
+
|
|
18
|
+
## Start And Stop
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
hcc web [--host HOST] [--port N] [--token TEXT] [--local] [--no-discover] [--no-guidance]
|
|
22
|
+
hcc down
|
|
23
|
+
hcc up [--no-discover] [--no-guidance]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
`hcc web` is the default entry. It initializes coordination, installs hooks and
|
|
27
|
+
shims, starts or reuses the Web console, and returns the terminal to you. Use
|
|
28
|
+
`hcc up` only when you want coordination without the Web console or shims.
|
|
29
|
+
|
|
30
|
+
## Peers And Status
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
hcc peers
|
|
34
|
+
hcc status [--peer ID]
|
|
35
|
+
hcc scan [--register]
|
|
36
|
+
hcc prompt --peer ID [--kind codex|claude|shell|other] [--role ROLE]
|
|
37
|
+
hcc join --peer ID [--kind codex|claude|shell|other] [--role ROLE]
|
|
38
|
+
hcc env --peer ID
|
|
39
|
+
hcc heartbeat [--peer ID] [--renew-locks --ttl 900]
|
|
40
|
+
hcc run --peer ID --kind codex|claude|shell --role ROLE -- COMMAND [ARGS...]
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Use these commands to inspect live project state, register terminals, and run a
|
|
44
|
+
CLI with `HCC_PEER`, `HCC_ROOT`, and `HCC_DB` set.
|
|
45
|
+
|
|
46
|
+
## Browser-Controllable Terminals
|
|
47
|
+
|
|
48
|
+
```text
|
|
49
|
+
hcc peer list
|
|
50
|
+
hcc peer start PEER [--kind K] [--role R] [--cwd DIR] [--restart-env] -- COMMAND [ARGS...]
|
|
51
|
+
hcc peer start PEER --kind codex --resume SESSION_ID [--restart-env]
|
|
52
|
+
hcc peer start PEER --kind codex --last
|
|
53
|
+
hcc peer start PEER --kind claude --resume SESSION_ID [--restart-env]
|
|
54
|
+
hcc peer start PEER --kind claude --continue
|
|
55
|
+
hcc peer attach PEER [--pane PANE] [--kind K] [--role R] [--cwd DIR]
|
|
56
|
+
hcc peer stop PEER
|
|
57
|
+
hcc inject PEER TEXT [--no-enter]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
These commands create or attach tmux-backed terminals that can be viewed and
|
|
61
|
+
controlled from the Web console.
|
|
62
|
+
|
|
63
|
+
## Messages
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
hcc msg send [--from ID] [--to ID|all] --body TEXT [--task N] [--kind note|task|handoff]
|
|
67
|
+
hcc msg inbox [--peer ID] [--wait SEC] [--all] [--limit N]
|
|
68
|
+
hcc msg ack [--peer ID] --id N
|
|
69
|
+
hcc ask PEER MESSAGE [--from ID] [--task N] [--inject]
|
|
70
|
+
hcc broadcast MESSAGE [--from ID] [--task N] [--inject]
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Messages are addressed mailbox items. `ask` and `broadcast` also support live
|
|
74
|
+
terminal injection with `--inject`.
|
|
75
|
+
|
|
76
|
+
## Tasks
|
|
77
|
+
|
|
78
|
+
```text
|
|
79
|
+
hcc task create --title TEXT [--body TEXT] [--from ID] [--to ID] [--priority N]
|
|
80
|
+
hcc task list [--status S] [--peer ID] [--all]
|
|
81
|
+
hcc task claim [--peer ID] --id N
|
|
82
|
+
hcc task next [--peer ID]
|
|
83
|
+
hcc task update [--peer ID] --id N --status running|review|blocked|done|abandoned [--summary TEXT] [--body TEXT] [--to ID]
|
|
84
|
+
hcc task done [--peer ID] --id N --summary TEXT
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Tasks are shared project facts. They remain visible until marked `done` or
|
|
88
|
+
`abandoned`.
|
|
89
|
+
|
|
90
|
+
## Locks, Handoffs, And Events
|
|
91
|
+
|
|
92
|
+
```text
|
|
93
|
+
hcc lock acquire [--peer ID] --resource PATH [--task N] [--ttl SEC] [--reason TEXT]
|
|
94
|
+
hcc lock renew [--peer ID] --resource PATH [--ttl SEC]
|
|
95
|
+
hcc lock release [--peer ID] --resource PATH [--force]
|
|
96
|
+
hcc lock list [--all]
|
|
97
|
+
hcc handoff create [--from ID] --summary TEXT [--task N] [--to ID] [--changed-files JSON_OR_CSV] [--tests TEXT] [--risks TEXT]
|
|
98
|
+
hcc handoff list [--task N] [--limit N]
|
|
99
|
+
hcc event tail [--limit N]
|
|
100
|
+
hcc gc [--older-than DAYS] [--yes]
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Locks are advisory coordination locks with TTLs. Handoffs preserve the result,
|
|
104
|
+
tests, changed files, and remaining risks when work moves between peers.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# hello-cc 命令参考
|
|
2
|
+
|
|
3
|
+
使用 `hcc --help` 查看当前顶层命令列表。大多数子命令也支持 `--help`,
|
|
4
|
+
例如 `hcc update --help`、`hcc peer --help` 和 `hcc task --help`。
|
|
5
|
+
|
|
6
|
+
## 安装维护
|
|
7
|
+
|
|
8
|
+
```text
|
|
9
|
+
hcc update [--tag TAG] [--registry URL] [--dry-run]
|
|
10
|
+
hcc uninstall [--purge --yes]
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
`hcc update` 更新全局 npm 安装。`hcc uninstall` 移除本机 hooks 和 shims;
|
|
14
|
+
只有在确定也要删除当前项目的 `.hello-cc` 数据和指导块时,才加
|
|
15
|
+
`--purge --yes`。
|
|
16
|
+
|
|
17
|
+
## 启动和停止
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
hcc web [--host HOST] [--port N] [--token TEXT] [--local] [--no-discover] [--no-guidance]
|
|
21
|
+
hcc down
|
|
22
|
+
hcc up [--no-discover] [--no-guidance]
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`hcc web` 是默认入口。它会初始化协作状态,安装 hooks 和 shims,启动或复用
|
|
26
|
+
Web 控制台,然后把终端还给你。只想使用本地协作、不需要 Web 或 shims 时,
|
|
27
|
+
再使用 `hcc up`。
|
|
28
|
+
|
|
29
|
+
## Peers 和状态
|
|
30
|
+
|
|
31
|
+
```text
|
|
32
|
+
hcc peers
|
|
33
|
+
hcc status [--peer ID]
|
|
34
|
+
hcc scan [--register]
|
|
35
|
+
hcc prompt --peer ID [--kind codex|claude|shell|other] [--role ROLE]
|
|
36
|
+
hcc join --peer ID [--kind codex|claude|shell|other] [--role ROLE]
|
|
37
|
+
hcc env --peer ID
|
|
38
|
+
hcc heartbeat [--peer ID] [--renew-locks --ttl 900]
|
|
39
|
+
hcc run --peer ID --kind codex|claude|shell --role ROLE -- COMMAND [ARGS...]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
这些命令用于查看项目状态、注册终端,以及用 `HCC_PEER`、`HCC_ROOT`、
|
|
43
|
+
`HCC_DB` 环境运行 CLI。
|
|
44
|
+
|
|
45
|
+
## Web 可控终端
|
|
46
|
+
|
|
47
|
+
```text
|
|
48
|
+
hcc peer list
|
|
49
|
+
hcc peer start PEER [--kind K] [--role R] [--cwd DIR] [--restart-env] -- COMMAND [ARGS...]
|
|
50
|
+
hcc peer start PEER --kind codex --resume SESSION_ID [--restart-env]
|
|
51
|
+
hcc peer start PEER --kind codex --last
|
|
52
|
+
hcc peer start PEER --kind claude --resume SESSION_ID [--restart-env]
|
|
53
|
+
hcc peer start PEER --kind claude --continue
|
|
54
|
+
hcc peer attach PEER [--pane PANE] [--kind K] [--role R] [--cwd DIR]
|
|
55
|
+
hcc peer stop PEER
|
|
56
|
+
hcc inject PEER TEXT [--no-enter]
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
这些命令创建或接入 tmux-backed 终端,让 Web 控制台可以观察和操作它们。
|
|
60
|
+
|
|
61
|
+
## 消息
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
hcc msg send [--from ID] [--to ID|all] --body TEXT [--task N] [--kind note|task|handoff]
|
|
65
|
+
hcc msg inbox [--peer ID] [--wait SEC] [--all] [--limit N]
|
|
66
|
+
hcc msg ack [--peer ID] --id N
|
|
67
|
+
hcc ask PEER MESSAGE [--from ID] [--task N] [--inject]
|
|
68
|
+
hcc broadcast MESSAGE [--from ID] [--task N] [--inject]
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
消息是带收件人的邮箱记录。`ask` 和 `broadcast` 加 `--inject` 后,也会把内容
|
|
72
|
+
实时注入到终端。
|
|
73
|
+
|
|
74
|
+
## 任务
|
|
75
|
+
|
|
76
|
+
```text
|
|
77
|
+
hcc task create --title TEXT [--body TEXT] [--from ID] [--to ID] [--priority N]
|
|
78
|
+
hcc task list [--status S] [--peer ID] [--all]
|
|
79
|
+
hcc task claim [--peer ID] --id N
|
|
80
|
+
hcc task next [--peer ID]
|
|
81
|
+
hcc task update [--peer ID] --id N --status running|review|blocked|done|abandoned [--summary TEXT] [--body TEXT] [--to ID]
|
|
82
|
+
hcc task done [--peer ID] --id N --summary TEXT
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
任务是项目共享事实。任务会一直可见,直到被标记为 `done` 或 `abandoned`。
|
|
86
|
+
|
|
87
|
+
## 锁、交接和事件
|
|
88
|
+
|
|
89
|
+
```text
|
|
90
|
+
hcc lock acquire [--peer ID] --resource PATH [--task N] [--ttl SEC] [--reason TEXT]
|
|
91
|
+
hcc lock renew [--peer ID] --resource PATH [--ttl SEC]
|
|
92
|
+
hcc lock release [--peer ID] --resource PATH [--force]
|
|
93
|
+
hcc lock list [--all]
|
|
94
|
+
hcc handoff create [--from ID] --summary TEXT [--task N] [--to ID] [--changed-files JSON_OR_CSV] [--tests TEXT] [--risks TEXT]
|
|
95
|
+
hcc handoff list [--task N] [--limit N]
|
|
96
|
+
hcc event tail [--limit N]
|
|
97
|
+
hcc gc [--older-than DAYS] [--yes]
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
锁是带 TTL 的协作式 advisory lock。交接记录用于保存结果、测试、变更文件和
|
|
101
|
+
剩余风险,方便工作在多个 peer 之间继续。
|