feat(cli): scaffold nc CLI with list-groups command

Adds a transport-agnostic CLI control plane shared between three eventual
callers (host shell, Claude in project, container agent) — though only the
host-side socket transport is wired in this commit. Container DB transport
and approval flow land alongside the first risky command.

- src/cli/frame.ts:        wire format (RequestFrame, ResponseFrame, CallerContext)
- src/cli/registry.ts:     command registry with RiskClass
- src/cli/dispatch.ts:     transport-agnostic dispatcher
- src/cli/transport.ts:    Transport interface
- src/cli/socket-client.ts: SocketTransport against data/nc.sock
- src/cli/socket-server.ts: host-side listener (chmod 0600, line-delimited JSON)
- src/cli/format.ts:       human table / --json output modes
- src/cli/client.ts:       `nc` argv -> frame -> transport -> stdout
- src/cli/commands/list-groups.ts: first command (riskClass: safe)
- bin/nc:                  bash launcher (resolves project root via symlink)
- src/index.ts:            start/stop server + import command barrel

`data/nc.sock` is intentionally separate from `data/cli.sock` (which the
existing chat-style channel adapter still owns).

Verified end-to-end: `nc list-groups`, `nc list groups`, `--json`,
unknown-command error, host-down ENOENT message with start instructions.
typecheck clean; eslint reports only the same `no-catch-all` warnings the
rest of the codebase has.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gavrielc
2026-04-29 18:03:16 +03:00
parent ae9bcb7c33
commit 3a3d2ee644
12 changed files with 572 additions and 0 deletions

View File

@@ -52,6 +52,11 @@ import './channels/index.js';
// append registry-based modules. Imported for side effects (registrations).
import './modules/index.js';
// CLI command barrel — populates the `nc` registry before the CLI server
// accepts connections.
import './cli/commands/index.js';
import { startCliServer, stopCliServer } from './cli/socket-server.js';
import type { ChannelAdapter, ChannelSetup } from './channels/adapter.js';
import { initChannelAdapters, teardownChannelAdapters, getChannelAdapter } from './channels/channel-registry.js';
@@ -159,6 +164,9 @@ async function main(): Promise<void> {
startHostSweep();
log.info('Host sweep started');
// 7. Start the `nc` CLI socket server (data/nc.sock).
await startCliServer();
log.info('NanoClaw running');
}
@@ -174,6 +182,7 @@ async function shutdown(signal: string): Promise<void> {
}
stopDeliveryPolls();
stopHostSweep();
await stopCliServer();
await teardownChannelAdapters();
process.exit(0);
}