add-cli-command
Skill144 starsChanged 15 months agoMITView on GitHub
Use when adding a command to the aegis tool CLI (the framework's own `aegis ...` entry point, not a generated project's CLI). Covers Typer registration, the sync-plus-asyncio.run rule, brand help theming, i18n strings, optional guided/interactive wiring, tests, and the CLI reference doc that must change together.
We split this skill into the rules it gives an agent
12 rules. About 0 are common, 1 is distinctive, and 11 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈1
Distinctive · estimate
This author's own choices. Worth a look.
11
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 14
Use when the task is "add a new aegis <command>" to the framework's own CLI surface (the tool that generates and updates projects).
- Very specific: line 17
Do NOT use for adding a subcommand to a generated project's CLI (app/cli/*.py.jinja in the template tree) - that is template work with its …
- Very specific: line 56
All user-facing text goes through i18n keys in aegis/i18n/locales/en.py (the real English string), with the same key stubbed into every …
- Very specific: line 113
Write the failing test first: tests/cli/test_<name>_command.py (or a case in test_cli_basic.py) asserting aegis <name> --help exits 0 and …
- Very specific: line 117
Add aegis/commands/<name>.py with <name>_command(...) -> None.
- Very specific: line 124
Add the command's i18n keys to aegis/i18n/locales/en.py (help strings via lazy_t, runtime strings via t), then stub the same keys into …
- Very specific: line 127
Use brand.success/warn/error/accent/muted for all status output.
- Very specific: line 129
If the command takes project-shaping options, wire the guided/interactive flows (aegis/cli/guided.py, aegis/cli/interactive.py) and …
- Very specific: line 134
Add or extend the test coverage in tests/cli/ from step 1, including an error-path case.
- Very specific: line 136
Add the ### aegis <name> section to docs/cli-reference.md.
- Distinctive: line 137
Run the gates below and fix anything red.
- Very specific: line 158
Don't hardcode a color (typer.secho(msg, fg="red")) at a call site.
≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Add CLI command
Adds a new top-level command to the aegis tool's own CLI (aegis <command>),
as opposed to a command inside a generated project. Traced from
aegis/commands/update.py and its registration in aegis/__main__.py.
When to use
Use when the task is "add a new aegis <command>" to the framework's own
CLI surface (the tool that generates and updates projects).
Do NOT use for adding a subcommand to a generated project's CLI
(app/cli/*.py.jinja in the template tree) - that is template work with its
own conventions, not this skill. Do NOT use for adding a component or
service (aegis add <component> / aegis add-service <name>); those already
have dedicated commands and belong to the add-component / add-service
skills. Do NOT use for a plugin-provided CLI sub-app mounted via
aegis/core/plugins/discovery.py; that is a separate, plugin-owned
registration path.
Files that change
Command implementation and registration:
aegis/commands/<name>.py: the command function, e.g.<name>_command(seeupdate_commandinaegis/commands/update.pyfor the shape: Typer options viatyper.Option(...),lazy_t(...)for help strings,t(...)for runtime messages,brand.success/warn/errorfor status lines).aegis/__main__.py: import the command function and register it with the module-level Typerapp. The exact registration point is the block ofapp.command(name="...")(...)calls afterapp.add_typer(plugins_app, name="plugins")- addapp.command(name="<name>")(<name>_command)there, next to the existing entries (update,deploy-init,ingress-enable, and so on). This is the single place a new command becomes reachable asaegis <name>.
Help/UX theming (no new files, just correct usage):
aegis/cli/brand.pyis the single source of the CLI palette:AEGIS_TEAL(#17CCBF, typeable tokens and success state),AEGIS_WARNING(#F5A623, amber),AEGIS_ERROR(#E23E3E, red). Call sites never hardcode colors; they callbrand.success(...),brand.warn(...),brand.error(...),brand.accent(...),brand.muted(...)(or the_textvariants for inline composition) to express intent.brand.apply_help_theme()themes Typer's rich--helprendering globally (teal for command names and flags, dim for metavars/env-var hints, neutral for prose); it runs once at CLI startup and needs no per-command change.
Strings (i18n):
- All user-facing text goes through i18n keys in
aegis/i18n/locales/en.py(the real English string), with the same key stubbed into every other locale file for parity. Uselazy_t("<name>.help_opt_...")for Typerhelp=strings (lazy because locale isn't resolved until the--langcallback runs) andt("<name>....")for runtime echo strings. Follow the<command>.<thing>naming already used byupdate.*andcommon.*(for examplecommon.help_yes,common.help_project_path_fullfor options shared across commands). See the i18n skill for the full add-a-key procedure and locale list; do not attempt real (non-English) translation in the same change.
Init-style options (only if the new command takes project-shaping options,
the way init/add/add-service do):
aegis/cli/guided.py: add achoose_<thing>(...)method on the guided-setup UI class if the command should offer a guided prompt step, following the existingchoose_worker_backend()/choose_database_engine()pattern (a list of_Choice(value, label, description)fed toself._select(...), i18n-backed via_g(...)).aegis/cli/interactive.py: add aninteractive_<name>_selection(...)/interactive_<name>_config(...)function if the command needs a non-guided interactive prompt fallback, followinginteractive_component_add_selection/interactive_ai_service_config.aegis/cli/validators.py: input validation for CLI-supplied values (project names, component/service name lists) - add avalidate_<thing>(...)here if the new option needs syntactic validation, followingvalidate_project_name.aegis/cli/validation.py: shared cross-command checks that are not raw input validation, such asvalidate_copier_project(target_path, command_name)(confirms the target is a Copier-generated project before proceeding). Call this if the new command operates on an existing project directory, mirroring howupdate_commandand others gate on it.- Most new commands (a status check, a report, a one-shot action) need none of this; skip the whole section if the command takes no project-shaping options.
Tests:
tests/cli/test_<name>_command.py(or add to an existing file if the command is small):typer.testing.CliRunneragainstaegis.__main__.app(seetests/cli/test_utils.pyforCLI_RUNNER, timeouts, andrun_aegis_command/run_cli_help_commandhelpers). Cover--helptext, success path, and error path.tests/cli/test_cli_basic.py: add atest_<name>_helpcase asserting the command's help text and options appear, followingtest_init_help.
Docs:
docs/cli-reference.md: add a### aegis <name>section in command order (matches the registration order inaegis/__main__.py), with a**Usage:**code block and an**Example Output:**block, following the existing### aegis update/### aegis servicessections. Nomkdocs.ymlnav change is needed; this page is already wired underReference: CLI Reference.
Procedure
- Write the failing test first:
tests/cli/test_<name>_command.py(or a case intest_cli_basic.py) assertingaegis <name> --helpexits 0 and shows the expected help text. Confirm it fails because the command doesn't exist yet. - Add
aegis/commands/<name>.pywith<name>_command(...) -> None. Keep it a plain syncdef; if it needs to await anything, callasyncio.run(...)inside the function body (see Pitfalls - Typer has no native async command support). - Register it in
aegis/__main__.py: import the function, addapp.command(name="<name>")(<name>_command)next to the existingapp.command(...)calls. - Add the command's i18n keys to
aegis/i18n/locales/en.py(help strings vialazy_t, runtime strings viat), then stub the same keys into every other locale file for parity (see the i18n skill). - Use
brand.success/warn/error/accent/mutedfor all status output; never calltyper.sechowith a raw color inline. - If the command takes project-shaping options, wire the guided/interactive
flows (
aegis/cli/guided.py,aegis/cli/interactive.py) and validation (aegis/cli/validators.pyfor input shape,aegis/cli/validation.pyfor shared project-state checks likevalidate_copier_project). Skip this step for options-free or simple-flag commands. - Add or extend the test coverage in
tests/cli/from step 1, including an error-path case. - Add the
### aegis <name>section todocs/cli-reference.md. - Run the gates below and fix anything red.
Gates
make check(lint, typecheck, test) must pass.- A command that touches
aegis initor project generation also needsmake test-stacks:make checkno longer generates the stack matrix (CI does, in itsgenerationjobs). make cli-testfor a manual smoke: runspython -m aegis --helpand confirms the CLI still loads with the new command registered.
Pitfalls
- Typer has no native async command support in any version: a command
function must be a sync
def. If it needs async work, callasyncio.run(...)from inside the sync function body rather than trying to declare the command itselfasync def- Typer will not await it. - Forgetting the
app.command(name="...")(...)line inaegis/__main__.pyleaves the command fully implemented but unreachable;aegis <name> --helpfails with "no such command" even thoughaegis/commands/<name>.pyimports and type-checks cleanly. - Don't hardcode a color (
typer.secho(msg, fg="red")) at a call site; usebrand.error/brand.warn/brand.successso the palette stays defined in exactly one place (aegis/cli/brand.py) and stays in sync with the generated frontend's theme. - Adding a key to
en.pyonly failstests/core/test_i18n.py, which requires the same key in every other locale file; stub the key everywhere in the same change and leave real translation to the separate translator-reviewed pass (see the i18n skill). lazy_t(...)is forhelp=strings evaluated before the--langoption is resolved (Typer short-circuits its main callback on--help); using plaint(...)for a help string can render against the wrong locale on a fresh process. Uset(...)for anything echoed at runtime, after the callback has already set the locale.- A command that takes a
--project-pathand operates on an existing project should callvalidate_copier_project(or the equivalent check) before doing any work, the wayupdate_commanddoes immediately after resolving the target path - skipping it lets the command run against a directory that was never generated by Copier and fail with a confusing downstream error instead of a clear one.
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- dotnet/sdk★ 3.2ksame name
- james5101/jobtrack★ 2same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository14 other files it gives its agents.
- CLAUDE.mdCLAUDE.md
- docs/CLAUDE.mdCLAUDE.md
- tests/CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- llms.txtllms.txt
- add-componentSkill
- add-serviceSkill
- create-pluginSkill
- example-skillSkill
- execute-issueSkill
- i18nSkill
- releaseSkill
- squash-branchSkill
- template-devSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

