Skip to content

Stack-specific subcommands cannot be invoked: the --stack that triggers loading is no longer a CLI option #233

Description

@dboreham

Stack-specific subcommands — the subcommands/ directory feature documented in
docs/subcommands.md — cannot be invoked. The loader is triggered by a --stack
argument that the CLI no longer accepts, so every route to a stack's subcommand
ends in a Click error.

Found while checking whether the extension mechanisms are documented, after
#232. Same root cause family: a refactor moved --stack and left a consumer of
it behind.

Symptoms

With a stack that has subcommands/hello.py defining a command (verbatim from
docs/subcommands.md):

$ stack --stack .../stacks/example -h
Error: No such option: --stack

$ stack example-hello --stack .../stacks/example
Usage: stack example-hello [OPTIONS]
Error: No such option: --stack

$ stack example-hello
Error: No such command 'example-hello'.

The second form is the interesting one: example-hello resolved as a command, so
the discovery and registration in load_subcommands_from_stack() still work
fine. It then fails on the option itself. Without --stack anywhere in argv the
command is never registered at all. There is no third path — the argv scan is the
only trigger.

Cause

src/stack/main.py:105 registers the subcommands by scanning sys.argv for
--stack:

for i in range(len(sys.argv)):
    arg = sys.argv[i]
    if arg == "--stack":
        if i + 1 < len(sys.argv):
            stack_path = sys.argv[i + 1]
            break
if stack_path:
    load_subcommands_from_stack(cli, stack_path)

but --stack was removed from the CLI group in 171f6e9 ("74: Add checklist
command (#76)", 2025-07-08). It survives only as a per-command option on init,
deploy and friends, so Click rejects it on the group and on any dynamically
added subcommand.

Worth noting this is the second time: 5803b81 (2025-05-06) is titled "Restore
--stack option at top-level", so the same coupling broke once before and was
fixed by putting the group option back.

Fix

Re-adding --stack to the group would restore the documented invocation and
match what 5803b81 did, but it is worth deciding deliberately this time, since
the option now also exists per-command and the two would coexist
(stack --stack X init --stack Y becomes expressible). The alternative is to
give the loader a trigger that does not depend on a group option — resolving the
stack the same way the rest of the CLI now does, by name as well as path.

Either way the argv-scanning at import time is what makes this fragile: it
couples command registration to the spelling of an option it does not own, and
the except Exception around it means a failure prints a warning rather than
failing. That is worth revisiting alongside the fix.

Coverage

Nothing in CI invokes a stack subcommand, which is why this went unnoticed for
five weeks. A smoke-level test that registers a trivial subcommand and runs it
would catch both this and any future re-break; tests/skill/fixture already has
a fixture stack that could carry one.

Docs

docs/subcommands.md documents the stack --stack <path> -h form throughout,
including its example help output, so it needs updating with whatever invocation
the fix settles on.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions