refactor(cli): unify the arg grammar — one program, --config flag, real web subcommand

Drop the bare `dsh <config>` positional in favor of a `--config <path>` flag.
Without a root positional, `web` can be a real Commander subcommand in one
program instead of the reserved-first-token dispatch to a second parser, so
`dsh --help` lists every mode natively (no hand-pasted command text) and the
second parser + reserved-token machinery are gone.

Grammar:
  dsh                       TUI (shipped tree + ~/.dsh overlay)
  dsh --config <path>       TUI, alternate tree (demos/tests only)
  dsh --resume <id>         TUI, resume a session
  dsh -p "task"             headless one-shot
  dsh web [--host --port --dev]

`dsh` is the product front door with no positional; `--config` exists only so
demo:cordis, demo:code-mode, and the keyless PTY smokes can point the shipped
bin at an example tree. Those three sites and the /resume re-exec argv move to
`--config <path>`. The `-p` + `--config`/`--resume` mode-mixing guard and the
cordis.yml-owns-host/port-default fix are preserved.

Agent Note + Chinese pair, README, tui.ts docs updated. All 13 PTY smokes
(including code-mode via --config and the exec-replace resume handoff) green.
This commit is contained in:
Turtle
2026-07-25 15:47:55 +08:00
parent 91d86f9b21
commit fca2dda37d
13 files changed
+110 -93

No files matched your search

+4 -5
View File
@@ -1,6 +1,6 @@
/**
* `dsh` default surface — the interactive TUI coding agent. Boots the shipped
* tui-agent config (or an explicit config argument) with the personal overlay
* tui-agent config (or the `--config` override) with the personal overlay
* from the Harness home (`~/.dsh`): its `.env` fills environment gaps (precedence:
* ambient environment, then the invoking directory's `.env`, then the personal one)
* and its `config.yaml` patches the booted tree. The workspace is the invoking
@@ -42,7 +42,7 @@ const SOURCE_ROOT = fileURLToPath(new URL('../../..', import.meta.url))
/**
* Run the interactive TUI from the invoking directory.
* @param config - a config path to boot instead of the shipped default, or
* `undefined` for the default; already parsed from the optional positional.
* `undefined` for the default; already parsed from `--config`.
* @param resumeSessionId - a persisted session id to resume, or `undefined`;
* already parsed and non-empty-validated from `--resume`. It is provided on the
* boot context under {@link RESUME_SESSION_ID_KEY}, which the shipped config
@@ -73,14 +73,13 @@ export async function runTui(config: string | undefined, resumeSessionId: string
const current = app.current
if (current === undefined) throw new Error(`${NAME}: app boot has not completed`)
// Rebuild argv from the parsed config plus the selected id: TUI mode's
// only arguments are the optional config positional and `--resume <id>`.
// The `--` guard keeps a config named like a flag or `web` a positional.
// only arguments are `--config <path>` and `--resume <id>`.
const nextArgv = [
process.execPath,
...process.execArgv,
entry,
`--resume=${sessionId}`,
...config !== undefined ? ['--', config] : [],
...config !== undefined ? ['--config', config] : [],
]
try {
await current.fiber.dispose()