2026-07-24 19:43:59 +08:00
/ * *
* Commander adapter for the ` dsh ` command - line entry : the one place argv is
* parsed and routed to a mode . ` bin.ts ` switches on the returned discriminant
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.
2026-07-25 15:47:55 +08:00
* and dynamic - imports that mode ' s module . One program : the default ( no
2026-07-29 15:28:38 +08:00
* subcommand ) is the TUI / headless surface with option - only flags ; ` meta ` and
* ` web ` are real subcommands . Commander owns ` --help ` / ` --version ` and parse
* errors — it prints and exits at the point of failure ( a domain failure routes through
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.
2026-07-25 15:47:55 +08:00
* ` command.error ` ) , so this returns only a resolved mode .
2026-07-24 19:43:59 +08:00
* @module @deepseek - ai / dsh / args
* /
2026-07-25 14:15:25 +08:00
import { Command , CommanderError } from 'commander'
2026-07-24 19:43:59 +08:00
2026-07-29 15:28:38 +08:00
/ * *
* Interactive TUI : the default mode . ` --config ` applies an overlay over the
* shipped composition in place of the personal one , ` --config-replace ` boots a
* file as the whole tree instead , and ` --resume <id> ` rehydrates a session .
* /
2026-07-24 19:43:59 +08:00
interface TuiInvocation {
mode : 'tui'
config? : string
2026-07-29 15:28:38 +08:00
configReplace? : string
2026-07-24 19:43:59 +08:00
resume? : string
}
/** Headless one-shot: `dsh -p "task"`. */
interface HeadlessInvocation {
mode : 'headless'
prompt : string
}
2026-07-29 21:39:38 +08:00
/** Interactive fresh TUI over this harness checkout; accepts no default-surface options. */
2026-07-29 15:28:38 +08:00
interface MetaInvocation {
mode : 'meta'
}
/ * *
2026-07-29 21:41:51 +08:00
* Guided fresh - session entry : ` dsh upgrade ` seeds the first turn with the
* ` dsh-upgrade ` skill . It always mints a
2026-07-29 15:28:38 +08:00
* fresh session in the invoking directory and takes no options — ` --resume ` ,
* ` --config ` , and ` -p ` are rejected as mistyped , so there is nothing to carry .
* /
interface SkillSessionInvocation {
2026-07-29 21:41:51 +08:00
mode : 'upgrade'
2026-07-29 15:28:38 +08:00
}
2026-07-25 15:03:17 +08:00
/ * *
* Browser UI : ` dsh web ` . ` host ` / ` port ` are present only when the flag was
2026-07-25 16:19:02 +08:00
* passed — pass - through overrides with no CLI default and no CLI validation :
* the ` dsh-host-webserver ` schema ( ` host ` a loopback / all - interfaces literal ,
* ` port ` a natural ≤ 65535 ) is the single source of both the default ( the
2026-07-29 21:59:44 +08:00
* shipped Web overlay value stands when a flag is absent ) and validity ( a bad
2026-07-25 16:19:02 +08:00
* value fails loud at boot ) . ` port ` is ` Number ` - coerced only because the schema
2026-07-25 18:05:39 +08:00
* wants a number , not a string . ` dev ` mounts the client HMR driver ;
* ` workspaceRoot ` is the parent directory for name - created workspaces .
2026-07-25 15:03:17 +08:00
* /
2026-07-24 19:43:59 +08:00
interface WebInvocation {
mode : 'web'
2026-07-29 15:28:38 +08:00
/** Overlay of loader patches applied over the shipped web composition. */
config? : string
2026-07-25 15:03:17 +08:00
host? : string
port? : number
2026-07-25 12:02:28 +08:00
dev : boolean
2026-07-25 18:05:39 +08:00
workspaceRoot? : string
2026-07-28 15:40:02 +08:00
/** Extra authorities for the /api browser-trust fence (`host` or `host:port`); LAN IP literals are derived, not listed here. */
trustedHosts? : string [ ]
2026-07-24 19:43:59 +08:00
}
2026-07-25 14:15:25 +08:00
/** The resolved `dsh` invocation: exactly one mode. `--help`/`--version`/errors exit inside {@link parseDshArgs}. */
2026-07-29 15:28:38 +08:00
export type DshInvocation =
| TuiInvocation
| HeadlessInvocation
| MetaInvocation
| SkillSessionInvocation
| WebInvocation
2026-07-24 19:43:59 +08:00
2026-07-25 16:19:02 +08:00
/** Raw web-subcommand options straight from Commander. */
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.
2026-07-25 15:47:55 +08:00
interface WebOptions {
2026-07-29 15:28:38 +08:00
config? : string
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.
2026-07-25 15:47:55 +08:00
host? : string
port? : string
dev? : boolean
2026-07-25 18:05:39 +08:00
workspaceRoot? : string
2026-07-28 15:40:02 +08:00
trustedHost? : string [ ]
2026-07-24 20:01:38 +08:00
}
2026-07-25 16:19:02 +08:00
/ * *
* Narrow the raw ` web ` options into a { @link WebInvocation } . No host / port
* validation : both flow to the webserver schema , which is the sole gate . ` port `
* is coerced to a number ( the schema rejects a string ) but not range - checked
* here — ` NaN ` / out - of - range fail loud at the schema on boot .
* /
function resolveWeb ( options : WebOptions ) : WebInvocation {
2026-07-25 15:03:17 +08:00
return {
mode : 'web' ,
2026-07-29 15:28:38 +08:00
. . . options . config !== undefined && { config : options.config } ,
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.
2026-07-25 15:47:55 +08:00
. . . options . host !== undefined && { host : options.host } ,
2026-07-25 16:19:02 +08:00
. . . options . port !== undefined && { port : Number ( options . port ) } ,
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.
2026-07-25 15:47:55 +08:00
dev : options.dev === true ,
2026-07-25 18:05:39 +08:00
. . . options . workspaceRoot !== undefined && { workspaceRoot : options.workspaceRoot } ,
2026-07-28 15:40:02 +08:00
. . . options . trustedHost !== undefined && { trustedHosts : options.trustedHost } ,
2026-07-25 14:15:25 +08:00
}
2026-07-24 20:01:38 +08:00
}
2026-07-24 19:43:59 +08:00
2026-07-24 20:01:38 +08:00
/ * *
2026-07-25 14:15:25 +08:00
* Resolve the raw argv into a { @link DshInvocation } , or print and exit for
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.
2026-07-25 15:47:55 +08:00
* ` --help ` / ` --version ` / a parse error . The default ( no subcommand ) is the
* TUI / headless surface ; ` web ` is a subcommand .
2026-07-24 20:01:38 +08:00
* @param argv - the arguments after the node binary and script ( ` process.argv.slice(2) ` ) .
* @param version - the version string ` --version ` prints ; read from this app ' s package . json .
2026-07-25 14:15:25 +08:00
* @returns the resolved invocation ( only reached on a valid , non - help invocation ) .
2026-07-24 20:01:38 +08:00
* /
export function parseDshArgs ( argv : readonly string [ ] , version : string ) : DshInvocation {
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.
2026-07-25 15:47:55 +08:00
let resolved : DshInvocation | undefined
const program = new Command ( )
. name ( 'dsh' )
. version ( version , '-V, --version' , 'output the version number' )
2026-07-29 15:28:38 +08:00
. description ( 'dsh: DeepSeek Harness — an interactive coding agent for your terminal.\nRun `dsh` with no arguments to start a session in the current directory.' )
// The default surface takes no positional task, so `dsh "task"` fails
// commander's arity check with no hint; these examples are where a first
// reader learns the entry points and that a one-shot task rides `-p`.
. addHelpText ( 'after' , `
Examples :
dsh start an interactive session in this directory
dsh - p "run the tests" answer one task , print the result , and exit
dsh -- resume < id > continue a past session
` )
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.
2026-07-25 15:47:55 +08:00
. exitOverride ( )
// Default surface: option-only (no positional), so `web` can be a real
// subcommand without a positional collision.
2026-07-29 15:28:38 +08:00
. option ( '-p, --prompt <task>' , 'answer this task without the interactive UI, then exit' )
. option ( '--resume <id>' , 'continue a past session by id' )
. option ( '--config <path>' , 'apply this overlay of loader patches instead of the personal one' )
. option ( '--config-replace <path>' , 'boot this file as the entire tree, ignoring the shipped and personal configuration' )
. action ( ( options : { config? : string ; configReplace? : string ; prompt? : string ; resume? : string } ) = > {
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.
2026-07-25 15:47:55 +08:00
if ( options . prompt !== undefined ) {
// A headless prompt owns the invocation; an empty task has nothing to
// run, and --config/--resume are TUI inputs that must not silently
// vanish from a headless run.
if ( options . prompt === '' ) program . error ( 'error: --prompt needs a task' )
2026-07-29 15:28:38 +08:00
if ( options . config !== undefined || options . configReplace !== undefined || options . resume !== undefined ) {
program . error ( 'error: --prompt takes no --config, --config-replace, or --resume' )
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.
2026-07-25 15:47:55 +08:00
}
resolved = { mode : 'headless' , prompt : options.prompt }
return
}
// An empty --resume= id would silently start a fresh session downstream
// (agent-loop treats '' as no-resume), so a mistyped resume must fail loud.
if ( options . resume === '' ) program . error ( 'error: --resume needs a session id' )
2026-07-29 15:28:38 +08:00
// The two config flags are mutually exclusive: one layers over the shipped
// tree, the other discards it, so accepting both would silently drop one.
if ( options . config !== undefined && options . configReplace !== undefined ) {
program . error ( 'error: --config and --config-replace are mutually exclusive' )
}
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.
2026-07-25 15:47:55 +08:00
resolved = {
mode : 'tui' ,
. . . options . config !== undefined && { config : options.config } ,
2026-07-29 15:28:38 +08:00
. . . options . configReplace !== undefined && { configReplace : options.configReplace } ,
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.
2026-07-25 15:47:55 +08:00
. . . options . resume !== undefined && { resume : options.resume } ,
}
} )
2026-07-29 15:28:38 +08:00
// Commander parses the parent (default-surface) options on either side of a
// subcommand into `program.opts()`. For a subcommand that shares none of them,
2026-07-29 21:39:38 +08:00
// a leaked config/prompt/resume option is a mistyped invocation that must fail
2026-07-29 15:28:38 +08:00
// loud rather than silently run and drop the input.
const rejectParentOptions = ( command : string ) : void = > {
const parent = program . opts < { config? : string ; configReplace? : string ; prompt? : string ; resume? : string } > ( )
if ( parent . config !== undefined || parent . configReplace !== undefined
|| parent . prompt !== undefined || parent . resume !== undefined ) {
2026-07-29 21:39:38 +08:00
program . error ( ` error: ${ command } takes none of --config, --config-replace, -p/--prompt, or --resume ` )
2026-07-29 15:28:38 +08:00
}
}
// Registration order is the rendered help order, so daily use comes first
// and the harness-development surfaces (`web --dev`, `meta`) come last.
2026-07-29 21:41:51 +08:00
// `upgrade` is a guided fresh-session entry: it takes no options and always
// mints a fresh session, so nothing is left to carry.
program
. command ( 'upgrade' )
. description ( 'update this dsh installation to the latest version' )
. action ( ( ) = > {
rejectParentOptions ( 'upgrade' )
resolved = { mode : 'upgrade' }
} )
2026-07-29 15:28:38 +08:00
// Host and port name no default: the CLI passes neither through when the flag
2026-07-29 21:59:44 +08:00
// is absent, so the shipped Web overlay value stands and restating it here
2026-07-29 15:28:38 +08:00
// would duplicate a fact this file does not own.
const web = program . command ( 'web' ) . description ( 'serve the browser UI on the configured host and port' )
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.
2026-07-25 15:47:55 +08:00
web
2026-07-29 15:28:38 +08:00
. option ( '--config <path>' , 'apply this overlay of loader patches over the shipped configuration' )
. option ( '--host <host>' , 'bind host; pass 0.0.0.0 to reach it from another machine' )
. option ( '--port <port>' , 'listen port; pass 0 to let the OS pick a free one' )
. option ( '--dev' , 'developer mode: hot-reload the browser client' )
. option ( '--workspace-root <path>' , 'parent directory for workspaces created from the browser UI' )
2026-07-28 15:40:02 +08:00
. option ( '--trusted-host <authority...>' , 'extra authority the /api browser-trust fence accepts (host or host:port; repeatable)' )
2026-07-25 17:24:39 +08:00
. action ( ( options : WebOptions ) = > {
2026-07-29 15:28:38 +08:00
rejectParentOptions ( 'web' )
resolved = resolveWeb ( options )
} )
program
. command ( 'meta' )
. description ( 'work on the dsh source that runs this command, from any directory' )
. action ( ( ) = > {
2026-07-29 17:34:50 +08:00
rejectParentOptions ( 'meta' )
resolved = { mode : 'meta' }
2026-07-25 17:24:39 +08:00
} )
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.
2026-07-25 15:47:55 +08:00
2026-07-25 14:15:25 +08:00
try {
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.
2026-07-25 15:47:55 +08:00
program . parse ( argv , { from : 'user' } )
2026-07-25 14:15:25 +08:00
} catch ( error ) {
// Commander printed help/version/the error under `exitOverride`; exit with
// the code it chose (0 for help/version, 1 for a parse or domain error).
/* v8 ignore next -- Commander only throws CommanderError from parse/error under exitOverride */
return process . exit ( error instanceof CommanderError ? error.exitCode : 1 )
}
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.
2026-07-25 15:47:55 +08:00
/* v8 ignore next -- the default action or a subcommand action always resolves, or parse throws above */
if ( resolved === undefined ) throw new Error ( 'dsh: no invocation resolved' )
return resolved
2026-07-24 19:43:59 +08:00
}