From e3d69177f9d327e0f2432645d62b224b21a980db Mon Sep 17 00:00:00 2001 From: Dimitri Kennedy Date: Thu, 22 Oct 2026 16:08:14 -0400 Subject: [PATCH 1/2] fix(cli): report a missing project with one closed code across project commands Follow-ups to #394 (HACK-1371). - branch, config get/set, env, host, project owner and tui now raise the shared MissingProjectContextError (E_PROJECT_NOT_FOUND) for a path without a project. branch, config and project owner printed a stack trace (and project owner reported E_UNEXPECTED under --json); env and host reported E_USAGE. - docs/cli.md lists the commands and the exceptions, and tests/missing-project-error-code.test.ts pins every command that declares --path. - Native run: the empty --env/--profile check now runs before authored dispatch, so an authored project refuses it with E_USAGE, not E_NATIVE_PROJECT_UNSUPPORTED. - Pin that a profile list with a real entry ("web,") is compared, not refused as empty, which kills the .every -> .some mutant. - Document that a short spelling only another command declares is refused as an unknown option, and record that difference from the old parser with the #370 notes. Refs HACK-1371 Co-Authored-By: Claude Opus 5.5 --- docs/cli.md | 52 +++- docs/guides/native-candidate.md | 7 +- src/backends/native-project-one-off.ts | 3 + src/commands/branch.ts | 7 +- src/commands/config.ts | 6 +- src/commands/env.ts | 3 +- src/commands/project-admin.ts | 6 +- src/commands/project.ts | 44 +-- src/commands/tui.ts | 3 +- src/lib/missing-project-error.ts | 20 ++ tests/cli-per-command-options.test.ts | 43 +++ tests/cli-renderer-loading.test.ts | 7 +- tests/missing-project-error-code.test.ts | 275 +++++++++++++++++- .../native-authored-command-dispatch.test.ts | 35 +++ tests/native-run-selection-cli.test.ts | 38 +++ 15 files changed, 488 insertions(+), 61 deletions(-) create mode 100644 src/lib/missing-project-error.ts diff --git a/docs/cli.md b/docs/cli.md index 1c0a9e484..0eeb6f78f 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -130,8 +130,22 @@ Historical remote guides are excluded from the supported documentation. - `--json` on `hack up`/`down`/`restart`/`doctor` emits a `{ok, data | error: {code, message}}` envelope with stable `E_*` error codes. `--json` on `hack up` implies `--detach`. - A `--path` (or working directory) without a `.hack/` project reports `E_PROJECT_NOT_FOUND` - from every project command, for example `hack ps -p --json` and `hack up -p --json`. - Without `--json` it prints one `E_PROJECT_NOT_FOUND` line and no stack trace. + from `up`, `down`, `restart`, `ps`, `logs`, `run`, `exec`, `open`, `tui`, `branch ...`, + `config get`/`set`, `env ...`, `host exec`/`shell` and `project owner`/`target`/`task`, for + example `hack ps -p --json` and `hack branch list -p `. Under `--json` it is one + error envelope; without `--json` it is one `ERROR: E_PROJECT_NOT_FOUND: ...` line and no stack + trace. `project target` and `project task` use their own message, and `target` adds a + `detail`. These commands report something else: + - `tui` without a terminal refuses for that reason first. + - `env get` never prints a failure reason, since its output is raw values: it prints one fixed + line on stderr, with or without `--json`. + - `config validate`, `plan`, `import` and `adopt` read native configuration and keep their own + refusals: `E_NATIVE_PROJECT_UNSUPPORTED` for `validate`/`plan`, a refused preview for + `import --dry-run`, and `E_CONFIG_INVALID` for `adopt`. + - `init`, `doctor`, `crash-capture`, and the `setup`, `mcp` and `agent` commands do not need a + project. + + `tests/missing-project-error-code.test.ts` pins each command that takes `--path`. - Known Hack errors and closed native runtime refusals print their message only, never a stack trace; set `HACK_DEBUG=1` to add the stack. Unexpected exceptions always print their stack. - `--no-interactive` (or `HACK_NO_INTERACTIVE=1`) is a global flag: commands never prompt — they @@ -206,8 +220,16 @@ to JSON. A `--` before the child command is optional and is not passed on; a lat Hack parses options with the resolved command's own options plus the global ones, so each command keeps its own short flags and option types. `-p` is `--path` for project commands (`init`, `up`, -`down`, `ps`, `logs`, `env ...`, `config ...`, `setup ...`) and `--port` for `hack ssh`. An option -that only another command declares is refused as `Option(s) not valid for ""`. +`down`, `ps`, `logs`, `env ...`, `config ...`, `setup ...`) and `--port` for `hack ssh`. Both +refusals below are `E_USAGE`: + +- A long option that only another command declares is refused as + `Option(s) not valid for "": --