Drop the per-tool name list from group headers (redundant with the rows
below), remove individual borders inside groups, and tighten padding so
20-tool blocks scan as a dense list instead of a wall of cards.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The attach button precedes the draft in the DOM, so under the ≤520px wrap
rule it took a line of its own above the textarea. An explicit order puts
the draft back on the first line and groups every control below it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Reverts the stacked control strip from f78aa13. The floating card is still
gone — flush band, one hairline against the transcript that doubles as the
focus indicator — but the composer keeps its own `--surface` fill so it
still reads as distinct from the pane, and the draft, gear and Send stay on
one bottom-aligned row.
At phone width that row cannot hold a draft plus three controls, so it
wraps: the draft takes a full line and the controls drop below it.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A Claude Code or Codex spawn now asks for the structured view unless the
operator says otherwise: the two spawn forms open with the box ticked and
`maestro spawn` takes `--terminal` to opt out. Once the operator works the
toggle, a command change no longer re-ticks it under them.
The support predicate moves to `maestro_protocol::display` so all three
clients gate on the same resolution.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The composer was a floating bordered card inset from the pane edge, and
its send button was bottom-aligned beside the textarea, so it sank as the
draft grew.
It is now a flush band at the foot of the pane: one hairline rule against
the transcript, which doubles as the focus indicator. The textarea spans
the full width and the controls sit in a fixed strip below it, so the send
button keeps its place however tall the draft grows.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two things broke `nix build .#webFrontend`, and with it `nh home apply`
on any host that installs maestro.
`marked` was added to web/package.json without repinning pnpmDeps.hash,
so pnpm install failed offline with ERR_PNPM_NO_OFFLINE_TARBALL.
`tsc -b` also type-checks web/src/toolIcon.test.ts, which imports the
shared ../../fixtures/command-resolution.json. The derivation's src is
./web, so that path does not exist in the sandbox. The production build
now uses tsconfig.build.json, which excludes tests; `pnpm typecheck`
still covers them from a full checkout.
`command FOO=1 claude` and `nohup FOO=1 claude` are invalid shells (they
exec `FOO=1` as the program, exit 127), but the resolver skipped the
assignment unconditionally after any wrapper. Assignment prefixes are
only real at the start of a simple command or after a wrapper that itself
interprets them as env vars (env, sudo) or a keyword that restarts the
assignment grammar (time) — not after command, nohup, or a package
manager's subcommand, which just exec their next argument as the program.
Attach an accepts_assignment flag to each wrapper transition in both the
Rust and TS resolvers and gate the assignment-skip loop on the flag from
the most recently consumed wrapper. Extends the shared fixture with the
two reported cases plus every other wrapper transition and several nested
combinations (sudo+time valid, sudo+command / command+nohup / time+command
invalid, nohup+env valid) to lock the per-wrapper policy in for both
languages.
Fixes six issues in the shared command resolver (Rust display.rs and
its TS mirror toolIcon.ts), all locked in by shared fixture cases:
- An unescaped newline now ends resolution like `;` does, instead of
being folded in as whitespace; a backslash-newline is a line
continuation, removed entirely rather than embedded or splitting
the word.
- Assignment recognition (`FOO=bar`) now requires the `=` to come from
plain, unquoted/unescaped source text. `"FOO=1"` and `FOO\=1` are
executable words named `FOO=1`, not assignments — quote removal was
losing that provenance.
- `command -v`/`-V` (lookup only, no execution), `env -S` (embeds a
command string) and `npx -c`/`--call` (same) no longer resolve
through to a program they don't actually run. Restricted bash
`time`'s options to `-p`, the only flag the keyword itself parses
(`-o` etc. belong to external GNU time) — this corrects a
previously-wrong fixture case.
- `env`, `sudo` and `command`'s options may now bundle (`env -iv`,
`sudo -nE`) or take an attached value (`sudo -uroot`), the way real
getopt-based tools do, with missing-operand forms safely rejected.
- pnpm/yarn `dlx` and `bun x`/`bunx` now consume their own trailing
options (`--package foo`/`--package=foo`, `--bun`) before the
executable, matching each tool's documented CLI syntax.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
command_program/command_tool whitespace-split and only skipped whole
wrapper words, so quoted paths broke, sudo -u/env -i/nohup/time/npx
--yes/pnpm|yarn --silent dlx/bun x were rejected, and pnpm claude /
yarn claude (a package.json script, not `dlx claude`) resolved as a
Claude launch. Replace it with a tokenizer that honours quoting and
escaping without evaluating any expansion, and per-wrapper option
tables (env, sudo, command, nohup, time, npx, pnpm, yarn, bun) that
require dlx/x before looking through a package-manager wrapper.
Ambiguous shell grammar (pipelines, list operators, redirections,
substitution, unterminated quotes) is rejected outright rather than
guessed through; command_program still degrades to the naive first
word for display, but command_tool returns None.
Mirror the same resolver in web/src/toolIcon.ts and add
fixtures/command-resolution.json as a parity fixture both test
suites assert against.
The TUI's structured-view gate already delegates to command_tool, the
same resolver web/spawnForm.ts uses via splitCommand, so paths, env
assignments, wrappers, and configured presets already resolve like
daemon dispatch. Add coverage for those forms plus stale-selection
clearing on both preset switches and custom-command edits, so a
regression back to a naive basename match would be caught.